I am grabbing some items through a http call and then want to pre-populate the ngselect, but using the abstractcontrol.setvalue() method does not seem to work.
Template Code
<ng-select [items]="cars"
bindValue="code"
bindLabel="displayName"
formControlName="car"
[clearable]="false"
[searchable]="false"
id="car"
placeholder="Select a car">
Component Code
this.setValueForPrePopulatedPlanningDetail('car', car);
private setDefaultValue(fieldName: string, value: any) {
if (value && value.length > 0) {
const field = this.myFormGroup.get(fieldName);
field.markAsDirty();
field.setValue(value);
}
}
You need set your result to the list (countries) and then use setvalue to choose the option
Set result to your list in your component:
this.countries = ['XPTO','XPTO2','XPTO3']
Define the following in your html:
<select>
<option [value]="country" *ngFor="let country of countries"> {{country}}</option>
</select>
The right way of using ng-select, would be to assign the values to the items input binding.
For instance,
<ng-select [items]="cities2"
bindLabel="name"
bindValue="id"
[multiple]="true"
placeholder="Select cities"
[(ngModel)]="selectedCityIds">
</ng-select>
And on your component.ts, you will populate ng-select options by subscribing to the observable returned by the HTTP request, and assigning it the cities2 property.
cities2: any[] = [];
ngOnInit() {
this.dataService.getData.subscribe(res => {
this.cities2 = res;
});
}
This demo might not directly answer your queries, but it shows how the various input bindings (such as items) work with ng-select.
Related
In my Angular 8 component, I have added bi-directional binding to a dropdown control.
The view
<select (ngModelChange)='termSelectChanged($event)' [ngModel]="selected">
<option [ngValue]="t" *ngFor='let t of termsColl'>{{t?.code}}</option>
</select>
The component code
export class AppComponent implements OnInit{
public termsColl : Array<DDModel>;
public selected : DDModel;
constructor( private s : DDService ){}
termSelectChanged( event ){
alert('HIT');
}
ngOnInit(){
//service call #1
this.s.getDataForComplexBind().subscribe( data => {
this.termsColl = data;
}, error => error );
//service call #2
this.s.getOtherData( ).subscribe( data => {
//model changes here
this.selected = this.termsColl[1];
}, error => { console.error(error); });
}
}
When the component loads, it executes ngOnInit() and sets the model-bound property Selected with the first element of the array termsColl. termsColl has data but the line this.selected = this.termsColl[1]; does not change the selected option to the first element in the dropdown. In fact, when the component loads, I was expecting it to fire the event ngModelChange but it did NOT fire the event either. I have added an alert() in the code but it did not show when the component loads. It shows only if I select an option from the dropdown. How do I change the code so it will execute the ngModelChange event when the component loads?
Here is my stackblitz
https://stackblitz.com/edit/angular-version-yeg27j?file=src%2Fapp%2Fapp.component.ts
I changed the ngModeChange input to DDModel and called your termSelectChanged() from inside the subscribe. I also changed [ngModel] to [(ngModel)]
<select (ngModelChange)='termSelectChanged($event)' [(ngModel)]="selected">
<option [ngValue]="t" *ngFor='let t of termsColl'>{{t?.code}}</option>
</select>
termSelectChanged(selection: DDModel) {
console.log("HIT", selection);
}
ngOnInit() {
this.s.getOtherData().subscribe(
data => {
this.termsColl = data;
this.selected = this.termsColl[0];
this.termSelectChanged(this.selected);
},
error => {
console.error(error);
}
);
}
I can't tell you why changing this.selected from code does not trigger the ngModelChange. Maybe it's because ngModelChange is called in the template.
You can use viewToModelUpdate() of ngModel to update the value and if you want to trigger the ngModelChange. You can find more about it here.
But you need to do the following changes.
In html template:
<select (ngModelChange)='termSelectChanged($event)' [ngModel]="selected" #ngModel="ngModel">
<option [value]="t" *ngFor='let t of termsColl'>{{t?.code}}</option>
</select>
You can see I am adding a reference to the ngModel in template, which I will use it in the component class.
In the component class:
export class AppComponent {
name = "Angular 6";
version = VERSION.full;
public termsColl: Array<DDModel>;
public selected: string;
#ViewChild("ngModel") ngModel: NgModel;
constructor(private s: DDService) {}
termSelectChanged(event) {
this.selected = event;
}
ngOnInit() {
this.s.getOtherData().subscribe(
data => {
this.termsColl = data;
this.ngModel.viewToModelUpdate(this.termsColl[1]);
},
error => {
console.error(error);
}
);
}
}
You can see I am using the ngModel reference to call the viewToModelUpdate with the value, which in return triggers the ngModelChange.
Since you are not using two way binding directly, you have to set the value to the selected variable inside the trigger function termSelectChanged.
Hope this would help you to achieve your requirement.
You can use value instead of ngValue in you option elements. And assign t.code instead of t to the value attribute.
<select (ngModelChange)='termSelectChanged($event)' [ngModel]="selected">
<option [value]="t.code" *ngFor='let t of termsColl'>{{t?.code}}</option>
</select>
Reference: https://angular-version-xkjuff.stackblitz.io
termsColl has data but the code line this.selected = this.termsColl[1]; does not change the selected option to the first element in the drop down.
Because you are using propery binding [ngModel]="selected", not two-way data binding.
[(ngModel)]="selected" is for two-way binding, and the syntax is compound from:
[ngModel]="selected" and (ngModelChange)="selected = $event"
Also, the value of selected should be the value which is available in dropdown, i.e
ngOnInit(){
this.selected = this.termsColl[1].code
}
Below code would work for you:
<select [(ngModel)]="selected" (change)="onSelection()">
<option *ngFor='let t of termsColl' [value]="t.code" [selected]="selected === t.code">{{t?.code}}</option>
</select>
You might also want to refer this https://stackoverflow.com/a/63192748/13596406
[For some reason the selected select option not showing any value just a blank area only when clicking it it shows the values. ive checked the css files and it seems fine no idea what causes the problem when i remove the [(ngModel)] it works but not getting the values =/
import { Component, OnInit } from '#angular/core';
import { Company } from '../_models/company';
import { CompanyService } from '../_services/company.service';
import { AlertifyService } from '../_services/alertify.service';
#Component({
selector: 'app-company',
templateUrl: './companies.component.html',
styleUrls: ['./companies.component.css']
})
export class CompaniesComponent implements OnInit {
selectedCompany: Company;
companies: Company[];
constructor(private companyService: CompanyService, private alertify: AlertifyService) { }
ngOnInit() {
this.loadCompanies();
}
async loadCompanies() {
this.companyService.getCompanies().subscribe((companies: Company[]) => {
this.companies = companies;
}, error => {
this.alertify.error(error);
});
}
// selectedChangeHandler(event: any) {
// this.selectedCompany = event.target.value;
// }
}
<ng-container *ngIf="companies">
<div class="col-12 col-md-3 col-xl-2 mt-5 bd-sidebar">
<label for="">Select Company</label>
<select class="form-control" [(ngModel)]="selectedCompany" >
<option selected> -- select an option -- </option>
<option *ngFor="let value of companies" [ngValue]="value">{{value.name}}</option>
</select>
</div>
</ng-container>
<!--Just a test--->
<!-- <select class="form-control col-lg-8" #selectedValue name="selectedValue" id="selectedValue" [(ngModel)]="company" (ngModelChange)="assignCorporationToManage($event)">
<option *ngFor="let value of companies" [ngValue]="company">{{value.name}}</option>
</select> -->
<ul *ngIf="selectedCompany" class="list-group list-group-flush">
<li class="list-group-item">Company name: {{selectedCompany.name}}</li>
<li class="list-group-item">Company address: {{selectedCompany.address}}</li>
<li class="list-group-item">Company estimated revenue: {{selectedCompany.estimatedRevenue}}₪</li>
</ul>
I see several issues with your example, so I am going to offer another approach, and explain what I am doing along the way.
In an Angular application, when using two way binding with [(ngModel)] on a select, the initially selected option will always be set to the one that matches the value of ngModel.
In your example, the initial value for selectedCompany is never set, and that is the reason your initial page load displays the menu with nothing selected. You are going to need to set a value onto all of your options, including the first to get this to work.
Now, since you did not provide the structure of your model titled Company, I am going to improvise and assume it contains two elements, name and value. So just remember, you will need to adjust what I have below to match the actual structure of your data.
First, on the option tags in your select in the template, id suggest using the value attribute which we will be populating with strings, instead of ngValue which is can be used to contain an object or a string. You can obtain the object that contains all of the data you need via the change event later.
Let's adjust your template file as follows:
<select class="form-control" [(ngModel)]="selectedCompany" (change)="companyChange($event.target.value)">
<option [value]="'init'">-- select a company --</option>
<option *ngFor="let company of companies" [value]="company.value">{{company.name}}</option>
</select>
Next, in your component file, lets change that property that we're using for two way binding on your select to be typed as a string:
selectedCompany:string;
Next, lets create a new property typed to your model which we will ultimately set to the company selected:
myCompany:Company;
Next, set the initially selected option in your component so that '-- select a company --' will display as the initially selected option:
ngOnInit() {
this.selectedCompany = 'init';
this.loadCompanies();
}
And lastly, inside the change event, you can use the find() method to obtain the complete set of data that you need:
companyChange(value) {
this.myCompany = this.companies.find(element => element.value === value);
console.log("User selected the company:", this.myCompany);
}
In this case I'm not using ngModel directives.
I want to know how to get the full object binded to the drop down when it change.
Basically, this is what I'm doing:
It's an array of objects with properties, but i don't want only the
value, i want the full object selected
<select #tipoTextoItem (change)="handleChange($item)" name="cmbTipoTexto" class="form-control form-control-sm col-sm-7" id="cmbTipoTexto" formControlName="cmbTipoTexto">
<option *ngFor="let item of textTypes" value="{{item.key}} {{item.value}}
</option>
</select>
Then...
handleChange($event) {
console.log($event.ForExampleGetMyObjectInThisWay());
}
You can use compareWith input property provided by angular. In the function you can compare and return the object. You can use it like:-
<select #tipoTextoItem (change)="handleChange($item)"
[compareWith]="compareFn"
name="cmbTipoTexto"
class="form-control form-control-sm col-sm-7"
id="cmbTipoTexto"
formControlName="cmbTipoTexto">
<option *ngFor="let item of textTypes"
value="{{item}}">{{item.value}}</option>
</select>
In your component.ts file,
compareFn(item1, item2) {
return item1 && item2 ? item1.value === item2.value : item1 === item1;
}
You can look into below link for more info:-
https://angular.io/api/forms/SelectControlValueAccessor#customizing-option-selection
you can use selectedIndex, Here you go :
(change)="handleChange($event.target.selectedIndex)" // change method
handleChange(index) {
console.log(this.textTypes[index]);
}
WORKING DEMO
I have a Angular5 <select> bound to array of customers. See below:
<select class="form-control" [ngModel]="record.customer_id" (ngModelChange)="setCustomer($event)" name="customer_id">
<option *ngFor="let x of customers" [ngValue]="x.id">{{x.name}}</option>
</select>
In setCustomer function I get an customer's id as 'event'.
Property record.customer_id is type of number, not object. Is there any way how to get a whole customer entity in setCustomer method and also preserve binding to record.customer_id ?
I found on Angular docu a way [compareWith] so I tried:
<select class="form-control" [compareWith]="compareCustomer" [ngModel]="record.customer_id" (ngModelChange)="setCustomer($event)" name="customer_id">
<option *ngFor="let x of customers" [ngValue]="x">{{x.name}}</option>
</select>
and
compareCustomer(c1: customer, c2: number) : boolean {
if (c1 == null || c1 == undefined) {
return false;
}
if (c1.id == c2) {
return true;
}
return false;
}
Does not work. When I select any option, setCustomer is executed, record.customer_id gets selected id. However, after select loses focus, selected option is reset to blank.
There is a workaround (iteration in customers array and manual match by id) that I want to avoid:
setCustomer(event) {
this.record.customer_id = Number.parseInt(event);
customers.forEach(c => {
if (c.id === this.record.customer_id) {
// some logic with selected customer
}
});
}
Any advice?
Thanks!
Instead of bind customer_id, bind the whole object:
<select class="form-control" [ngModel]="record" (ngModelChange)="setCustomer($event)" name="customer_id">
<option *ngFor="let x of customers" [ngValue]="x">{{x.name}}</option>
</select>
So I have the following code:
<div class="form-group">
<label for="backings_select">Backing Single</label>
<select class="form-control"
required
[(ngModel)]="selectedBacking"
name="backings_select"
(ngModelChange)="storeValueRedux($event, count)">
<option *ngFor="let backing of backings" [ngValue]="backing.id" [selected]="backings.length === 1">{{backing.name}}</option>
</select>
It populates a select box with results from a service call, if the array length is 1, it auto selects the only option available, this works fine.
However, by default the select box uses a value from the component as its default value.
So when the service call is made, if the array only has a length of one, the value of the model is changing, but because its being auto selected (not by user input) the storeValueRedux event is not firing.
However, if the array has more than one entry, and then is selected by a user, the function is called and works as required. Is there anyway to trigger ngModelChange in the instance that backings.length = 1?
You can't use a condition inside your method calls in HTML but you can use change and handle the condition inside your method as below
<select class="form-control"
required
[(ngModel)]="selectedBacking"
name="backings_select"
(change)="storeValueRedux($event, count)">
<option *ngFor="let backing of backings" [ngValue]="backing.id"
[selected]="backings.length === 1">{{backing.name}}</option>
selectedBacking:any{};
backings:any[]=[
{id:1, name:'a'},
{id:2, name:'a'}
]
storeValueRedux(a,b){
if(this.backings.length!=1){
console.log(this.selectedBacking);
console.log(a,b);
}
}
LIVEDEMO
The service that returned my backings was an observable, so I modified the subscribe from:
.subscribe(
results => {this.backings = results},
error => console.log(error),
);
to:
.subscribe(
results => {this.backings = results, this.testBackingLength()},
error => console.log(error),
);
And then added:
testBackingLength(){
/* If the length of the arrau is only one, the template auto selects it
and does not trigger ngOnChange, so we need to manually trigger it here,
this function is called from the subscribe a few lines up */
if (this.backings.length === 1) {
this.storeValueRedux(this.backings[0]['id'], this.count)}
}
So each time my service is called, it tests the length of the array. If the length of the array is 1, it will auto call my function.