What is the difference between valueChanges and snapshotChanges() - html

this is my component.html
<div class="form-group">
<label for="category">Category</label>
<select ngModel name="category" id="category" class="form-control">
<option value=""></option>
<option *ngFor="let c of categories$ | async" [value]="c.key$">
{{ c.name }}
</option>
</select>
</div>
This is my Component.ts
categories$: Observable<any[]>;
constructor(categoryService: CategoryService) {
this.categories$ = categoryService.getCategories();
}
save(product){
console.log(product);
}
This is my categoty.service.ts
I used both valueChanges() and snapShotChanges(). In using valueChanges i can access c.name but cannot c.key
And using snapshotChanges() i can access c.key but cannot access c.name
getCategories(){
//return this.db.list('/categories/').valueChanges();
return this.db.list('/categories').snapshotChanges();`
}
I need to access c.name and c.key$ in the same time Need a solution for this

valueChanges():
Use it when you just need the object data. No document metadata is attached which makes it simple to render to a view.
snapshotChanges():
When you need the document data but also want to keep around metadata. This metadata provides you the underyling DocumentReference and document id. Having the document's id around makes it easier to use data manipulation methods. This method gives you more horsepower with other Angular integrations such as ngrx, forms, and animations due to the type property. The type property on each DocumentChangeAction is useful for ngrx reducers, form states, and animation states.
Basically snapshotChanges() will give you access to the document id compared to valueChanges()
https://github.com/angular/angularfire/blob/master/docs/firestore/documents.md

By adding .pipe(tap(categories => console.log(categories)); to .snapshotChanges() you get a good look on what the data you're working with is looking like. It's a collection of objects that look like below
0: {
payload: DataSnapshot,
type: "value",
prevKey: null,
key: "frock"
},
1: {
payload: DataSnapshot,
type: "value",
prevKey: "frock",
key: "saree"
},
As you can see, there's no name property in your data, so you can't use that. You must instead use one of the other values that the data provides, such as key or prevKey.
On another note, I don't think it's a good idea to use snapshotChanges to map data to a select dropdown. I'm sure there are better ways in Firestore to retrieve the values.

Related

Pre populated select in Angular 6 from API data

I populating a select dropdown in Angular 6 from data returned from a resolver. Im subscribing to this.route.data and storing the data 'teamMembers' in an array.
Then i iterate over the array in my template with:
<div class="form-group" [ngClass]="{'highlighted' : messageForm.controls.MessageTo.disabled === false}" required>
<label>To</label>
<select class="form-control" formControlName="MessageTo" required>
<option [ngValue]="member" *ngFor="let member of teamMembers">{{ member.User.EmailID }}</option>
</select>
</div>
Im prepopulating the select with data returned from API with:
this.messageForm = this.fb.group({
MessageTo: [{value: this.thisMessage.ToUser.EmailID, disabled: false}]
});
I can see the data in the select dropdown in the browser, BUT ONLY once i click on the select. If I dont click on the select then it just shows blank as though nothing is in the select???
I want it to show the prepopulated Email set in the component.ts file.
Any help greatly appreciated.
Ah, sorted it. I added a new property of "originator" which was equal to the value of my default teamMemberGuid, to my class, and bound the value of that via [(ngModel)]="originator" on my select. Works a treat

Setting default selected value in select dropdown Vue.Js

I have a problem getting the default selected value using select in VueJs. I have tried in two instances:
Passing id and v-model fields in the select as:
<select v-model="sort_brand" id="sort-brand" class="form-control">
<option value="all" selected="selected">All(Brands)</option>
<option v-for="brand in brands" :value="brand.id">{{ brand.name }}</option>
</select>
The selected default value is empty in this case.
Passing sort_brand without id to select:
<select id="brand-id" class="form-control">
<option value="all" selected="selected">All(Brands)</option>
<option v-for="brand in brands" :value="brand.id">
{{ brand.name }}
</option>
</select>
The default selected value is populated but then i don't have the form binding for VueJs. Anyone please assist with this:
Vue.js will manipulate the selected attribute based on the v-model of the select and the option value. Thus trying to set the selected attribute will not work.
For your "All" option, you could assign the null value like this:
<option :value="null">All(Brands)</option>
And then set your sort_brand variable to null. Then vue.js will match the null sort_brand to the option with the null value.
Note: I used null here because that's the way I usualy do it, but I could use any other value. You could use the 'all' string also.
Update sort_brand to all in created hook which will set the option will value 'all' as default:
created (){
this.sort_brand = 'all';
}
You can also initialize the model in data itself.
data (){
return {
sort_brand : 'all'
}
}
The answers above (e.g. #Lunfel) work if your selection values are simple (null, "All", etc). If your combo is bound to complex JSON objects, then it's very difficult to set the bound variable and let it auto-select.
I found it much easier to find the option I wanted, based on the display text, in the mounted function() using JQuery, and then set the 'selected' attribute. How you do this depends on your version of JQuery. In recent versions (not sure when it changed, I'm running 1.10.2), you can use something like:
$("#sort-brand option").filter(function () {
return $(this).prop("label") == 'All';
}).prop("selected", true);
Older versions of JQuery don't have the prop() function, but had a somewhat better selector for the find() function. Try:
$('#sort-brand').find('option[text="All"]').attr('selected', 'selected');
See How to select an option by its text? for more discussion on selecting dropdown options with jQuery.

Load large data in select tag in Angular 4

I have a select tag with thousands of options (names of cities), which the user can choose between as birth city, and is given by a service.
service file
getAllCities () {
return this.httpClient.get<City[]>(this.API_URL + 'cities');
}
In html component file:
<select
class="form-control"
id="city"
name="city"
ngModel
>
<option disabled selected value=""> Select city</option>
<option *ngFor="let elt of cities" [ngValue]="elt.code_city">
{{ elt.city_name }}
</option>
</select>
In component.ts file
getCities(){
this.shareService.getAllCities()
.subscribe(
(response: Array<City>) => {
console.log(response);
this.cities = response;
},
(error) => console.log('Error getting cities: ' + error)
);
}
The problem is that my application block? and does not load the cities, have you any idea how I can do that in angular 4 (which method I must take to prevent this behavior)
thanks
I would recommend to use AutoComplete input for such cases.
You can use one provided by NgPrime for Angular applications.
This is easy to use.
https://www.primefaces.org/primeng/#/autocomplete
Limit your data binding to DOM. Binding too much data to single DOM may block your Application and your user may have to scroll too much to search his city. Use select with search(ng-select) and use limitTo pipe for limited data binding.

Setting selected option of select control in an Angular 2 model-driven form

I have researched many similar existing answers on SO and elsewhere, but just can't find the solution to this.
I'm using the model-driven approach in Angular 2 to build my form, which is both an add and edit form. When in edit mode, the values are populated with data retrieved from a service: this aspect is all fine because the simple text inputs all bind correctly.
One of the properties is 'Country' and this is an object as follows:
export class Country {id: number; name: string;}
I want to bind this to a select control which will have the list of countries available, and the one from the model populated when the form loads. I want the value of the binding to be the country object, not just the id.
Here's the html of the select control:
<select class="form-control" id="country" formControlName="country">
<option value="default">--Select a country--</option>
<option *ngFor="let c of countries" [value]="c">{{c.name}} </option>
</select>
And here is where i try to to populate the value from the component class:
(<FormControl>this.personForm.controls['country'])
.setValue(this.person.country, { onlySelf: true });
But there is no selected option when the page loads, even though the console confirms that this.person.country exists and is populated with the correct object.
I can get it working with ids: changing to [value]="c.id" in the view and appending .id in the class, and then it works in that the right option is selected. The problem is that the select no longer emits an object for the country property, just the id. I tried changing [value] to [ngValue] and get the same result. I even added [ngModel]="country" to the select element and that didn't help either.
I'd be grateful for any help.
The issue is most likely that this.person.country is not the same country as in your countries array.
If we want to make them the same we can either explicitly subscribe to the valueChanges of the select control or bind [(ngModel)] to person.country:
subscribe to changes
code
this.countryForm.controls['country'].valueChanges.subscribe(country =>
this.person.country = country;
);
// initialize by finding the correct country object (this will overwrite the person's country object)
this.countryForm.controls['country'].setValue(countries.filter(c => c.id === person.country.id));
template
ngModel bind
We still have to make the objects match (compare strategy that Angular 2 uses which is really what JS uses)
code
this.person.country = this.countries.filter(c => c.id === this.person.country.id)[0];
template
<select class="form-control" id="country" formControlName="country" [(ngModel)]="person.country">
<option value="default">--Select a country--</option>
<option *ngFor="let c of countries" [ngValue]="c">{{c.name}}</option>
</select>
ngModel Plunker: http://plnkr.co/edit/UIS2V5rKh77n4JsjZtii?p=preview
subscription Plunker: http://plnkr.co/edit/yyZ6ol1NPD77nyuzwS2t?p=info

Accessing Object properties, from HTML Select.

Scenario:
I am trying to access a property (code) of a ng-Model object (myRide).
I've tried doing this by
<select ng-model = "myRide"
ng-change = "getCode(myRide.code)">
...and at getCode,
alert(parameter) //which should be myRide.code.
I've also tried to do this by
<select ng-model = "myRide"
ng-change = getCode(myRide)
(Note: 'myRide' is passed, not 'myRide.code') ...and at getCode,
alert(myRide.code).
myRide does indeed contain a property called 'code', which is not undefined.
Problem: Both tries do not produce the wanted outcome.
How can I make it display the property (code)?
Here is the JSFiddle: http://jsfiddle.net/a2J6z/1/
The better way to do this is to restructure the view. Instead of using ng-repeat on options inside of a select, use the ng-options directive. Then, you can bind the actual object to the model instead of just a JSON string, which is what your current fiddle is doing.
Your new select looks like
<select ng-options="car.Name for car in myGarage" ng-model="myRide" ng-change="getCode(myRide)">
</select>
Then in your controller
$scope.getCode = function(car){
alert(car.code);
}
An updated fiddle:
http://jsfiddle.net/a2J6z/5/
I updated the fiddle:
http://jsfiddle.net/a2J6z/3/
var ngApp = angular.module('ngAppl',[]);
function aControlla($scope){
$scope.myRide = "bus";
$scope.getCode = function(car){
alert(JSON.parse(car).code);
}
$scope.myGarage = [
{Name: "Toyota 86", code:"1"},
{Name: "Hyundai Genesis Coupe", code:"2"},
{Name: "Nissan GTR", code:"3"},
{Name: "Veyron", code:"4"}
];
};
And
<div ng-app="ngAppl">
<div ng-controller="aControlla">
<select ng-model="myRide" ng-change="getCode(myRide)">
<option ng-repeat="car in myGarage">{{car}}</option>
<!--Note: value of myRide MUST be simply 'car' (not say, 'car.Code')-->
</select>
<br/> This shows that 'myRide' contains the property called 'Name', and importantly, 'code':<br/> {{myRide}}
</div>
</div>
Basically I just had it alert what car was with myRide as the parameter, it showed the JSON string so I added the parse to get it to give me the code. There may be a better way I'm an AngularJS noob so to speak.