I use ng-prime <p-autocomplete> for display values via search in the back-end
here is my html
<p-autoComplete [(ngModel)]="agent" [suggestions]="filteredAgents" name="agents" (completeMethod)="filterAgents($event)" [size]="10"
placeholder="Agents" [minLength]="3"></p-autoComplete>
At the component.ts I initialize array like this at start of the component
filteredAgents: string[] = [];
and I have a method to send query to back end and push it to array
filterAgents(event) {
let query = event.query;
this._agentsService.getAgentSearch(query).subscribe(result => {
result.items.forEach((value) => {
this.filteredAgents.push(value.name);
console.log(this.filteredAgents);
});
});
}
I see filtered value in console, but I don't see it in suggestions.
Where can be my problem?
AutoComplete either uses setter based checking or ngDoCheck to realize if the suggestions has changed to update the UI. This is configured using the immutable property, when enabled (default) setter based detection is utilized so your changes such as adding or removing a record should always create a new array reference instead of manipulating an existing array as Angular does not trigger setters if the reference does not change. ( Angular documentation )
Array.prototype.push doesnt create a new reference it rather mutates the original array. So you need to make a new one.
filterAgents(event) {
let query = event.query;
this._agentsService.getAgentSearch(query).subscribe(result => {
this.filteredAgents = [...result.items.map(e => e.name)]
});
}
I maped the result to extract the names.
If filtered agents is an object array try adding field="name" to the directive attributes.
Here name is a field in the object. The directive uses this field to display in suggestions
Related
I've been trying to figure out how to do this, but can't seem to get it to work. I have created a function that is being called when I click a component using v-on:click. I am also trying to pass in a value that I can then use to access a particular array that is coming in the form of data from a backend.
Here is the function in the component:
<v-card
#click="getContent('topSellers')"
>
I am then passing the 'topSellers' value as an "id" into the function that is being used to get access the exact array that I am looking for:
getContent(id) {
this.accordion = this.data.id;
console.log("data", id);
}
First of all, this.data.topSellers works. And I am seeing that the id value is correct in the console. Obviously, this is not working because id is currently a string, but I don't know how to fix that issue. Any help would be appreciated!
You need this.data[id] instead of this.data.id.
See property accessors on MDN for reference.
data[id] will access the property of data with the name of the value of id, in your case topSellers. So data.topSellers is equivalent to data["topSellers"]
[] allows you to access objects with variables.
It is recommended to handle exceptions because the variable received is not safe.
getContent(id) {
const accordion = this.data[id] || null;
if(accordion){
console.log("data", accordion);
this.accordion = accordion;
}
}
GOOD SOLUTION HERE
In the case below I am trying to get data from one location and then find the related data in a different location (the firebase joins).
I am able to retrieve the appropriate data and display in the my console but I kind of stuck when it comes to storing them in one of my properties to then loop over them with a dom-repeat template. In addition I am not entirely sure if this should be done with plane JavaScript or the PolymerFire components.
//Key value for a course that I retrieved from a URL Query
var ckey = KSH456YU789;
//Get the Videos that belong to that course key
firebase.database().ref('/courseVideos/' + ckey).on('child_added', snap => {
//Get the video data of each video that belongs to the course
let videoRef = firebase.database().ref('videos/' + snap.key);
videoRef.once('value', function(snapShot) {
this.set('courseVidObj', snapShot.val());
//console.log() the data works
console.log(this.courseVidObj);
}.bind(this));
});
As it can be seen above I am able to log the data that is stored in my property 'courseVidData' which is from type Array. However, this is run for each request which basically overwrites the previous stored value.
This makes it impossible to use my property inside a dom-repeat template. As shown below:
<template is="dom-repeat" items="[[courseVidData]]" as="vid">
<my-card
card-img="[[vid.img]]"
card-name="[[vid.title]]"
card-text="[[vid.description]]"
card-key="[[vid.$key]]">
</my-card>
</template>
Second Attempt
In my second attempt I used a forEach() to store the returned data insie an array which I then added to my 'courseVidData' property.
This returns me as expected an array with three objects. Unfortunately the dom-repeat is doing nothing.
firebase.database().ref('/courseVideos/' + ckey).once('value', function(snap) {
var vidData = [];
snap.forEach(function(childSnapshot) {
let videoRef = firebase.database().ref('videos/' + childSnapshot.key);
videoRef.once('value', function(snapShot) {
vidData.push(snapShot.val());
this.courseVidData = vidData;
console.log(this.courseVidData); //returns array with the object's
}.bind(this));
});
});
So I found a way of doing it after reading through the documentation of the Polymer Template repeater (dom-repeat) And the way Arrays are handled in Polymer.
This might be not the cleanest approach but it works for now. If somebody is pointing out what could be improved I am happy to change my answer or accept a different one.
//Retrieve course videos and their details
firebase.database().ref('/courseVideos/' + ckey).on('child_added', snap => {
let videoRef = firebase.database().ref('videos/' + snap.key);
videoRef.once('value', function(snapShot) {
if (!this.courseVidData) this.set('courseVidData', []);
this.push('courseVidData', {video: snapShot.val()});
}.bind(this));
});
I can't really explain why but I had to add a if statement to check for the array and set it if not existing. Then I place the value of my snapshot inside the 'courseVidData' Property which is declared inside my Properties and from type Array.
Because the key of each returned object is now 'video' it is necessary to use [[item.video.title]] to access the object values (code below).
<template is="dom-repeat" items="[[courseVidData]]">
<h1>[[index]]</h1>
<h1>[[item.video.title]]</h1>
</template>
Update
Although this method works the unique key Firebase creates gets lost in the array. To keep the key for each object I store both key and object inside another object and append this to my array.
I know this isn't pretty and as mentioned above I am still looking for a better solution. However, it does the trick for me as a bloody beginner.
firebase.database().ref('/courseVideos/' + ckey).on('child_added', snap => {
let videoRef = firebase.database().ref('videos/' + snap.key);
videoRef.once('value', function(snapShot) {
var vidKeyObj = {key:snapShot.key, value:snapShot.val()};
if (!this.courseVidData) this.set('courseVidData', []);
this.push('courseVidData', {video: vidKeyObj});
}.bind(this));
I have this JS Bin which is using the latest ember and demonstrates my issue. I have 2 lists. one is bound to the ArrayController 'content' and the other is bound do a property function called 'filtered' which filters out certain records. When You add a new record (use the add new link above the lists) you can see that the new record does make it into the content, but the filtered list does not update. What do I need to do so that the ArrayController will see that there is a new record in the list, and re-render the {{#each filtered}} block?
The dependent key of your computed property filtered is wrong, you need to use content.length, because filtered depends of some change in content property not the filtered itself:
filtered:function() {
return this.get("content").reduce(function (arr, object, index) {
if(object.get("id") != 2) {
arr.pushObject(object);
}
return arr;
}, Em.A());
}.property("content.length")
Updated jsbin http://jsbin.com/okAKAnU/1/edit
For UI purposes, when I load the array my viewModel is based on I add a new property to each object based on some other properties:
item.forEach(function (party) {
if (party.AcknowledgementDate() === null) {
party.Agreed = ko.observable(false);
}
else {
party.Agreed = ko.observable(true);
}
vm.Parties.push(party);
});
"Parties" is defined as ko.observableArray when the page starts.
The items in this array are edited in a separate UI window. When those changes are saved and the window closed, I call this function to update those values:
function updateAgreed() {
vm.Parties().forEach(function (i) {
if (i.AcknowledgementDate() === null) {
i.Agreed(false);
}
else {
i.Agreed(true);
}
});
}
This all works fine, and makes me very happy. The problem arrives when users create a new party item. We're using Breeze too, so we go off to the data service which requests entity framework create a new object of the appropriate type, then add an observable:
var lp = manager.createEntity('Party_dto'. { [an array of initial values] });
lp.Agreed = ko.observable('');
return lp;
Thanks to Breeze, this adds itself to the Parties observableArray because it's related to the same parent object. I can then call updateAgreed again to populate the Agreed observable with the appropriate value.
Logically, this work as expected - you can step through it and watch the Agreed observable of the new item be added and populated with the expected values. The problem comes in the UI - it doesn't update as having changed. Yet running the same code against an already-loaded object does cause the UI to update.
I'm stumped by this. I can't replicate it in Fiddle because we create objects in Breeze and not on the fly - and making a mock version without Breeze works perfectly. Why do my observables update on already loaded objects, but the same observable not update on a new object?
There are a few things that I see that need to be addressed. One, since you are using Breeze, take advantage of the model constructors and initializers. Wherever you are defining properties for your models, add the following code -
metadataStore.registerEntityTypeCtor(
'Party', null, partyinitializer);
function partyinitializer(party) {
party.Agreed = ko.observable(false);
}
Now all of your party entities have an agreed property that you can access. Next, make sure you aren't setting the Party's parent navigation property in the createEntity method, as that will break your binding.
var lp = manager.createEntity('Party'. { [an array of initial values] });
lp.parentParty(something); // Set the parent here
return lp;
This will make sure that before the party is bound back to the parent and shown in the view, all of the properties will be set. Then when you set the navigation property, it will show up in your view all happy-like.
I'm currently having problems having the UI refresh when I'm getting new data from the server for a single item which is in an observableArray of wrapper objects which holds an object of several observables.
Consider the following:
var vm = {
....
localEdited: ko.mapping.fromJS(new ItemWrapper(defaultModelSerialised)),
selected: ko.observable(null),
editItem: function(data) {
// clone a temporary copy of data for the dialog when opening (*.localEdited on dialog)
var clonedData = ko.toJS(data);
ko.mapping.fromJS(clonedData, null, this.localEdited);
// selected should now point to the item in the obserable array which will be refreshed
this.selected(data);
// open dialog...
},
submitDialog: function(data) {
// submit data to server...
// (1) commit the data back to UI (new item is return in resp.entity from server)
vm.selected(new ItemWrapper(resp.entity));
// at this point the UI isn't showing the updated value
// (2) however if I do this it reflects the data change in the UI
this.selected().Name("changed"); // updates the UI.
}
Can someone explain why passing in the ItemWrapper into vm.selected isn't updating the UI whereas in (2) it works. I don't want to have to set-up each property like in (2) for every property.
ItemWrapper looks like so:
function PoolWrapper(pool) {
this.Name = ko.observable(pool.Name);
// more properties...
}
OK- the issue is that your clones end up with mapping meta-data on them and eventually this causes recursion when trying calling ko.mapping.fromJS.
The solution is to create your clones using ko.mapping.toJS instead of ko.toJS, so that you get a clean clone (without mapping meta-data).
Here is an updated fiddle: http://jsfiddle.net/rniemeyer/tDDBp/
Something I also stumbled upon today that I thought I'd share:
If you clone using:
var clone = ko.mapping.fromJS(ko.mapping.toJS(itemToClone));
Then the clone will be stripped of any computed observables. They will exist as the last value of the function, but no longer function as a computed observable.
If your item is a complex model with computed observables that you would like to keep on your clone you can do the following:
var clone = ko.mapping.fromJS(ko.mapping.toJS(itemToClone), null, new itemModel());
Where itemModel is your complex model for your item containing your computed observables.