如何使用 angular 元素定义自定义元素方法
How to define a custom element method with angular elements
我们的 Angular-Elements webcomponent 应该有两个方法 openModal()
和 closeModal()
可以在 React 中用作自定义元素、Vue 或任何其他库来更改可见性状态。
这个简单的javascript用法应该是可行的:
<my-modal id="modal"></my-modal>
<button id="openModal" onclick="openModal()">open</button>
<button id="closeModal" onclick="closeModal()">close</button>
<script>
const modal = document.getElementById("modal");
function openModal() {
modal.openModal();
}
function closeModal() {
modal.closeModal();
}
</script>
内部属性 visible
应设置为 true
或 false
以便模板可以使用它。
@Component({
selector: 'my-modal',
template: `<p>Attribute visible: {{visible}}</p>`,
styles: []
})
export class MyModalComponent {
// Without `@Input` changes to the variable within `openModal()`
// and `closeModal()` will not effect the components state!
visible = false;
// Without the `@Input()` here, angular-elements will not
// map the `openModal()` to our custom-element.
@Input()
public openModal(): void {
console.log("Open Modal")
this.visible = true;
}
@Input()
public closeModal(): void {
console.log("Close Modal")
this.visible = false;
}
}
问题:
- 为什么我们需要装饰我们的 public 组件方法(
openModal()
和 closeModal()
与 @Input()
),即使我们没有将它们用作输入参数?
- 为什么我们需要用
@Input
装饰visible
-Flag?这非常糟糕,因为我们暴露了内部状态,而且它看起来是一种有效的方法?
@Input()
s 是为属性而不是函数设计的。对于您的用例,您可以在那里简单地使用 getter 和 return 函数。
至于visible
状态没变:确实变了(变后用console.log
查一下)。但是,angular 的更改检测未检测到更改,因为调用来自 angular 的上下文。例如,在使用 setTimeout
更改值时,您会遇到同样的问题。要解决此问题,请将 NgZone
注入您的组件,并使用 this.zone.run
让 angular 检测更改。
import { Component, Input, NgZone } from '@angular/core';
@Component({
selector: 'app-root',
template: `<p>Attribute visible: {{visible}}</p>`,
styles: []
})
export class MyModalComponent {
visible = false;
// use property getters to return functions
@Input()
get openModal(): () => void {
return () => {
// make change in angular's context so changes are detected by angular
this.zone.run(() => this.visible = true);
}
}
@Input()
get closeModal(): () => void {
return () => {
// make change in angular's context so changes are detected by angular
this.zone.run(() => this.visible = false);
}
}
// inject NgZone
constructor(private zone: NgZone) { }
}
或者,您可能想尝试一下 OnPush
变化检测。您将使用 Subjects 和 Observables 以及一个“推送”管道(异步管道的变体),如此包中所实现的:https://github.com/rx-angular/rx-angular
// in app.module.ts
import {TemplateModule} from '@rx-angular/template';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
TemplateModule // import module
// ...
import { Component, Input, NgZone } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Component({
selector: 'app-root',
// use observable with push pipe
template: `<p>Attribute visible: {{visible$ | push}}</p>`,
// use OnPush change detection
changeDetection: ChangeDetectionStrategy.OnPush,
styles: []
})
export class AppComponent {
// use subjects/observables
visibleSubject = new BehaviorSubject<boolean>(false);
visible$ = this.visibleSubject.asObservable();
@Input()
get openModal(): () => void {
return () => {
this.visibleSubject.next(true);
}
}
@Input()
get closeModal(): () => void {
return () => {
this.visibleSubject.next(false);
}
}
}
当然,如果它像更改布尔标志一样简单,您也可以将 属性 公开为 @Input()
,这样它就可以在主机应用程序中绑定。
我们的 Angular-Elements webcomponent 应该有两个方法 openModal()
和 closeModal()
可以在 React 中用作自定义元素、Vue 或任何其他库来更改可见性状态。
这个简单的javascript用法应该是可行的:
<my-modal id="modal"></my-modal>
<button id="openModal" onclick="openModal()">open</button>
<button id="closeModal" onclick="closeModal()">close</button>
<script>
const modal = document.getElementById("modal");
function openModal() {
modal.openModal();
}
function closeModal() {
modal.closeModal();
}
</script>
内部属性 visible
应设置为 true
或 false
以便模板可以使用它。
@Component({
selector: 'my-modal',
template: `<p>Attribute visible: {{visible}}</p>`,
styles: []
})
export class MyModalComponent {
// Without `@Input` changes to the variable within `openModal()`
// and `closeModal()` will not effect the components state!
visible = false;
// Without the `@Input()` here, angular-elements will not
// map the `openModal()` to our custom-element.
@Input()
public openModal(): void {
console.log("Open Modal")
this.visible = true;
}
@Input()
public closeModal(): void {
console.log("Close Modal")
this.visible = false;
}
}
问题:
- 为什么我们需要装饰我们的 public 组件方法(
openModal()
和closeModal()
与@Input()
),即使我们没有将它们用作输入参数? - 为什么我们需要用
@Input
装饰visible
-Flag?这非常糟糕,因为我们暴露了内部状态,而且它看起来是一种有效的方法?
@Input()
s 是为属性而不是函数设计的。对于您的用例,您可以在那里简单地使用 getter 和 return 函数。
至于visible
状态没变:确实变了(变后用console.log
查一下)。但是,angular 的更改检测未检测到更改,因为调用来自 angular 的上下文。例如,在使用 setTimeout
更改值时,您会遇到同样的问题。要解决此问题,请将 NgZone
注入您的组件,并使用 this.zone.run
让 angular 检测更改。
import { Component, Input, NgZone } from '@angular/core';
@Component({
selector: 'app-root',
template: `<p>Attribute visible: {{visible}}</p>`,
styles: []
})
export class MyModalComponent {
visible = false;
// use property getters to return functions
@Input()
get openModal(): () => void {
return () => {
// make change in angular's context so changes are detected by angular
this.zone.run(() => this.visible = true);
}
}
@Input()
get closeModal(): () => void {
return () => {
// make change in angular's context so changes are detected by angular
this.zone.run(() => this.visible = false);
}
}
// inject NgZone
constructor(private zone: NgZone) { }
}
或者,您可能想尝试一下 OnPush
变化检测。您将使用 Subjects 和 Observables 以及一个“推送”管道(异步管道的变体),如此包中所实现的:https://github.com/rx-angular/rx-angular
// in app.module.ts
import {TemplateModule} from '@rx-angular/template';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
TemplateModule // import module
// ...
import { Component, Input, NgZone } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Component({
selector: 'app-root',
// use observable with push pipe
template: `<p>Attribute visible: {{visible$ | push}}</p>`,
// use OnPush change detection
changeDetection: ChangeDetectionStrategy.OnPush,
styles: []
})
export class AppComponent {
// use subjects/observables
visibleSubject = new BehaviorSubject<boolean>(false);
visible$ = this.visibleSubject.asObservable();
@Input()
get openModal(): () => void {
return () => {
this.visibleSubject.next(true);
}
}
@Input()
get closeModal(): () => void {
return () => {
this.visibleSubject.next(false);
}
}
}
当然,如果它像更改布尔标志一样简单,您也可以将 属性 公开为 @Input()
,这样它就可以在主机应用程序中绑定。