typescript - LitElement:当模板被定义为 const 时,如何调用函数
当模板在单独的文件中定义时,无法在组件中调用函数。但如果模板直接定义,例如返回渲染 HTML 调用 this.funct,它是有效的。当模板在单独的文件中定义时,如何调用函数?
组件.ts
import { LitElement, html} from 'lit-element';
import { cTemplate } from './template/ctemplate';
@customElement('card-form')
export class cardFormComponent extends LitElement {
constructor() {
super();
}
render() {
return cTemplate;
}
createRenderRoot() {
return this;
}
validateForm() {
alert('ok');
}
}
**tempalate.ts**
import { html } from 'lit-element';
export const cTemplate = html`
<div>
<button class="button" @click="${this.validateForm}">Validate</button>
</div>
';
答案#1
我不确定你为什么要把模板放进另一个文件,但我想你的实际用例可能更复杂,或者你正试图在多个组件中重复使用相同的模板。
无论哪种情况,在尝试回答如何让你的具体示例工作之前,我建议先了解一下智能组件与哑组件的区别。简而言之,这个模板知道的太多了,它知道在按钮被点击时应该调用像 validateForm 这样的函数(这使模板与组件紧密耦合,因此某种程度上违背了将它们放在不同文件中的初衷)。一个可能更好的方法(如果你想解耦的话)是提供一个在按钮点击时运行的回调函数。该回调函数可以是点击按钮时要执行的任何操作。
现在,要修正你的例子(也许还可以让它更灵活),你必须理解在 JavaScript 中 this 的含义。this 是你函数的当前上下文,根据你的函数如何被创建和调用,它会被设置为不同的值。
在你的 template.ts 文件中,你没有一个函数(虽然模块被转译的方式会使其变成一个函数),但你仍然有一个 this,在这个上下文中,this 是模块本身的上下文。所以在你的模板片段中,你试图调用 module.validateForm,但它不存在。这是因为你在模块加载时创建片段,而不是在 render 需要它的时候创建。
解决方案的第一步是将代码片段创建到一个函数中并导出它,以便可以在 render 内部调用该函数来准备代码片段。通过这种方式,你可以开始控制函数内部的 this。我不确定下一步是否必要,但如果 this 仍然不是你的组件类的上下文,你将需要使用 bind 来强制 this 指向组件类。
请参阅下面的完整示例:
Component.ts
import { LitElement, html} from 'lit-element';
import { cTemplate } from './template/ctemplate'; // cTemplate is now a function, not a const
@customElement('card-form')
export class cardFormComponent extends LitElement {
constructor() {
super();
this.cTemplate = cTemplate.bind(this); // This step forces this inside of the cTemplate function to be the component class. This might not be needed.
}
render() {
return this.cTemplate(); // .bind() returns a new function, which we save on the component class so we use this in order to call the new version which has this = component class
}
validateForm() {
alert('ok');
}
}
**tempalate.ts**
import { html } from 'lit-element';
export function cTemplate() {
return html`
<div>
<button class="button" @click="${this.validateForm}">Validate</button>
</div>
';
}
为了改进:
BetterComponent.ts
import { LitElement, html} from 'lit-element';
import { cTemplate } from './template/ctemplate';
@customElement('card-form')
export class cardFormComponent extends LitElement {
constructor() {
super();
this.cTemplate = cTemplate.bind(this);
}
render() {
return this.cTemplate(this.validateForm, 'Validate');
}
validateForm() {
alert('ok');
}
}
**tempalate.ts**
import { html } from 'lit-element';
export function cTemplate(onClick, label) {
// cTemplate is now a function that creates a snippet for a button that calls a function when the button is clicked (or, a button "component")
return html`
<div>
<button class="button" @click="${onClick}">${label}</button>
</div>
';
}
我仍然要强调,这感觉像是一种有点奇怪的模式。如果你想创建一个可以重复使用的按钮组件,那么我会建议将其做成一个完整的独立 Web 组件。让 render 返回目前 cTemplate 所做的内容,而在 cardForm 内部,我会让 render 仅仅创建类似于:
html`<my-button label="${'Validate'}" .onClick="${this.validateForm}"></my-button>`
你也可以让 my-button 像真正的按钮一样触发 click 事件,并使用 @click。
答案#2
为澄清您代码片段中的问题:
- 你并没有在 template.ts 中定义一个模板函数,你只是在创建一个不变的模板结果。模板通常应该是函数。
- 你在
render()中没有调用模板。 - 独立的模板函数不应该使用
this。
这就是我会改写它的方式:
import { LitElement, html} from 'lit-element';
import { cTemplate } from './template/ctemplate';
@customElement('card-form')
export class cardFormComponent extends LitElement {
render() {
return cTemplate(this.validateForm, 'Validate');
}
validateForm() {
alert('ok');
}
}
/** template.ts **/
import { html } from 'lit-element';
export const cTemplate = (onClick, label) => html`
<div>
<button class="button" @click="${onClick}">${label}</button>
</div>
`;
请注意,我没有绑定模板函数或点击处理程序。相反,将数据作为参数传递给模板,LitElement 会负责使用宿主元素作为 this 值来调用事件处理程序。
