Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update ngModel from directive?

I've created a directive to limit the length of input field type=number.

// Input

<input min="1" appLimitTo [limit]="5" type="number" name="property" [(ngModel)]="property">

// Directive

import {Directive, HostListener, Input} from '@angular/core';

@Directive({
  selector: '[appLimitTo]',
})
export class LimitToDirective {

    @Input() limit: number;
    @Input() ngModel: any;

    @HostListener('input', ['$event'])
    onInput(e) {
        if (e.target.value.length >= +this.limit) {
            e.target.value = (e.target.value).toString().slice(0, this.limit - 1);
            e.preventDefault();
        }
    }
}

It works fine if we enter the value through the keyboard. But the problem arises when if I copy & paste 12345678913465789 this number, this line e.target.value = (e.target.value).toString().slice(0, this.limit - 1); shorten it to the limit, but the ngModel still contains 12345678913465789 value. How to update this ngModel value?

Please help.

PS - What should I add in my directive to meet the requirement?

like image 785
Yashwardhan Pauranik Avatar asked Apr 09 '18 13:04

Yashwardhan Pauranik


People also ask

How do I update my ngModel value?

If we use two way binding syntax for ngModel the value will be updated. So the default (ngModelChange) function will update the value of ngModel property. i.e., user.Name . And the second (ngModelChange) will be triggered printing the user name value in the console.

Is ngModel attribute directive?

The ngModel directive binds an input , select , textarea (or custom form control) to a property on the scope using NgModelController, which is created and exposed by this directive. ngModel is responsible for: Binding the view into the model, which other directives such as input , textarea or select require.

What is the difference between [( ngModel )] and ngModel?

The answer is: (ngModel) causes a 1-way data-binding, whereas [(ngModel)] ensures a two-way data binding.


1 Answers

You can inject NgControl into your own directive. You can then listen to the control valueChanges event.

limit-to.directive.ts

import {Directive, HostListener, Input, OnInit, OnDestroy} from '@angular/core';
import {NgControl} from '@angular/forms';
import {map} from 'rxjs/operators';
import {Subscription} from 'rxjs/Subscription';

@Directive({
  selector: '[appLimitTo]',
})
export class LimitToDirective implements OnInit, OnDestroy {
    @Input('appLimitTo') limit: number;

    private subscription: Subscription;

    constructor(private ngControl: NgControl) {}

    ngOnInit() {
      const ctrl = this.ngControl.control;

      this.subscription = ctrl.valueChanges
        .pipe(map(v => (v || '').toString().slice(0, this.limit)))
        .subscribe(v => ctrl.setValue(v, { emitEvent: false }));
    }

    ngOnDestroy() {
      this.subscription.unsubscribe();
    }
}

usage:

<input ngModel appLimitTo="3" type="number" />

Live demo

like image 73
Tomasz Kula Avatar answered Oct 30 '22 15:10

Tomasz Kula