关于angular中@Input装饰器传入值遇到的问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于angular中@Input装饰器传入值遇到的问题相关的知识,希望对你有一定的参考价值。
参考技术A 我们知道,在ng中,父子组件通信中的一种方式就是使用@Input/@Output装饰器,@Input用于数据从父组件流入子组件,@Output用于从子组件传出数据到父组件。准备工作:定义两个组件
父组件:HomePage
html:
ts:
子组件:
html:
ts:
测试效果:
从图片中我们可以看到:
关于何时在 TypeScript 中调用装饰器的困惑
【中文标题】关于何时在 TypeScript 中调用装饰器的困惑【英文标题】:Confusion about when decorators are called in TypeScript 【发布时间】:2018-09-02 20:31:06 【问题描述】:我一直认为 TypeScript 中的装饰器是在类的构造函数之后调用的。但是,我被告知不是这样,例如,this 帖子的最佳答案声称 装饰器是在声明类时调用的,而不是在实例化对象时调用。我参加的 Angular 课程的 Udemy 讲师还告诉我,Typescript 中的装饰器在属性初始化之前运行。
但是,我在这个主题上的实验似乎表明并非如此。例如,这是一个带有属性绑定的简单 Angular 代码:
test.component.ts
import Component, Input from '@angular/core';
@Component(
selector: 'app-test',
template: 'testString'
)
export class TestComponent
@Input() testString:string ="default string";
constructor()
console.log(this.testString);
app.component.html
<app-test testString="altered string"></app-test>
当我执行代码时,控制台会记录“默认字符串”而不是“更改的字符串”。这证明装饰器是在类的构造函数执行后调用的。
谁能给我一个关于何时调用装饰器的明确答案?因为我的在线研究与我所做的实验相矛盾。谢谢!
【问题讨论】:
您链接到的帖子非常详尽,并且明确指出“在声明类时调用装饰器,而不是在实例化对象时调用。”就是这样。您可以通过替换方法或类来获得实例的行为(类和方法装饰器可以做到这一点),但这是特定于装饰器实现的。装饰器本身在类声明中运行 【参考方案1】:装饰器在类被声明时调用——而不是在对象被声明时 实例化。
没错。
作为@H.B.已经说过了,我们可以通过查看转译的代码来证明。
var TestComponent = /** @class */ (function ()
function TestComponent()
this.testString = "default string";
console.log(this.testString);
__decorate([
core_1.Input(),
__metadata("design:type", String)
], TestComponent.prototype, "testString", void 0);
TestComponent = __decorate([
core_1.Component(
selector: 'app-test',
template: 'testString'
),
__metadata("design:paramtypes", [])
], TestComponent);
return TestComponent;
());
现在,让我们通过接下来的步骤来了解您错在哪里。
Step 1. 主要目的是提供元数据
当我执行代码时,控制台会记录“默认字符串”而不是 “改变的字符串”。这证明装饰器是在 类的构造函数执行。
只有知道 @Input()
装饰器的作用才能确定。
Angular @Input
装饰器只是 装饰 带有一些信息的组件属性。
这只是元数据,在TestComponent.__prop__metadata__
属性中将是stored。
Object.defineProperty(constructor, PROP_METADATA, value: )[PROP_METADATA]
第 2 步。Angular 编译器。
现在是 Angular 编译器收集有关组件的所有信息(包括 @Input
元数据)以生成组件工厂的时候了。准备好的元数据如下所示:
"selector": "app-test",
"changeDetection": 1,
"inputs": [
"testString"
],
...
"outputs": [],
"host": ,
"queries": ,
"template": "testString"
(注意:当 Angular TemplateParser 遍历模板时,它使用此元数据 to check 指令是否输入名称为 testString
)
基于元数据编译器constructsupdateDirective 表达式:
if (dirAst.inputs.length || (flags & (NodeFlags.DoCheck | NodeFlags.OnInit)) > 0)
updateDirectiveExpressions =
dirAst.inputs.map((input, bindingIndex) => this._preprocessUpdateExpression(
nodeIndex,
bindingIndex,
sourceSpan: input.sourceSpan,
context: COMP_VAR,
value: input.value
));
将包含在生产工厂中:
我们可以在上面注意到更新表达式是在父视图(AppComponent)中生成的。
步骤 3. 变化检测
在 Angular 生成所有工厂并初始化所有必要对象后,它会从顶视图到所有子视图循环运行更改检测。
在此过程中,角度调用checkAndUpdateView 函数,它还调用updateDirectiveFn:
export function checkAndUpdateView(view: ViewData)
if (view.state & ViewState.BeforeFirstCheck)
view.state &= ~ViewState.BeforeFirstCheck;
view.state |= ViewState.FirstCheck;
else
view.state &= ~ViewState.FirstCheck;
shiftInitState(view, ViewState.InitState_BeforeInit, ViewState.InitState_CallingOnInit);
markProjectedViewsForCheck(view);
Services.updateDirectives(view, CheckType.CheckAndUpdate); <====
这是您的@Input
属性gets value 的第一个位置:
providerData.instance[propName] = value;
if (def.flags & NodeFlags.OnChanges)
changes = changes || ;
const oldValue = WrappedValue.unwrap(view.oldValues[def.bindingIndex + bindingIdx]);
const binding = def.bindings[bindingIdx];
changes[binding.nonMinifiedName !] =
new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0);
如你所见,它发生在 ngOnChanges
钩子之前。
结论
Angular 在装饰器执行期间不会更新 @Input
属性值。变更检测机制负责此类事情。
【讨论】:
【参考方案2】:你可以只看生成的代码,例如
const defaultValue = (value: any) =>
(target: any, propertyKey: string, descriptor: PropertyDescriptor) =>
target[propertyKey] = value;
;
class Test
@defaultValue("steven")
myProperty: string;
constructor()
console.log(this.myProperty);
new Test();
会产生这个:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc)
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
;
var defaultValue = function (value)
return function (target, propertyKey, descriptor)
target[propertyKey] = value;
;
;
var Test = /** @class */ (function ()
function Test()
console.log(this.myProperty);
__decorate([
defaultValue("steven")
], Test.prototype, "myProperty", void 0);
return Test;
());
new Test();
如您所见,__decorate
函数是在 类声明时对属性调用的。这将根据装饰器代码重新定义属性。对于 Angular,这可能只是设置一些元数据,为输入类启动。这里直接设置值。
因此,这里的属性已经在构造函数中发生了变化。
【讨论】:
【参考方案3】:您感到困惑的原因不是装饰器是如何工作的,而是 Angular 如何更新它的输入绑定属性。你可以证明自己和
ngOnInit()
console.log(this.testString) // see updated value
这是因为 ngOnInit
在第一个 ngOnChanges
和 ngOnChanges
更新您的输入之后被调用。
【讨论】:
所以在 Typescript 中使用类声明调用装饰器,但 Angular 中的输入绑定行为仅在第一个 ngOnChanges 中首先执行,它在构造函数之后调用。这是正确的吗? @Eddie Lin - 是的,装饰器只是一个附加到类、方法、访问器或属性的函数(如您的情况)。该函数可以随心所欲,因此 Angular 编写了@Input
装饰器,它根据结果 NgOnChanges 更改目标属性值。我现在正在玩它,但找不到证明,所以我可以演示,但我很确定 @Input
正在构造函数级别进行评估,但根据 ngOnChanges 被调用以上是关于关于angular中@Input装饰器传入值遇到的问题的主要内容,如果未能解决你的问题,请参考以下文章
通过 Angular 2 中的 Input 装饰器使用多个属性
Angular 4 中的装饰器语法 @Input('someValue') 和 @Input() 有啥区别?