7

I want to implement a "TrimDirective", which deletes leading and trailing spaces from input fields, with Angular 2 RC 5 and model driven / reactive forms.

I managed to change the value of the input field, however I don't get a new value in the component in myForm.valueChanges().

See this plunker: http://plnkr.co/edit/ruzqCh?p=preview

How can I trigger a update of the formGroup when the directive changes the value?

template:

<form [formGroup]="myForm">
  <input formControlName="name" trim>
</form>
latest value: -{{ latestValue }}-

component:

@Component({
  selector: 'my-app',
  templateUrl: 'app/app.html'
})
export class AppComponent implements OnInit {

  private myForm: FormGroup;
  private latestValue: string;

  ngOnInit() {
    this.myForm = new FormGroup({
      name: new FormControl(),
    });
    this.myForm.valueChanges.subscribe(v => this.latestValue = v.name)
  }
}

directive:

@Directive({
    selector: '[trim]'
})

export class TrimDirective {

    private el: any;

    constructor(
        el: ElementRef
    ){
        this.el = el.nativeElement;
    }

    @HostListener('keypress')
    onEvent() {
        setTimeout(() => { // get new input value in next event loop!
            let value: string = this.el.value;
            if(value && (value.startsWith(' ') || value.endsWith(' '))) {
                console.log('trim!');
                this.el.value = value.trim();
            }
        },0);
    }
}
2
  • Try changing onEvent to onKeyDown Commented Oct 12, 2016 at 7:55
  • 3
    @viku that's not the issue. the issue is that this.el.value = value.trim(); updates the input's value and the DOM, but angular's "model" doesn't change. how can the directive force a detectChanges() or emit an event or something/anything (access the component/FormGroup/FormControl ?) Commented Dec 9, 2016 at 19:43

1 Answer 1

4

You can create and trigger a change event on the element to tell angular to update the model value.

let event: Event = document.createEvent("Event");
event.initEvent('input', true, true);
Object.defineProperty(event, 'target', {value: this.elementRef.nativeElement, enumerable: true});
this.renderer.invokeElementMethod(this.elementRef.nativeElement, 'dispatchEvent', [event]);
Sign up to request clarification or add additional context in comments.

3 Comments

I moved to template driven model in the meanwhile, but yeah, thanks, that works! plnkr.co/edit/CLrA3b5v1V8LgszC3Vqi?p=preview
note to others: add private renderer: Renderer to directive's constructor params and add import { Renderer } from '@angular/core'; to the file. Finally, unrelated: make sure that you're using the correct this I'm embarrassed to say that I've probably been trying various solutions to this problem inside an event listener callback where the context of this has changed. d'oh
the renderer is deprecated in the last version of angular and in the rendered2 it dosen't any invokeElementMethod

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.