I have a one to many relationship in the model of the application to relevantUsers. Now I want to iterate via the {{#each}} helper over those values. Which works.
content: function()
{
return this.get('controllers.application.model.relevantUsers');
}.property('controllers.application.model.relevantUsers'),
And when removing an item from the relevantUsers the view updates. But when adding a new relevantUser nothing happens. The user gets added to the data store, but the view does not update. Am I missing something?
This is how I create a new user
// Create new user
var relevantUser = this.store.createRecord('relevantUser', relevantUserData);
// And push it to remote
relevantUser.save();
In my application, I've done it like [CoffeeScript] :
video = self.store.createRecord 'video', id: #Id, title: #Title, thumbnailUrl: #ThumbnailUrl
#Formats.map (i) -> //Formats is simple JavaScript array.
format = self.store.createRecord 'format', itag: i.itag, quality: i.quality, resolution: i.resolution, type: i.type, url: i.url
video.get('formats').then (f) ->
f.pushObject format
The difference, is, that I use .pushObject() method here. As #fanta wrote in his comment, you have to get array that has relationship with model, so it will notify all observers.
More example code.
Related
One of the recurring problems i've been having with ionic 2 is it's storage service. I have successfully set and retrieved stored data. However, when i store something, it is inaccessible on other pages unless i refresh the page/application.
Example one: Editing a contact
I push to an edit contact page, make changes, then saveEdits. saveEdits successfully makes the change to the right contact but fails to update the contact list UNTIL the application is refreshed.
HTML:
<button (click)="saveEdits(newName, newPostCode)"ion-button round>Save Edits</button>
TypeScript:
saveEdits(newName, newPostCode){
console.log("saveid"+this.id);
this.name = newName; //saves property
this.postcode = newPostCode; //saves property
this.items[this.id] = {"id": this.id, "Name": newName, "PostCode": newPostCode};
this.storage.set('myStore',this.items);
//this.navCtrl.pop(ContactPage);
}
Example two: Accessing contacts on another page
On another page i iterate through contacts and display them in a radio alert box list. Again, the contacts are displayed successfully, but when I add a contact on the add contact page, the new contact does not appear on the radio alert box list.
addDests(){
console.log('adddests');
{
let alert = this.alertCtrl.create();
alert.setTitle('Choose Friend');
for(let i = 0; i<this.items.length; i++){
console.log('hello');
alert.addInput({
type: 'radio',
label: this.items[i].Name,
value: this.items[i].PostCode,
checked: false
});
}
alert.addButton('Cancel');
alert.addButton({
text: 'OK',
handler: data => {
console.log(data);
}
});
alert.present();
}
}
You're changing the reference the variable is pointing to:
this.items[this.id] = {"id": this.id, "Name": newName, "PostCode": newPostCode};
I assume that your LIST is iterating (ngFor) over the array referenced by this.items? If yes, update directly the properties of this.items[this.id] instead of re-initializing it.
this.items[this.id].Name = newName;
this.items[this.id].PostCode = newPostCode;
(By the way, I'd recommend to be consistent with your property naming: either Id and Name, or id and name (capital letters matter!)).
Your "list" view will always be refreshed if the references to the objects being used are not changed. The only exception would be an update made in a callback given to a third-part library. In that case, you can use NgZone to "force" Angular to take the update into account.
Also, have a look at Alexander's great advice about Observable.
You should use Angular provider(s) with Observable property to notify subscribers (other pages & components) about changes.
For example read this article: http://blog.angular-university.io/how-to-build-angular2-apps-using-rxjs-observable-data-services-pitfalls-to-avoid/
There are a lot of information on this: https://www.google.com/search?q=angular+provider+observable
When a Handsontable is instantiated, it calls a recursive method to build a data schema: https://github.com/handsontable/handsontable/blob/be8654f78ca84efc982047ca6b399e6c6d99f893/src/dataMap.js#L28, which in turns calls objectEach: https://github.com/handsontable/handsontable/blob/master/src/helpers/object.js#L235-L245
However, with an Ember Data record, it tries to iterate over properties like store, which means it gets caught in an endless loop.
Is there any way to bypass the recursiveDuckSchema method?
Unless Handsontable has some interface to allow pre-parsing data before it reaches the core of the plugin, I would say you may have better luck by transforming your ember-data models into something handsometable understands.
let queryParams = //Your query params
let data = this.get('getTheContent'); //Your models
let handsomeData = data.map(function(item, index, enumerable)){
return { id: item.get('id'), name: item.get('name'), other: item.get('other') }
};
// Result is [{id: 1, name: 'John', other: 'Other'}, {...}]
Using Ember-Data 0.13-59 & Ember 1.0.0 RC 6 (from starter kit)
Problem: upon save() to a new record made from App.Userstat.createRecord({ ... }) the server gets the POST and successfully returns an id but the new record is not available in the Userstat model.
To better understand example: this is a quiz app(for multiple choice questions). Each question has several choices and when a user selects a choice, their choice to the corresponding question is stored in a Model, App.Userstat.
At each question, the app needs to know whether the user has already answered this question or if it's new.
I use a computed property as a setter and getter. The setter is called when a user selects a choice (the choice's value is passed to computed property). First it checks if a record exists for the user's current question. If it doesn't exist it will create a new record. If it does exist, it should only issue a PUT request with updated content.
Code Updated(July 8, 11AM)
App.UserstatsController = Ember.ArrayController.extend();
App.QuestionController = Ember.ObjectController.extend({
needs: "userstats",
chosen = function(key, value) {
// getter
if(value === undefined) {
// something goes here
// setter
} else {
// the question.id is used to compare for an existing record in Userstat mdoel
var questionId = this.get('id');
var questionModel = this.get('model');
// does any Userstat record belong to the current question??
var stats = this.get('controllers.Userstats');
var stat = stats.get('model').findProperty('question.id', questionId);
// if no record exists for stat, means user has not answered this question yet...
if(!stat) {
newStat = App.Userstat.createRecord({
"question" : questionModel,
"choice" : value // value passed to the computed property
)}
newStat.save(); // I've tried this
// newStat.get('store').commit(); // and this
return value;
// if there is a record(stat) then we will just update the user's choice
} else {
stat.set('choice', value);
stat.get('store').commit();
return value;
}
}.property('controllers.Userstats')
No matter how many times I set chosen it always sends a POST (as opposed to an update only sending a PUT request) because it never adds the record to the model the first time.
To demonstrate further, in the setter part of the computed property, when I put this code:
var stats = this.get('controllers.Userstats')
console.log stats
the Userstats controller shows all previously existing records, but not newly submitted records!
How come the new record isn't available after I save() or commit() it???
Thanks :)
EDIT
maybe it has something to do with me adding a record to the singular model App.Userstat and then when I look for it, I'm searching using the UserstatsController which is an Array controller???
I don't know if it's a typo, but the computed property is defined the wrong way and should be like this:
App.QuestionController = Ember.ObjectController.extend({
needs: 'userstats',
choice: 'controllers.userstats.choice',
chosen: function(key, value) {
...
}.property('choice')
...
});
Inside the property() you should also define properties that trigger the computed property if they change. This way if choice changes the chosen cp will be triggered.
Please let me know if it helps.
I have a DS.Store which uses the DS.RESTAdapter and a ChatMessage object defined as such:
App.ChatMessage = DS.Model.extend({
contents: DS.attr('string'),
roomId: DS.attr('string')
});
Note that a chat message exists in a room (not shown for simplicity), so in my chat messages controller (which extends Ember.ArrayController) I only want to load messages for the room the user is currently in:
loadMessages: function(){
var room_id = App.getPath("current_room.id");
this.set("content", App.store.find(App.ChatMessage, {room_id: room_id});
}
This sets the content to a DS.AdapterPopulatedModelArray and my view happily displays all the returned chat messages in an {{#each}} block.
Now it comes to adding a new message, I have the following in the same controller:
postMessage: function(contents) {
var room_id = App.getPath("current_room.id");
App.store.createRecord(App.ChatMessage, {
contents: contents,
room_id: room_id
});
App.store.commit();
}
This initiates an ajax request to save the message on the server, all good so far, but it doesn't update the view. This pretty much makes sense as it's a filtered result and if I remove the room_id filter on App.store.find then it updates as expected.
Trying this.pushObject(message) with the message record returned from App.store.createRecord raises an error.
How do I manually add the item to the results? There doesn't seem to be a way as far as I can tell as both DS.AdapterPopulatedModelArray and DS.FilteredModelArray are immutable.
so couple of thoughts:
(reference: https://github.com/emberjs/data/issues/190)
how to listen for new records in the datastore
a normal Model.find()/findQuery() will return you an AdapterPopulatedModelArray, but that array will stand on its own... it wont know that anything new has been loaded into the database
a Model.find() with no params (or store.findAll()) will return you ALL records a FilteredModelArray, and ember-data will "register" it into a list, and any new records loaded into the database will be added to this array.
calling Model.filter(func) will give you back a FilteredModelArray, which is also registered with the store... and any new records in the store will cause ember-data to "updateModelArrays", meaning it will call your filter function with the new record, and if you return true, then it will stick it into your existing array.
SO WHAT I ENDED UP DOING: was immediately after creating the store, I call store.findAll(), which gives me back an array of all models for a type... and I attach that to the store... then anywhere else in the code, I can addArrayObservers to those lists.. something like:
App.MyModel = DS.Model.extend()
App.store = DS.Store.create()
App.store.allMyModels = App.store.findAll(App.MyModel)
//some other place in the app... a list controller perhaps
App.store.allMyModels.addArrayObserver({
arrayWillChange: function(arr, start, removeCount, addCount) {}
arrayDidChange: function(arr, start, removeCount, addCount) {}
})
how to push a model into one of those "immutable" arrays:
First to note: all Ember-Data Model instances (records) have a clientId property... which is a unique integer that identifies the model in the datastore cache whether or not it has a real server-id yet (example: right after doing a Model.createRecord).
so the AdapterPopulatedModelArray itself has a "content" property... which is an array of these clientId's... and when you iterate over the AdapterPopulatedModelArray, the iterator loops over these clientId's and hands you back the full model instances (records) that map to each clientId.
SO WHAT I HAVE DONE
(this doesn't mean it's "right"!) is to watch those findAll arrays, and push new clientId's into the content property of the AdapterPopulatedModelArray... SOMETHING LIKE:
arrayDidChange:function(arr, start, removeCount, addCount){
if (addCount == 0) {return;} //only care about adds right now... not removes...
arr.slice(start, start+addCount).forEach(function(item) {
//push clientId of this item into AdapterPopulatedModelArray content list
self.getPath('list.content').pushObject(item.get('clientId'));
});
}
what I can say is: "its working for me" :) will it break on the next ember-data update? totally possible
For those still struggling with this, you can get yourself a dynamic DS.FilteredArray instead of a static DS.AdapterPopulatedRecordArray by using the store.filter method. It takes 3 parameters: type, query and finally a filter callback.
loadMessages: function() {
var self = this,
room_id = App.getPath('current_room.id');
this.store.filter(App.ChatMessage, {room_id: room_id}, function (msg) {
return msg.get('roomId') === room_id;
})
// set content only after promise has resolved
.then(function (messages) {
self.set('content', messages);
});
}
You could also do this in the model hook without the extra clutter, because the model hook will accept a promise directly:
model: function() {
var self = this,
room_id = App.getPath("current_room.id");
return this.store.filter(App.ChatMessage, {room_id: room_id}, function (msg) {
return msg.get('roomId') === room_id;
});
}
My reading of the source (DS.Store.find) shows that what you'd actually be receiving in this instance is an AdapterPopulatedModelArray. A FilteredModelArray would auto-update as you create records. There are passing tests for this behaviour.
As of ember.data 1.13 store.filter was marked for removal, see the following ember blog post.
The feature was made available as a mixin. The GitHub page contains the following note
We recommend that you refactor away from using this addon. Below is a short guide for the three filter use scenarios and how to best refactor each.
Why? Simply put, it's far more performant (and not a memory leak) for you to manage filtering yourself via a specialized computed property tailored specifically for your needs
I'm having difficulty wrapping my head around the following:
There's a view that displays the list of items
I take the list of items from the backend via RESTful interface in JSON using ember-data and hand-crafted adapter
In my view I do something like this:
{{#collection contentBinding="App.recentAdditionsController"}}
...
{{/collection}}
App.recentAdditionsController is defined like this:
App.recentAdditionsController = Em.ArrayController.create({
refresh: function(query) {
var items = App.store.findAll(App.Item);
this.set('content', items);
}
});
And... this doesn't work. The reason being App.store.findAll() returning ModelArray which is much like ArrayController itself.
I saw people doing something like this:
App.recentAdditions = App.store.findAll(App.Item);
I could imagine doing it like that, but how would I refresh the list at will (checking if there's anything new).
Hope all is clear more or less.
I've verified that you can use a ModelArray inside an ArrayController. Here's a jsFiddle example: http://jsfiddle.net/ebryn/VkKX2/
"Now the question is how to make the list update itself if there are new objects in the backend?"
Use App.Model.filter to keep your recordArray in sync. Add the query hash when the filter is invoked to ensure than an initial query was made.
model: ->
App.Model.filter {page: 1}, (data) ->
data
edit: Just saw how old the question was, but leaving it here in case it helps someone.