从Angular2中的指令改变组件属性

Mar*_*yer 7 angular

我有一个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"}但是如果只是传递一个属性,它似乎传递了值,而不是引用,因此绑定不会流回到组件.有没有办法做到这一点,仍然允许模板语法不冗长,但仍然允许轻松重用指令.

Eri*_*nez 8

该指令不知道其父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更新到真正的正确答案.


Alb*_*ova 6

我发现这个非常顺利的解决方案适用于我的案例(向现有 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