如何将对象(或关联数组)作为属性值传递给我的 Web 组件

Posted

技术标签:

【中文标题】如何将对象(或关联数组)作为属性值传递给我的 Web 组件【英文标题】:How to pass object(or Associative Array) as value of attribute to my web-component 【发布时间】:2019-12-30 10:29:21 【问题描述】:

我正在制作 vanilla-js 网络组件,需要将对象作为其值传递给属性,但每次都将其作为字符串。

我可以将字符串传递给组件的属性,但我知道我需要传递一个对象。

var obj =  name: "John", age: 30, city: "New York" ;

const template = document.createElement('template');

class TabelComponent extends htmlElement 

    constructor() 
        super();
        this.attachShadow(mode: 'open');
        this.shadowRoot.appendChild(template.content.cloneNode(true));
    

    connectedCallback() 
        console.log(this.content);
    
    
    get content() 
        return this.getAttribute("content")
    


window.customElements.define('table-component', TabelComponent);
<table-component content=obj></table-component>

我的属性“内容”应该将 obj 作为对象而不是字符串。我应该得到 name: "John", age: 30, city: "New York" ; 在控制台中

【问题讨论】:

html 属性是字符串。如果您想将该字符串作为对象,请使用JSON.parse() 【参考方案1】:

您所要求的通常是框架的工作。

最好通过属性而不是属性将对象传递给 Web 组件:

属性

var obj =  name: "John", age: 30, city: "New York" ;

class TabelComponent extends HTMLElement 
  constructor() 
    super();
    this.attachShadow(mode: 'open').innerHTML = '<h1>your DOM here</h1><pre id="content"></pre>';
  

  connectedCallback() 
  

  set content(val) 
    this._content = val;
    // render changes
    let el = this.shadowRoot.querySelector('#content');
    el.textContent = JSON.stringify(this._content,0,2);
  
  get content() 
    return this._content;
  


window.customElements.define('table-component', TabelComponent);

setTimeout(() => 
  let el = document.getElementById('mine');
  el.content = obj;
, 10);
&lt;table-component id="mine"&gt;&lt;/table-component&gt;

数据 URI

但是,如果您真的想通过属性传递一些东西,那么请考虑使用数据 URI 的标准。 (https://devdocs.io/http/basics_of_http/data_uris)

这是通过始终使用fetch 来获取数据并将content 属性视为URL 来完成的。这允许您从 URL 加载数据,并且仍然允许您直接传入 JSON 数据。

您的数据确实需要完全符合 JSON 标准,并且还需要进行 URL 编码。

最简单的方法是使用observedAttributes 静态函数定义您要观看的属性,然后attributeChangedCallback 知道属性何时发生变化。请注意,当属性被删除时,newVal 将变为 null,您需要以不同方式处理它。

下面的例子很简单,需要一些错误检查。但它展示了如何使用数据 URI 来完成任务。

class TabelComponent extends HTMLElement 
  constructor() 
    super();
    this.attachShadow(mode: 'open').innerHTML = '<h1>your DOM here</h1><pre id="content"></pre>';
  

  static get observedAttributes() 
    return ['content'];
  

  attributeChangedCallback(attrName, oldVal, newVal) 
    if (oldVal !== newVal) 
      if (newVal === null) 
        // Attribute was removed
        this._content = null;
        this.render();
      
      else 
        fetch(newVal).then(
          (res) => res.json()
        ).then(
          (data) => 
            this._content = data;
            this.render();
          
        );
      
    
    

  connectedCallback() 
  

  render() 
    let el = this.shadowRoot.querySelector('#content');
    el.textContent = JSON.stringify(this._content,0,2);
  

  get content() 
    return this._content;
  


window.customElements.define('table-component', TabelComponent);

setTimeout(() => 
  let el = document.getElementById('mine');
  el.content = obj;
, 10);
&lt;table-component content="data:application/json,%7B%22name%22%3A%22John%22%2C%22age%22%3A30%2C%22city%22%3A%22New%20York%22%7D" id="mine"&gt;&lt;/table-component&gt;

属性中的 JSON

如果你真的不想使用完整的数据 URI,你可以将代码剪裁一点:

class TabelComponent extends HTMLElement 
  constructor() 
    super();
    this.attachShadow(mode: 'open').innerHTML = '<h1>your DOM here</h1><pre id="content"></pre>';
  

  static get observedAttributes() 
    return ['content'];
  

  attributeChangedCallback(attrName, oldVal, newVal) 
    if (oldVal !== newVal) 
      if (newVal === null) 
        // Attribute was removed
        this._content = null;
        this.render();
      
      else 
        console.log(newVal);
        this._content = JSON.parse(newVal);
        this.render();
      
    
    

  connectedCallback() 
  

  render() 
    let el = this.shadowRoot.querySelector('#content');
    el.textContent = JSON.stringify(this._content,0,2);
  

  get content() 
    return this._content;
  


window.customElements.define('table-component', TabelComponent);
&lt;table-component content="&amp;quot;name&amp;quot;:&amp;quot;John&amp;quot,&amp;quot;age&amp;quot;:30,&amp;quot;city&amp;quot;:&amp;quot;New York&amp;quot;" id="mine"&gt;&lt;/table-component&gt;

您可能会注意到,这确实需要您将双引号编码为 &amp;quot; 以防止意外的属性值结束。

当然,您总是可以强制您的属性使用单引号,然后不需要转义双引号,但您需要转义任何单引号:

&lt;table-component content='"name":"John","age":30,"city":"New York"' id="mine"&gt;&lt;/table-component&gt;

属性中的变量值

我真正认为您所要求的是能够通过在属性中设置变量名称来将变量的值传递给组件:

&lt;table-component content='&lt;variable name&gt;' id="mine"&gt;&lt;/table-component&gt;

这是可以做到的,但你现在正在进入框架领域。

问题是那个变量的作用域是什么?是全球性的吗?是成员变量吗?是别的吗?

如果您只想使用全局变量,那么这很容易。 但非常有限!

var obj =  name: "John", age: 30, city: "New York" ;

class TabelComponent extends HTMLElement 
  constructor() 
    super();
    this.attachShadow(mode: 'open').innerHTML = '<h1>your DOM here</h1><pre id="content"></pre>';
  

  static get observedAttributes() 
    return ['content'];
  

  attributeChangedCallback(attrName, oldVal, newVal) 
    if (newVal === null) 
      // Attribute was removed
      this._content = null;
      this.render();
    
    else 
      this._content = window[newVal];
      this.render();
    
    

  connectedCallback() 
  

  render() 
    let el = this.shadowRoot.querySelector('#content');
    el.textContent = JSON.stringify(this._content,0,2);
  

  get content() 
    return this._content;
  


window.customElements.define('table-component', TabelComponent);
&lt;table-component content="obj"&gt;&lt;/table-component&gt;

obj 的值发生变化时,这不会自动更新。要让组件重新读取变量,您需要更改属性的值。但这比仅设置属性要复杂。

多个属性

这里没有代码,但最简单的版本可能是创建多个属性,每个属性都会影响 DOM 的一部分。然后您可以在需要更改时设置每个属性,而不是一直重置所有内容。

【讨论】:

【参考方案2】:

使用JSON.stringify()Element.setAttribute() 将您的对象转换为字符串:

var obj =  name: "John", age: 30, city: "New York" 
document.querySelector( 'table-component' ).setAttribute( 'content', JSON.stringify( obj ) )

并使用JSON.parse()Element.getAttribute() 将字符串属性转换为对象:

get content() 
    return JSON.parse( this.getAttribute( 'content' ) )

var obj =  name: "John", age: 30, city: "New York" ;
document.querySelector( 'table-component' ).setAttribute( 'content', JSON.stringify(obj) )

const template = document.createElement('template');

class TabelComponent extends HTMLElement 

    constructor() 
        super();
        this.attachShadow(mode: 'open');
        this.shadowRoot.appendChild(template.content.cloneNode(true));
    

    connectedCallback() 
        console.log(this.content);
    
    
    get content() 
        return JSON.parse( this.getAttribute( "content" ) )
    


window.customElements.define('table-component', TabelComponent);
&lt;table-component content=''&gt;&lt;/table-component&gt;

【讨论】:

【参考方案3】:

您可以将对象解析为 JSON 字符串,例如:

var obj =  name: "John", age: 30, city: "New York" ;
var objString = JSON.stringify(obj);
<table-component id="h" content="objString "></table-component>

然后在你的 web 组件中应该将它解析回你的对象

var obj = JSON.parse(this.content);

【讨论】:

以上是关于如何将对象(或关联数组)作为属性值传递给我的 Web 组件的主要内容,如果未能解决你的问题,请参考以下文章

如何将值的“数组”传递给我的存储过程?

更新:如何将默认值传递给我的模态输入元素

将JSON对象作为属性传递给指令

如何从 Reactjs 中的 useEffect 挂钩访问道具

将实例传递给对象与稍后添加实例作为属性[关闭]

将二维数组从 C# 传递到 C++