10

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
  • 3,783
  • 6
  • 19
  • 24
  • This looks related http://stackoverflow.com/questions/36106350/attribute-directive-with-ngmodel-to-change-field-value/37561412?noredirect=1#comment62675441_37561412 maybe you can get some ideas. – Günter Zöchbauer Jun 02 '16 at 18:00
  • Hi @GünterZöchbauer. Unfortunately those solutions didn't work for me. I've created a Plunker though to better illustrate the issue. https://plnkr.co/edit/1xMjzo – sharpmachine Jun 02 '16 at 19:22
  • What about Thierrys approach? – Günter Zöchbauer Jun 02 '16 at 19:25
  • @GünterZöchbauer Yeah that doesn't work either. They just both change the value of the input without updating the value of the model. – sharpmachine Jun 02 '16 at 19:25

3 Answers3

7

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>
sharpmachine
  • 3,783
  • 6
  • 19
  • 24
  • 3
    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. – Brad Kent Dec 09 '16 at 15:39
  • 1
    "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"` – Maxim Kuzmin May 09 '19 at 14:29
1

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.

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • 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: https://plnkr.co/edit/1xMjzo – sharpmachine Jun 02 '16 at 19:25
0

This is what i did with my datepicker , Might help.

//Create a method in your component to change the model
dateChanged(date) {
   this.hero.bday = date;
}

public ngOnInit() {
    //CREATE A REFERERENCE TO YOUR COMPONENT
    var component:CreateEventComponent = this;
    $("#bday").datepicker({
        dateFormat: "dd-mm-yy",
        altFormat: "dd-mm-yy",
        onSelect: function (dateText, datePicker) {
            //UPDATE YOUR MODEL FORM JQUERY CODE.
            component.dateChanged(dateText);
        }
    });
}
manukyanv07
  • 469
  • 6
  • 4