Changing Component Property from Directive in Angular2

The directive doesn't know about its parent name property. You can though emit an event from the directive and catch it in the parent. Check this example

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

Now that our directive is emitting correctly, we have to catch the value in the component. For brevity only the template

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

updateProperty is the @Output from the directive. When it gets triggered we catch it and the value we emied will be assigned to $event. After that we assign $event to our property name and you got your app working.

Here's your plnkr working. I hope it helps.

Update

Thank to this answer I saw that it is possible what you asked for.

You can match the Output to what is called when the syntax [()] is desugared. If you have a syntax like [(myProperty)]="expr" it is desugared to [myProperty]="expr" (myPropertyChange)="expr = $event"

So changing the original answer to as follows

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

It will give you this template, which is what you asked from the beginning.

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

Here's the plnkr updated to the real correct answer.


I found this very smooth solution that worked for my case (adding a readonly role to existing UI) using the @Host decorator for injecting the component you want to set property of in the Directive. In my case I have an abstract class with readonly property that is then extended by all custom components.

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

Where in the selector part I put directly the selectors of my custom components that implement the BaseComponent (e.g. my-comp). This is because I want the directive to be automatically applied to all instances of my-comp. I have an additional param that can turn the directive off.

If this is a single component to use the directive - put it on the place of BaseComponent.

If multiple components will use the same directive - you'll need to specify what will be injected on the @Host parameter by specifying a provider in the extending class:

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

Source: https://github.com/angular/angular/issues/13776


If you simply want to change the value using pure JavaScript and do not want to go towards the [(model]) route, then this is for you.

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

Issue - https://github.com/text-mask/text-mask/issues/696

Solution - https://github.com/text-mask/text-mask/issues/696#issuecomment-354887412

Hope this helps someone.

Cheers!

Tags:

Angular