我的理解是数据通过其属性传递给自定义html元素,并通过调度CustomEvent发送出去。
JavaScript对象显然可以在事件的 detail 字段中发送,但是如果元素需要传递大量数据,该怎么办呢?有没有办法在JavaScript中为它提供一个对象。
例如,如果元素包含需要动态初始化或更改的可变数量的部分(例如,具有可变行数的表),该怎么办?我可以想象设置和修改一个由在组件内部解析的JSON字符串组成的属性,但它不是一种优雅的继续方式:
<my-element tableRowProperties="[{p1:'v1', p2:'v2'}, {p1:'v1',p2:'v2'}, {p1:'v1',p2:'v2'}]"></my-element>
或者你可以让元素监听来自外部的包含数据有效载荷的事件吗?
答案 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">
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,[{"a":9},{"a":8},{"a":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":"<b>Hi!</b>"},{"c":"</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>