Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change an input's ngModel value using a directive Angular 2

I'm stuck on how to access and change an inputs ngModel value using a directive. The outcome of the issue is that the model's address value doesn't update when I select the desired address...it's just set to what I actually typed into the input, rather than final value of the input.

I type '830':

enter image description here

I select '8300 Fauntleroy Way Southwest, Seattle, WA, United States':

enter image description here

Resulting value:

{
  address: '830'
}

Desired value:

{
  address: '8300 Fauntleroy Way Southwest, Seattle, WA, United States'
}

In AngularJS I could do this:

(function() {
  'use strict';

  angular
  .module('casemanagerApp')
  .directive('googleplace', googleplace);

  function googleplace() {

    var directive = {
      require: 'ngModel',
      link: link
    };

    return directive;

    function link(scope, element, attrs, model) {
      var options = {
        types: [],
        componentRestrictions: {}
      };
      scope.gPlace = new google.maps.places.Autocomplete(element[0], options); // jshint ignore:line

      google.maps.event.addListener(scope.gPlace, 'place_changed', function() { // jshint ignore:line
        scope.$apply(function() {
          model.$setViewValue(element.val());
        });
      });
    }
  }

})();

But now that I'm trying to convert it Angular 2, I'm a little stuck. Here's what I have so far on the conversion:

/// <reference path="../../../../typings/browser/ambient/googlemaps/index.d.ts"/>

import { Directive, ElementRef, OnInit } from '@angular/core';

@Directive({
  selector: '[google-places]'
})
export class GooglePlaces implements OnInit {

  constructor(private _el: ElementRef) { }

  ngOnInit() {
    let gPlace = new google.maps.places.Autocomplete(this._el.nativeElement);
    google.maps.event.addListener(gPlace, 'place_changed', () => console.log(this._el.nativeElement));
  }

}

Usage:

<input type="text"
       ngControl="address"
       placeholder="Enter a location"
       [(ngModel)]="subject.address"
       #address="ngForm"
       google-places
       required>

The heart of the issue is I don't understand how to do the equivalent of model.$setViewValue(element.val()); in Angular 2.

Any assistance would be greatly appreciated.

Plunker

like image 366
sharpmachine Avatar asked Jun 02 '16 17:06

sharpmachine


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 is a directive that is used to bind the values of the HTML controls (input, select, and textarea) or any custom form controls, and stores the required user value in a variable and we can use that variable whenever we require that value. It also is used during form validations.

What does [( ngModel )] do?

ngModel is responsible for: Binding the view into the model, which other directives such as input , textarea or select require.

How does ngModel work in Angular 2?

This directive is used by itself or as part of a larger form. Use the ngModel selector to activate it. It accepts a domain model as an optional Input . If you have a one-way binding to ngModel with [] syntax, changing the domain model's value in the component class sets the value in the view.


2 Answers

I ended up getting this to work, although I don't understand why it works because I'm not binding ngModelChange to the element...but it works.

Directive:

/// <reference path="../../../../typings/browser/ambient/googlemaps/index.d.ts"/>

import { Directive, ElementRef, Output, EventEmitter, OnInit, NgZone } from '@angular/core';

@Directive({
  selector: '[google-places]'
})
export class GooglePlaces implements OnInit {
  @Output() ngModelChange: EventEmitter<any> = new EventEmitter(false);

  options = {
    types: ['address'],
    componentRestrictions: { country: "us" }
  };

  constructor(
    private _el: ElementRef,
    private _ngZone: NgZone) { }

  ngOnInit() {
    let gPlace = new google.maps.places.Autocomplete(this._el.nativeElement, this.options);
    google.maps.event.addListener(gPlace, 'place_changed', () => {
      this._ngZone.run(() =>
        this.ngModelChange.emit(this._el.nativeElement.value));
    });
  }

}

Component Template:

<input type="text"
            class="form-control"
            ngControl="address"
            id="subjectAddress"
            placeholder="Enter a location"
            [(ngModel)]="subject.address"
            #address="ngForm"
            google-places
            required>
like image 147
sharpmachine Avatar answered Sep 20 '22 01:09

sharpmachine


I would inject the ControlValueAccessor associated with your input. Here is a sample:

@Directive({
  selector: '[test]'
})
export class TestDirective {
  constructor(@Inject(NG_VALUE_ACCESSOR) private valueAccessor:ControlValueAccessor) {
    setTimeout(() => {
      this.valueAccessor[0].writeValue('test');
    }, 1000);
  }
}

See this plunkr for example: https://plnkr.co/edit/owhBHdBncAxlzwJ8xkfq?p=preview.

like image 42
Thierry Templier Avatar answered Sep 21 '22 01:09

Thierry Templier