javascript Web组件,将数据传入和传出

k4emjkb1  于 2023-02-02  发布在  Java
关注(0)|答案(6)|浏览(111)

我的理解是,数据通过其属性传递给自定义html元素,并通过调度CustomEvent发送出去。
JavaScript对象显然可以在事件的 detail 字段中发送出去,但是如果元素需要传入大量数据,有没有办法在JavaScript中为它提供一个对象呢?
例如,如果元素包含需要动态初始化或更改的可变数量的部分(例如,具有可变行数的表),该怎么办?我可以想象设置和修改由组件内部解析的JSON字符串组成的属性,但感觉这不是一种优雅的处理方式:

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

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

jaql4c8m

jaql4c8m1#

传入数据

如果您确实希望/需要将大量数据传递到组件中,那么您可以通过四种不同的方式来完成:
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组件中使用嵌入式数据的示例:

一个二个一个一个
你可以用XHR做同样的事情,但是如果你的浏览器支持Web组件,那么它可能也支持fetch。如果你真的需要的话,有几个很好的fetch polyfills。
使用选项4的最大好处是你 * 可以 * 从URL**获取数据,**你 * 可以 * 直接嵌入数据,这正是大多数预定义的HTML元素,比如<img>的工作方式。

    • 更新**

我确实想到了第五种将JSON数据放入对象的方法。那就是在组件中使用<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)事件。这可能是添加到组件中的最重要的东西。事件应该在组件中的状态改变时触发。事件应该基于用户交互来触发,并且只是为了提醒用户发生了一些事情或者有一些事情可用。传统上,您会在事件中包含相关数据。这减少了组件用户需要编写的代码量。是的,它们仍然可以读取属性或特性,但是如果您的事件包含所有相关数据,那么它们可能不需要做任何额外的事情。

ajsxfq5m

ajsxfq5m2#

还有第六种方法,它确实类似于上面@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>
t30tvxxf

t30tvxxf3#

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

bksxznpy4#

使用Lego这样的小型库将允许您编写以下内容:

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

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

<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>
9bfwbjaz

9bfwbjaz5#

我知道这个问题已经得到了解答,但我还是采取了一种方法,我知道这不是什么高深的科学,也许有理由不这样做;然而,对我来说,这效果很好。
这是一种传递数据的间接方法,其中名为wc_data的属性被传递到自定义元素中,该元素是一个可以使用一次的"键"。
很明显,你可以对wc数据做任何事情,比如回调和"callins"到custom-tag。
link to codesandbox
文件:
wc_数据. ts

export const wc_data: {
    [name: string]: any,
    get(key: string): any,
    set(key: string, wc_data: any): any
} = {

    get(key: string): any {
        const wc_data = this[key];
        delete this[key];
        return wc_data;
    },

    set(p_key: string, wc_data: any) {
        this[p_key] = wc_data;
    }

}

CustomTag.ts

import { wc_data } from './wc_data';

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

template.innerHTML = `

    <style>
        .custom-tag {
            font-size: 1.6em;
        }
    </style>

    <button class="custom-tag">Hello <span name="name"></span>, I am your <span name="relation"></span></button>
`;

class CustomTag extends HTMLElement {

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

    callin() {
        console.log('callin called');
    }

    connectedCallback() {
        const v_wc_data = wc_data.get(this.getAttribute('wc-data'));
        console.log('wc_data', v_wc_data);

        const v_name = this.shadowRoot.querySelector('[name="name"]');
        const v_relation = this.shadowRoot.querySelector('[name="relation"]');

        v_name.innerHTML = v_wc_data.name;
        v_relation.innerHTML = v_wc_data.relation;

        const v_button = this.shadowRoot.querySelector('button');

        v_button.style.color = v_wc_data.color;

        v_wc_data.element = this;

        v_button.addEventListener('click', () => v_wc_data.callback?.());

    }

    disconnectedCallback() {
    }
}

window.customElements.define('custom-tag', CustomTag);

console.log('created custom-tag element');

export default {};

SomeTsFile.ts

wc_data.set('tq', {
    name: 'Luke',
    relation: 'father',
    color: 'blue',
    element: undefined,
    callback() {
        console.log('the callback worked');
        const v_tq_element = this.element;
        console.log(this.element);
        v_tq_element.callin();
    },
});

一些html ..

<div>stuff before..</div>

<custom-tag wc_data="tq" />

<div>stuff after...</div>
bybem2ql

bybem2ql6#

感谢其他的贡献者,我想出了这个看起来更简单的解决方案。没有json解析。我用这个例子把整个组件 Package 在a-href中,使块可以点击:

customElements.define('ish-marker', class extends HTMLElement {
    constructor() {
      super()
      const template = document.getElementById('ish-marker-tmpl').content

      const wrapper = document.createElement("a")
      wrapper.appendChild( template.cloneNode(true) )
      wrapper.setAttribute('href', this.getAttribute('href'))

      const shadowRoot = this.attachShadow({mode: 'open'}).appendChild( wrapper )
    }
  })

<ish-marker href="https://go-here.com">
  ...
  // other things, images, buttons.
  <span slot='label'>Click here to go-here</span>
</ish-marker>

相关问题