我有一个Angular 1应用程序,它使用一个简单的contentEditable指令,可以像这样在模板中使用:
<span contenteditable="true" ng-model="model.property" placeholder="Something">
Run Code Online (Sandbox Code Playgroud)
编辑元素将触发$setViewValue(element.html()并按预期工作.
我想在Angular2中使用类似的简洁模板语法制作一些东西.理想情况下,我希望模板看起来像这样:
<span contentEditable="true" [(myProperty)]="name"></span>
Run Code Online (Sandbox Code Playgroud)
其中'name'是组件上的属性,并且指令在更改时更新组件.我觉得我很接近这个(Plunker Link):
//our root app component
import {Component, Input, Output Directive, ElementRef, Renderer, OnInit} from 'angular2/core'
@Directive({
selector: '[contentEditable]',
host: {
'(blur)': 'update($event)'
}
})
export class contentEditableDirective implements OnInit {
@Input() myProperty;
constructor(private el: ElementRef, private renderer: Renderer){}
update(event){
this.myProperty = this.el.nativeElement.innerText;
}
ngOnInit(){
this.el.nativeElement.innerText = this.myProperty;
}
}
Run Code Online (Sandbox Code Playgroud)
如果我传递一个像这样的对象,这个想法是有效的,{name: "someName"}但是如果只是传递一个属性,它似乎传递了值,而不是引用,因此绑定不会流回到组件.有没有办法做到这一点,仍然允许模板语法不冗长,但仍然允许轻松重用指令.
该指令不知道其父name属性.但是,您可以从指令中发出事件并在父项中捕获它.检查此示例
@Directive({
selector: '[contentEditable]',
host: {
'(input)': 'update($event)' // I changed it to input to see the changes immediatly
}
})
export class contentEditableDirective implements OnInit {
// Output that will emit outside the directive
@Output() updateProperty: EventEmitter<any> = new EventEmitter();
// When 'update' is called we emit the value
update(event){
this.updateProperty.emit(this.el.nativeElement.innerText);
}
Run Code Online (Sandbox Code Playgroud)
既然我们的指令正确发出,我们必须捕获组件中的值.为简洁起见,仅限模板
<div contentEditable="true" [myProperty]="name" (updateProperty)="name = $event"></div>
Run Code Online (Sandbox Code Playgroud)
updateProperty是@Output来自指令.当它被触发时,我们抓住它,我们将分配给我们的值$event.之后,我们分配$event到我们的财产name,你的应用程序工作.
这是你的plnkr工作.我希望它有所帮助.
感谢这个回答,我看到你可能要求的.
您可以将输出与语法[()]结构时调用的内容进行匹配.如果你有一个类似于[(myProperty)]="expr"它的语法[myProperty]="expr" (myPropertyChange)="expr = $event"
所以将原来的答案改为如下
@Output() myPropertyChange: EventEmitter<any> = new EventEmitter();
update(event){
this.myPropertyChange.emit(this.el.nativeElement.innerText);
}
Run Code Online (Sandbox Code Playgroud)
它将为您提供此模板,这是您从一开始就提出的问题.
<div contentEditable="true" [(myProperty)]="name"></div>
Run Code Online (Sandbox Code Playgroud)
这是plnkr更新到真正的正确答案.
我发现这个非常顺利的解决方案适用于我的案例(向现有 UI 添加只读角色),使用 @Host 装饰器注入要在指令中设置属性的组件。就我而言,我有一个具有只读属性的抽象类,然后由所有自定义组件扩展。
@Directive({
selector: 'authCheck'
})
export class ComponentReadonlyDirective implements OnInit {
constructor(private authService: AuthorizationService,
@Host() private baseComponent: BaseComponent) {
}
ngOnInit() {
if (!this.authorizationService.canEdit()) {
this.baseComponent.readonly = true;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在选择器部分中,我直接放置实现 BaseComponent 的自定义组件的选择器(例如 my-comp)。这是因为我希望该指令自动应用于 my-comp 的所有实例。我有一个额外的参数可以关闭该指令。
如果这是使用该指令的单个组件 - 将其放在 BaseComponent 的位置。
如果多个组件将使用相同的指令 - 您需要通过在扩展类中指定提供程序来指定将在 @Host 参数上注入的内容:
@Component({
selector: 'my-comp',
...
providers: [{
provide: BaseComponent, useExisting: forwardRef(() => MyComp)
}]
})
export class MyComp extends BaseComponent { ... }
Run Code Online (Sandbox Code Playgroud)
来源: https: //github.com/angular/angular/issues/13776