Web 组件,传入和传出数据

Posted

技术标签:

【中文标题】Web 组件,传入和传出数据【英文标题】:Web Components, pass data to and from 【发布时间】:2018-10-28 12:29:15 【问题描述】:

我的理解是数据通过其属性传递给自定义 html 元素,并通过调度 CustomEvent 发送出去。

javascript 对象显然可以在事件的 detail 字段中发送出去,但是如果元素需要向其中传递大量数据怎么办。有没有办法在 JavaScript 中为它提供一个对象。

例如,如果元素包含需要动态初始化或更改的可变数量的部分(例如,具有可变行数的表格)怎么办?我可以想象设置和修改一个由在组件内部解析的 JSON 字符串组成的属性,但感觉不像是一种优雅的继续方式:

<my-element tableRowProperties="[p1:'v1', p2:'v2', p1:'v1',p2:'v2', p1:'v1',p2:'v2']"></my-element>

或者你可以让元素监听来自外部的包含数据负载的事件吗?

【问题讨论】:

您可以通过调用其方法或属性中的一个来向自定义元素传递数据或从其获取数据。 太棒了!当然!那是我需要掌握 Web 组件有用性的缺失部分(尽管很明显)。我有一种感觉,他们可以完全取代使用 jQuery 小部件工厂的需要,但直到现在才真正看到如何。谢谢! 不客气 :-) 这里的例子***.com/q/47510957/4600982 您可以使用来自 EHTML 的电子表单元素:github.com/Guseyn/EHTML 【参考方案1】:

传入数据

如果您真的想要/需要将大量数据传递到您的组件中,那么您可以通过四种不同的方式来实现:

1) 使用属性。这是最简单的,因为您只需将值传递给对象,如下所示:el.data = myObj;

2) 使用属性。我个人讨厌这种方式,但有些框架需要通过属性传入数据。这类似于您在问题中的显示方式。 &lt;my-el data="[a:1,a:2....]"&gt;&lt;/my-el&gt;。请注意遵守与escaping attribute values 相关的规则。如果您使用此方法,您将需要在您的属性上使用JSON.parse,这可能会失败。在 HTML 中,在属性中显示大量数据也会变得非常丑陋。

3 通过子元素传递它。想想&lt;select&gt; 元素和&lt;option&gt; 子元素。您可以将任何元素类型用作子元素,它们甚至不需要是真实元素。在您的 connectedCallback 函数中,您的代码只是抓取所有子元素并将元素、它们的属性或它们的内容转换为组件所需的数据。

4 使用 Fetch。 为您的元素提供一个 URL 以获取其自己的数据。想想&lt;img src="imageUrl.png"/&gt;。如果您已经拥有组件的数据,那么这似乎是一个糟糕的选择。但是浏览器提供了一个很酷的嵌入数据的功能,它类似于上面的选项 2,但由浏览器自动处理。

这是在图像中使用嵌入数据的示例:

img 
  height: 32px;
  width: 32px;
&lt;img src="data:image/svg+xml;charset=utf8,%3C?xml version='1.0' encoding='utf-8'?%3E%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 314.7 314.7'%3E%3Cstyle type='text/css'%3E .st0fill:transparent;stroke:%23231F20;stroke-width:12; .st1fill:%23231F20;stroke:%23231F20;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:10; %3C/style%3E%3Cg%3E%3Ccircle class='st0' cx='157.3' cy='157.3' r='150.4'/%3E%3Cpolygon class='st1' points='108,76.1 248.7,157.3 108,238.6'/%3E%3C/g%3E%3C/svg%3E"&gt;

下面是在 Web 组件中使用嵌入数据的示例:

function readSrc(el, url) 
    var fetchHeaders = new Headers(
      Accept: 'application/json'
    );

    var fetchOptions = 
      cache: 'default',
      headers: fetchHeaders,
      method: 'GET',
      mode: 'cors'
    ;

    return fetch(url, fetchOptions).then(
      (resp) => 
        if (resp.ok) 
          return resp.json();
        
        else 
          return 
            error: true,
            status: resp.status
          
        
      
    ).catch(
      (err) => 
        console.error(err);
      
    );
  

  class MyEl extends HTMLElement 
    static get observedAttributes() 
      return ['src'];
    

    attributeChangedCallback(attrName, oldVal, newVal) 
      if (oldVal !== newVal) 
        this.innerHtml = '';
        readSrc(this, newVal).then(
          data => 
            this.innerHTML = `<pre>
$JSON.stringify(data,0,2)
            </pre>`;
          
        );
      
    
  

  // Define our web component
  customElements.define('my-el', MyEl);
<!--
This component would go load its own data from "data.json"
<my-el src="data.json"></my-el>
<hr/>
The next component uses embedded data but still calls fetch as if it were a URL.
-->
<my-el src="data:json,[&quot;a&quot;:9,&quot;a&quot;:8,&quot;a&quot;:7]"></my-el>

您可以使用 XHR 执行相同的操作,但如果您的浏览器支持 Web 组件,那么它可能支持 fetch。如果你真的需要,有几个很好的 fetch polyfills。

使用选项 4 的最大优势是您可以从 URL 获取数据并且可以直接嵌入数据。这正是大多数预定义 HTML 元素(例如 &lt;img&gt;)的工作方式。

更新

我确实想到了将 JSON 数据放入对象的第 5 种方法。那就是在你的组件中使用&lt;template&gt; 标签。这仍然需要您调用 JSON.parse,但它可以清理您的代码,因为您不需要尽可能多地转义 JSON。

class MyEl extends HTMLElement 
  connectedCallback() 
    var data;
    
    try 
      data = JSON.parse(this.children[0].content.textContent);
    
    
    catch(ex) 
      console.error(ex);
    

    this.innerHTML = '';
    var pre = document.createElement('pre');
    pre.textContent = JSON.stringify(data,0,2);
    this.appendChild(pre);
  


// Define our web component
customElements.define('my-el', MyEl);
<my-el>
  <template>["a":1,"b":"&lt;b>Hi!&lt;/b>","c":"&lt;/template>"]</template>
</my-el>

传递数据

从组件中获取数据的方式有以下三种:

1) 从属性中读取值。这是理想的,因为属性可以是任何东西,并且通常采用您想要的数据格式。属性可以返回字符串、对象、数字等。

2) 读取一个属性。这要求组件使属性保持最新,并且可能不是最佳的,因为所有属性都是字符串。因此,您的用户需要知道他们是否需要就您的价值致电 JSON.parse

3) 事件。这可能是添加到组件中最重要的事情。当组件中的状态发生变化时应该触发事件。事件应该基于用户交互触发,并且只是提醒用户某事已经发生或某事可用。传统上,您会在事件中包含相关数据。这减少了组件用户需要编写的代码量。是的,他们仍然可以读取属性或属性,但如果您的事件包含所有相关数据,那么他们可能不需要做任何额外的事情。

【讨论】:

【参考方案2】:

第 6 种方式与上面@Intervalia 的答案非常相似,但使用&lt;script&gt; 标签而不是&lt;template&gt; 标签。

这与a Markdown Element 使用的方法相同。

class MyEl extends HTMLElement 
  connectedCallback() 
    var data;
    
    try 
      data = JSON.parse(this.children[0].innerHTML);
    
    
    catch(ex) 
      console.error(ex);
    

    this.innerHTML = '';
    var pre = document.createElement('pre');
    pre.textContent = JSON.stringify(data,0,2);
    this.appendChild(pre);
  


// Define our web component
customElements.define('my-el', MyEl);
<my-el>
  <script type="application/json">["a":1,"b":"<b>Hi!</b>","c":"</template>"]</script>
</my-el>

【讨论】:

【参考方案3】:

如果您使用基于 Polymer 的 Web 组件,则数据的传递可以通过数据绑定来完成。数据可以存储为 JSON 字符串,并通过上下文变量传递。

<p>JSON Data passed via HTML attribute into context variable of  and populating the variable into combobox.</p> 
<dom-bind><template>
<iron-ajax url='data:text/json;charset=utf-8,
                ["label": "Hydrogen", "value": "H"
                ,"label": "Oxygen"  , "value": "O"
                ,"label": "Carbon"  , "value": "C"
                ]'
           last-response="lifeElements" auto handle-as="json"></iron-ajax>
<vaadin-combo-box id="cbDemo"
        label="Label, value:[[cbDemoValue]]"
        placeholder="Placeholder"
        items="[[lifeElements]]"
        value=" cbDemoValue "
>
    <template>
        [[index]]: [[item.label]] <b>[[item.value]]</b>
    </template>
</vaadin-combo-box>
<vaadin-combo-box label="Disabled" disabled value="H" items="[[lifeElements]]"></vaadin-combo-box>
<vaadin-combo-box label="Read-only" readonly value="O" items="[[lifeElements]]"></vaadin-combo-box>   



<web-elemens-loader selection="
@polymer/iron-ajax,
@vaadin/vaadin-element-mixin/vaadin-element-mixin,
@vaadin/vaadin-combo-box,
"></web-elemens-loader>
</template></dom-bind>
<script src="https://cdn.xml4jquery.com/web-elements-loader/build/esm-unbundled/node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script><script type="module" src="https://cdn.xml4jquery.com/web-elements-loader/build/esm-unbundled/src/web-elemens-loader.js"></script>

【讨论】:

【参考方案4】:

使用诸如Lego 之类的小型库将允许您编写以下内容:

<my-element :tableRowProperties="[p1:'v1', p2:'v2', p1:'v1',p2:'v2', p1:'v1',p2:'v2']"></my-element>

在您的 my-element.html 网络组件中:

<template>
  <table>
    <tr :for="row in state.tableRowProperties">
      <td>$row.p1</td>
      <td>$row.p2</td>
    </tr>
</template>

<script>
  this.init() 
    this.state =  tableRowPropoerties: [] 
  
</script>

【讨论】:

以上是关于Web 组件,传入和传出数据的主要内容,如果未能解决你的问题,请参考以下文章

使用 python 驱动程序将数据传入和传出 C++ 代码

将数据从C#传入和传出DLL

传出 UDP 流量是不是允许此端口上的传入流量?

传入和传出字节的 SQL Server 性能计数器

将变量传入和传出 PayPal

关于angular中@Input装饰器传入值遇到的问题