我可以将函数作为属性传递给Web组件吗?

24

我正在尝试为输入元素创建本地Web组件。我希望该组件具有自定义验证功能,类似于Polymer的paper-input自定义验证函数。我不确定是否可以将自定义验证器函数作为属性传递给(Web组件)输入元素的实例。任何建议将不胜感激。

3个回答

13

属性是一个字符串,而不是一个函数。你可以将一个函数作为字符串传递,然后使用eval()函数进行评估。出于安全原因,这不被认为是一种好的做法。

另一种解决方案是将其作为Javascript属性传递给自定义元素:

function validate( value ) { return Number.isInteger( value) }
myCustomElement.validation = validate

或者,使用箭头函数:

myCustomElement.validation = v => Number.isInteger( va )

class CustomInput extends HTMLElement {
    constructor() {
        super()
        var sh = this.attachShadow( { mode: 'open' } )
        sh.appendChild( tpl.content.cloneNode( true ) )
        
        var div = sh.querySelector( 'div' )
        div.addEventListener( 'input', () => { 
           if ( !this.validate( Number( div.textContent ) ) )
            div.classList.add( 'error' )
           else
            div.classList.remove( 'error' ) 
        } )        
    }
}

customElements.define( 'custom-input', CustomInput )

integer.validate = v => Number.isInteger( v )
<template id="tpl">
  <style>
    :host {
       display: inline-block ;
       min-width: 150px ;
       border: 1px solid cyan ;
    }
    
    div.error {
       color: red ;
    }

  </style>
  <div contenteditable></div>
</template>
    
<custom-input id="integer"></custom-input>


2
虽然这不是最佳实践,但可以将全局方法名作为字符串传递,并且可以在不使用 eval 的情况下执行该方法。 - abraham
@abraham 你是对的。例子:windowattribute_string - Supersharp

9
最好通过属性传递函数,因为所有属性都是字符串。
如果必须将其作为属性传入,则需要将该字符串转换为函数。问题在于该函数的作用域。
如果您假设该函数在全局范围内,则可以使用该字符串作为window对象的属性名称。要执行代码,可以执行以下操作:
window[fnName]();

但这样做有些局限性,因为您可能想要调用自己类或对象的成员函数。

您可以在名称中使用点符号,例如func="myObj.fnName()",如果您不担心使用eval的警告,可以尝试以下操作:

eval(el.getAttribute('func'));

当然,这会让您遭受各种可能的注入攻击。但是同样存在着img标签和script标签的安全问题。

您可以尝试更加安全地执行以下操作:

设置不带()的属性:

`func="myObj.fnName"`

您尝试进行呼叫的代码如下所示:

var parts = el.getAttribute('func').split('.');
var obj = window;
var found = parts.some( part => {
  var next = obj[part];
  if (next) {
    obj = next;
    return true;
  }
});

if (found) {
  obj();
}

但这也防止你传入任何参数。
正因为此,AngularJS、React、Vue等技术存在的意义就在于解决这种复杂性。这也是我避免将函数通过属性传递的原因。
如果您通过属性传递它,则代码可能如下所示:
el.callme = globalfunc; // or
el.callme = this.localFunc; // or
el.callMe = (some params) => { /* do something */ };

或任何您想要的内容。

事件

现在,我想建议做与大多数本机组件相同的事情。也就是在需要完成某些事情或发生更改并且外部世界可能对这些更改感兴趣时,触发事件。

在您的元素中,只需调用

this.dispatchEvent(new CustomEvent('event name', {bubbles: true, detail: {your detail}});

那么,任何关心您的事件的人都将听取它。

这个事件听起来像是最合适的解决方案。 - undefined

4

只需将其传递给构造函数。 您的自定义元素语句:

class CustomInput extends HTMLElement {
  constructor(validator) {
    super()
    this.validate = validator
  }

  /* Your Custom Input Methods */
}

然后通过new运算符实例化您的组件,而不是使用document.createElement

实例化:

const customInputEl = new CustomInput((inputString) => {
  // your validation code
})

如果你想将一个函数传递给组件,那么无论如何都必须通过JavaScript来实例化它。


不适用于React。 - Justin Meskan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接