tanoy009 / ng4-geoautocomplete

angular 4 compatable google autocomplete with server side api support and AOT enabled
MIT License
44 stars 44 forks source link

Edit input #11

Open roman-lisnyi opened 7 years ago

roman-lisnyi commented 7 years ago

Hello. I need your assistance. How can I add formControlName, ngModel, class or something like this into the input tag?

Thank you!

Webberjo commented 6 years ago

I was wondering how to do this as well for my Angular4 app using Angular's FormBuilder. I ended up setting the formControl's value in the callback and setting the input's style through the ng4geo-autocomplete tag.

HTML

<ng4geo-autocomplete (componentCallback)="autoCompleteCallback($event)"></ng4geo-autocomplete>

TS

autoCompleteCallback(selectedData: any) {
  this.myForm.get('myControl').setValue(selectedData.formatted_address);
}

SCSS

ng4geo-autocomplete {
  #search_places {
    // styles here
  }
}

To set the value of the input when retrieving data from a database, I did:

TS

// dbReturnValue = whatever value was returned from the database query
this.myForm.get('myControl').setValue(dbReturnValue);
(<HTMLInputElement>document.getElementById('search_places')).value = dbReturnValue;

It's ugly, but it works.

TrackFR commented 6 years ago

Hi @Webberjo, i need you'r help for that's code :

autoCompleteCallback(selectedData: any) {
  this.myForm.get('myControl').setValue(dbReturnValue);
  (<HTMLInputElement>document.getElementById('search_places')).value = dbReturnValue;
}

Where does the " myForm " & " ('myControl') " from ?

My form look like this and i really want to add the autocomplete in my firebase :

<form #f="ngForm" (ngSubmit)="save(f.value)">
     <input #adress="ngModel" [(ngModel)]="cavist.adress" name="adress" id="adress" type="text" class="form-control" required>
     <button [disabled]="!f.valid" class="btn btn-primary">Enregistrer</button>
     <button *ngIf="id" type="button" (click)="delete()" class="btn btn-danger">Supprimer</button>
</form>

Thank's a lot in advance.

Webberjo commented 6 years ago

@remyblancke myForm is the FormGroup that holds related FormControls, myControl is the name of the FormControl. Your example isn't using FormControls though.

With what you have, you might be able to bind the result of the callback to a backend variable or hidden input, something like...

<input type="hidden" value="{{selectedAddress}}"/>
<ng4geo-autocomplete (componentCallback)="autoCompleteCallback($event)"></ng4geo-autocomplete>
<button [disabled]="!selectedAddress" class="btn btn-primary">Enregistrer</button>
selectedAddress: string = '';
autoCompleteCallback(address: any) {
  this.selectedAddress = address.formatted_address;
}

I did say my solution was ugly...

TrackFR commented 6 years ago

Hum, try to run your solution but not work..

Look my component.ts, maybe i that he will inspire you

import { ActivatedRoute } from '@angular/router';
import { Router } from '@angular/router';
import { ProductService } from '../../services/product.service';
import { CategoryService } from '../../services/category.service';
import { Component, OnInit } from '@angular/core';
import 'rxjs/add/operator/take';

import { MapsAPILoader } from '@agm/core';
import {} from '@types/googlemaps';
import { ViewChild, ElementRef, NgZone } from '@angular/core';

import { CavistService } from '../../services/cavist.service';

@Component({
  selector: 'app-cavist-form',
  templateUrl: './cavist-form.component.html',
  styleUrls: ['./cavist-form.component.css']
})
export class CavistFormComponent implements OnInit {
  //categories$;
  //cavist = {};
  // @ViewChild('search') public searchElement: ElementRef

  id;

  //MOI pour coriger erreurs ng build --prod
  cavist = {
    "title" : "",
    "description" : "",
    "adress" : "",
    "zipcode" : "",
    "city" : "",
    "lat" : "",
    "lng" : "",
    "imageUrl" : ""
    };

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private mapsAPILoader: MapsAPILoader,
    private ngZone: NgZone,
    //private categoryService: CategoryService,
    private cavistService: CavistService) {
    //this.categories$ = categoryService.getAll();

    this.id = this.route.snapshot.paramMap.get('id');
    if (this.id) this.cavistService.get(this.id).take(1).subscribe(p => this.cavist = p);

  }

  // Autocomplete

  public componentData1: any = '';

  autoCompleteCallback1(data: any): any {
    this.componentData1 = JSON.stringify(data);
  }

  public userSettings5: any = {
    geoCountryRestriction: ['fr'],
    geoTypes: ['address'],
    inputPlaceholderText?: ['Veuillez saisir votre adresse']
  };

  save(cavist) {

    if (this.id) this.cavistService.update(this.id, cavist);
    else this.cavistService.create(cavist);

    this.router.navigate(['/admin/cavists']);
  }

  delete() {
    if (!confirm('Are you sure you want to delete this cavist?')) return;

    this.cavistService.delete(this.id);
    this.router.navigate(['/admin/cavists']);
  }

  ngOnInit() {

    // this.mapsAPILoader.load().then(
    //   () => {
    //     let autocomplete = new google.maps.places.Autocomplete(this.searchElement.nativeElement, {
    //       types: ['address'],
    //       componentRestrictions: {
    //         country: 'FR'
    //       }
    //     });
    //   }
    // );
  }

}

It's also ugly..

Webberjo commented 6 years ago

I suggest implementing it one step at a time and testing each step along the way. Add the component to your HTML and the callback function to your TS. Does it work? Yes - Work on getting the information you need from the callback parameter. No - Check the documentation to see if you installed and implemented it correctly.

TrackFR commented 6 years ago

Okay autocomplete work and parsing : look this screen Then, to inject autocomplete into my input ,I tried two solutions.

1

A first solution with the hidden input


autoCompleteCallback(address: any) {
  this.selectedAddress = address.formatted_address;
}

Not working.

2

A second solution ( ugliest )


autoCompleteCallback1(data: any): any {
  this.componentData1 = JSON.stringify(data);
  // dbReturnValue = whatever value was returned from the database query
  this.cavistService.get('ngForm').setValue(this.cavist);
  (<HTMLInputElement>document.getElementById('adress')).value = this.cavist;
}

Not working yet and i have error in console like this


ERROR in src/app/admin/cavist-form/cavist-form.component.ts(67,38): 
error TS2339: Property 'setValue' does not exist on type 'FirebaseObjectObservable<any>'.
src/app/admin/cavist-form/cavist-form.component.ts(68,5): 
error TS2322: Type '{ "title": string; "description": string; "adress": string; "zipcode": string; "city": string; "l...' is not assignable to type 'string'.
Webberjo commented 6 years ago

.get() is a FormGroup method and setValue() is a FormControl method. My solution is for Angular's FormBuilder. Your code isn't using FormBuilder, that's why you're getting those errors.

If you do

autoCompleteCallback(data: any) {
  console.log(data);
}

and open your developer tools, you will get the exact JSON structure of the returned data in your Console tab when you select a location. (like this) Go through it, find what you need and set them in your cavist object, like

autoCompleteCallback(data: any) {
  this.cavist = {
    city: data.city,
    lat: data.geometry.lat,
    lng: data.geometry.lng
    // and the rest
  };
}
TrackFR commented 6 years ago

I think we are getting closer to the goal. console.log gave me everything I needed but,

I tried this solution just for add address,


  autoCompleteCallback(data: any) {
    // console.log(data);
    // this.componentData1 = JSON.stringify(data);
    this.cavist = {
    title: "",
    description: "",
    imageUrl: "",
    adress: data.formatted_address,
    zipcode: "",
    city: "",
    lat: "",
    lng: ""
    };
  }

This code is interesting because when I select an address according to the autocomplete, all the inputs become empty, look this screen but give me error : formatted_adress undefined.

Before select address -> https://ibb.co/bQJrbS After select address -> https://ibb.co/gzbzVn

So tried something


  autoCompleteCallback(data: any) {
    console.log(data);
    // this.componentData1 = JSON.stringify(data);
    this.cavist = {
    title: this.cavist.title,
    description: this.cavist.description,
    imageUrl: this.cavist.imageUrl,
    adress: "",
    zipcode: "",
    city: "",
    lat: "",
    lng: ""
    };
  }

Return me good ( title, description, imageUrl ) from my cavist but I still can not get the data of the json.

For example this console.log give me "undefined".

console.log(data.formatted_address);

Also lat & lng gives me "undefined" when i try : data.geometry.lat.

I think the problem comes from the synthax of the data.

TrackFR commented 6 years ago

Okay find the solution

  autoCompleteCallback1(data: any) {
    console.log(data)
    this.cavist = {
    title: this.cavist.title,
    description: this.cavist.description,
    imageUrl: this.cavist.imageUrl,
    adress: data.data.name,
    zipcode: data.data.address_components[6].short_name,
    city: data.data.vicinity,
    lat: data.data.geometry.location.lat,
    lng: data.data.geometry.location.lng
    };
  }

Work fine but, just one problem, zipcode is not saved.

Webberjo commented 6 years ago

What happens if data.data.address_components doesn't have 7 elements in it? You'll get an error because it's trying to get information that's out of bounds.

fruits = ['apple', 'banana', 'orange'];
           0        1         2
console.log(fruits[6]); // undefined, fruits only goes to 2

Also, you are assuming that the postal code information is going to be in the exact same index of address_components every time. What if it's in address_components[5] or address_components[7]? It appears to always be in the last index of the array, so you can do

autoCompleteCallback1(data: any) {
  this.cavist = {
    ...
    zipcode: data.data.address_components[data.data.address_components.length - 1].short_name,
    ...
  }
}

this way it will always get the information from the last index, no matter how many items are in the array.

TrackFR commented 6 years ago

Sorry, all work's fine with :

  autoCompleteCallback(data: any) {
    console.log(data)
    this.cavist = {
    title: this.cavist.title,
    description: this.cavist.description,
    imageUrl: this.cavist.imageUrl,
    adress: data.data.name,
    zipcode: data.data.address_components[6].short_name,
    city: data.data.vicinity,
    lat: data.data.geometry.location.lat,
    lng: data.data.geometry.location.lng
    };
  }

Thank you for your precious help

Webberjo commented 6 years ago

Zip codes are specific to only the United States, every other country in the world uses a postal code.

As for setting the input's value, try adding this to the callback

(<HTMLInputElement>document.getElementById('search_places')).value = data.data.name;

If that doesn't work, I don't know how to set it.

TrackFR commented 6 years ago

@Webberjo All works fine with my last code !

An other question : Do you know how I could get the address of the cavist in the placeholder of ng4-geoautocomplete?

Like :


  public userSettings5: any = {
    inputPlaceholderText: this.cavist.adress
  };

This does not work unfortunately..

Webberjo commented 6 years ago

You are setting the placeholder text to the result of this.cavist.adress which is "" at the time of component initialization. Re-read the documentation for how to alter component settings after initialization.

Hint: It's below the list of component settings.

TrackFR commented 6 years ago

Hum okay it's something like that but I still do not understand how to recover cavist.adress

this.userSettings5['inputPlaceholderText'] = 'this.cavist.adress';
this.userSettings5 = Object.assign({},this.userSettings5);

This not work but that's the idea

askri2pac commented 6 years ago

i try these : autoCompleteCallback1(data: any) { console.log(data); } but console.log return undefined !

Webberjo commented 6 years ago

You probably forgot the $.

                                                               ↓
<ng4geo-autocomplete (componentCallback)="autoCompleteCallback($event)"></ng4geo-autocomplete>