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

时间:2016-01-18 02:29:24

标签: angular

我有一个Angular 1应用程序,它使用简单的contentEditable指令,可以在模板中使用这样的指示:

<span contenteditable="true"  ng-model="model.property" placeholder="Something">

编辑元素会触发$setViewValue(element.html()并按预期工作。

我想在Angular2中使用类似的简洁模板语法。理想情况下,我希望模板看起来像这样:

<span contentEditable="true" [(myProperty)]="name"></span>

其中'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; 
    }
}

如果我传递像{name: "someName"}之类的对象,这个想法是有效的,但是如果只传递一个属性,它似乎传递了值,而不是引用,因此绑定不会流回到组件。有没有办法做到这一点,仍然允许模板语法不冗长,但仍然允许轻松重用指令。

3 个答案:

答案 0 :(得分: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);
}

既然我们的指令正确地发出,我们必须捕获组件中的值。 为简洁起见,仅提供模板

<div contentEditable="true" [myProperty]="name" (updateProperty)="name = $event"></div>

updateProperty是指令中的@Output。当它被触发时,我们会抓住它,我们发现的值将被分配给$event。之后,我们将$event分配给我们的媒体资源name,您的应用就可以了。

这是您的plnkr工作。我希望它有所帮助。

更新

感谢answer我看到你要求的可能。

您可以将输出与语法[()]被取消时调用的内容进行匹配。如果您使用[(myProperty)]="expr"之类的语法,则会将其置于[myProperty]="expr" (myPropertyChange)="expr = $event"

所以将原来的答案改为如下

@Output() myPropertyChange: EventEmitter<any> = new EventEmitter();
update(event){
  this.myPropertyChange.emit(this.el.nativeElement.innerText);
}

它将为您提供此模板,这是您从一开始就提出的问题。

<div contentEditable="true" [(myProperty)]="name"></div>

此处plnkr更新为真实的正确答案。

答案 1 :(得分:2)

我发现这个非常流畅的解决方案适用于我的情况(使用现有的UI添加只读角色)使用@Host装饰器来注入要在Directive中设置其属性的组件。就我而言,我有一个具有readonly属性的抽象类,然后所有自定义组件都对其进行了扩展。

@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;
    }
  }
}

在选择器部分中,我直接放置实现BaseComponent的自定义组件的选择器(例如my-comp)。这是因为我希望将指令自动应用于my-comp的所有实例。我还有一个可以关闭指令的参数。

如果这是使用指令的单个组件,请将其放在BaseComponent的位置。

如果多个组件将使用同一指令-您需要通过在扩展类中指定提供程序来指定将在@Host参数上注入的内容:

@Component({
  selector: 'my-comp',
  ...
  providers: [{
      provide: BaseComponent, useExisting: forwardRef(() => MyComp)
  }]
})
export class MyComp extends BaseComponent { ... }

来源:https://github.com/angular/angular/issues/13776

答案 2 :(得分:0)

如果您只想使用纯 JavaScript 来更改,而 则不想转到 [(model] )路线,那么此路线适合您。

const input = this.el.nativeElement.querySelector('#myElement');
input.value = 'My Programmatic Value';
input.dispatchEvent(new Event('input'));

问题-https://github.com/text-mask/text-mask/issues/696

解决方案-https://github.com/text-mask/text-mask/issues/696#issuecomment-354887412

希望这对某人有帮助。

干杯!