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

12,040

Solution 1

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>

Solution 2

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.

Share:
12,040
sharpmachine
Author by

sharpmachine

Hi, I'm Jesse, and I like to party.

Updated on July 20, 2022

Comments

  • sharpmachine
    sharpmachine over 1 year

    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

  • sharpmachine
    sharpmachine almost 8 years
    Thanks. However this updates the value of the input but not the value of the model. I've created a plunker to further explain the issue: plnkr.co/edit/1xMjzo
  • Brad Kent
    Brad Kent over 7 years
    is ngOnInit in a separate directive? How is the directive getting access to the component?
  • Brad Kent
    Brad Kent over 7 years
    This does not work for me. I'm assuming it probably worked for 2.0.0-rc.1 (what was avail on June 2), but probably broken in 3 different ways since then.
  • Maxim Kuzmin
    Maxim Kuzmin almost 5 years
    "I don't understand why it works because I'm not binding ngModelChange to the element" - you actually do, whe you use [(ngModel)]="subject.address" it is translated to [ngModel]="subject.address" and (ngModelChange)="subject.address = $event"