Web组件,传入和传出数据

时间:2018-05-18 06:22:04

标签: javascript html json web-component

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

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

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

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

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

4 个答案:

答案 0 :(得分:21)

传递数据

如果您确实需要/需要将大量数据传递到组件中,那么您可以通过以下四种方式执行此操作:

1)使用属性。这是最简单的,因为您只需通过将值赋给元素来传递Object:el.data = myObj;

2)使用属性。我个人讨厌这种方式,但是有些框架需要通过属性传递数据。这与您在问题中的显示方式类似。 <my-el data="[{a:1},{a:2}....]"></my-el>。请务必遵守与escaping attribute values相关的规则。如果您使用此方法,则需要在属性上使用JSON.parse,这可能会失败。它也可能在HTML中变得非常难看,以便在属性中显示大量数据。

3 通过子元素传入。<select>元素与<option>子元素一起考虑。您可以将任何元素类型用作子元素,它们甚至不需要是真实元素。在connectedCallback函数中,您的代码只会抓取所有子代,并将元素,属性或内容转换为组件所需的数据。

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

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

img {
  height: 32px;
  width: 32px;
}
<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 .st0{fill:transparent;stroke:%23231F20;stroke-width:12;} .st1{fill:%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">

以下是在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。如果你真的需要,可以使用几种优质的填充剂。

使用选项4的最大好处是,您可以 > 直接嵌入数据的网址获取数据。这正是大多数预定义的HTML元素,如<img>的工作方式。

<强>更新

我确实想到了将JSON数据放入对象的第5种方法。这是在组件中使用<template>标记。这仍然需要您调用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)事件。这可能是添加到组件中最重要的事情。当组件中的状态发生变化时,事件应该触发。事件应基于用户交互触发,并仅提醒用户已发生某事或某事可用。传统上,您会在活动中包含相关数据。这减少了组件用户需要编写的代码量。是的,他们仍然可以读取属性或属性,但如果您的事件包含所有相关数据,那么他们可能不需要做任何额外的事情。

答案 1 :(得分:1)

第六种方法与上面@Intervalia的答案非常相似,但是使用<script>标签而不是<template>标签。

这与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>

答案 2 :(得分:0)

如果您使用的是基于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>

答案 3 :(得分:0)

使用诸如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>