Ember.js with EmberFire Object - How to use with array attributes? - javascript

I read a lot of, still can't solve by myself. I've an Ember Route, with az EmberFire model.
App.NewRoute = Ember.Route.extend({
model: function() {
return EmberFire.Object.create({
ref: window.ref
});
},
setupController: function(controller, model) {
controller.set('model', model);
})
After that for example I've in the controller:
App.NewController = Ember.Controller.extend({
actions: {
save: function() {
this.get('model').set('questions', Ember.A([]));
this.get('model').get('questions').pushObject(Ember.Object.create({title: 'foo'}));
this.get('model').get('questions').get('lastObject').set('title', 'bar');
this.get('model').set('title', 'foobar');
}
}
});
When the save action called only the title change is made on fire base.
Can someone please explain how can I changed the array too?

I would change your model from an EmberFire.Object.create to an EmberFire.Array.create. Instead of having an object with an array as a property. EmberFire is a simple wrapper that bridges ember objects to firebase objects. I don't think it works with nested arrays inside an object yet as I just started working with the library.
If you need a property identified to that array in an object, create a second object EmberFire.Object and add a property questions that references this EmberFire.Array firebase object instead.
So your save action would look more like this:
var array = this.get('model');
array.pushObject(Ember.Object.create({title: 'foo'}));
array.get('lastObject').set('title', 'bar');

Related

Backbone.js removing the model

I have been trying for hours now, without luck. If you could see on the image, I have mildly complex model. Image is taken from Chrome in debug.
I need to delete a model from collection, also I need to be able to change the URL where the backbone will shoot its ajax for delete. So in essence, this is my model structure:
attributes:
favorites {
bookmarkedArticles: [{id: 123123},{id: ...}],
bookedmarkedSearches: [{}],
dispatchesMailds: []
}
How can I delete model in bookmarkedArticles with id of 123123?
I have tried this:
var model = new metaModel(
{
favourites: {
bookmarkedArticles: {
id: "123123"
}
}
}
);
model.destroy();
also this
aamodel.headerData.collection.remove(model);
No success at all.
The information provided is not giving a lot of details, but I will try to answer considering two scenarios:
Option A:
You are trying to delete a model in the collection that has bookmarkedArticle.id="123123". if that is the case and considering the bookmarkedArticles it is just an Array of objects, I would suggest to filter the Collection using the underscore method filter and then delete the models returned by the filter.
var id = 123123;
var modelsToDelete = aamodel.headerData.collection.filter(function(model){
// find in the bookmarked articles
return _.find(model.get('bookmarkedArticles'), function(ba){
return (ba.id === id);
});
});
_.each(modelsToDelete, function(model){
model.destroy();
});
Option 2: If you want to remove the bookmarked article '123123' associated to your main model using just the 'destroy' method, firstable you have to convert 'bookmarkedArticles' to a Backbone.Collection as it is just an Array of Objects, there are some utilities for Backbone that allows you to do this easily:
https://github.com/blittle/backbone-nested-models
But by default this is not possible, then, If you want to remove the 'bookmarkedArticle' you can create the Backbone.Model and then use the method destroy. Example:
var BookmarkedArticle = Backbone.Model.extend({
url: function(){
return '/bookmarkArticle/' + this.id;
}
});
new BookmarkedArticle({"id": "123123","master": "5",...}).destroy();
Hope this information is useful and provide some guidance to solve your problem.

Ember route or controller expose data model as json

I am trying to work with Ember.js
Can I expose my data model as JSON through a route or controller?
I have an object like this saved in the store:
this.store.createRecord('Person', {
id: 1,
name: this.get('name'),
email: this.get('email')
});
I want to expose this data from a route or controller as JSON object. I don't want to use any view.
Is it possible to do this?
Thanks for help!
EDIT
My route is:
App.ResultRoute = Ember.Route.extend({
model: function() {
return this.store.find('person', 1);
}
});
There is '1' because I want only this record.
In this way It works and I see in the view the {{name}} and the {{email} of the Person object.
I want to see only the JSON, I tried to do how you suggest me :
App.ResultRoute = Ember.Route.extend({
afterModel: function (model) {
model.get('content').forEach(function (item) {
console.log(item.get('content'));
});
}
});
But I receive this error:
Uncaught Error: Assertion Failed: Error: More context objects were passed than there are dynamic segments for the route: error
What is my error?
The way I would do this would be, I would have an api in my model which would return a plain json object to whoever asked it. So the Person model would have a getPersonDetails method which will hide all the internal details, including the attributes and associations and whatever else, and return the state of the person object it is invoked upon.
So, for example, if you wanted to display a table of persons or something, you would do a createRecord, and just ask the newly created person object for it's details.
Start from the beginning of this guide. http://emberjs.com/guides/routing/specifying-a-routes-model/ It will show you how to specify a model for a route.
Then, read this entire guide on controllers: http://emberjs.com/guides/controllers/
In general, you would access that data from the route's model hook with:
this.store.find('person') // All records
If you wanted to access that first object as JSON, you could do:
var person_JSON = this.store.find('person').then(function (persons) {
//The persons records are now available so you can do whatever you want with them
console.log(persons.objectAt(0).get('content'));
});
You could also iterate over all records and strip out the content to produce raw json without the Ember wrapping... Just depends on what you need to really do.
Really the best place to put this would be the route's afterModel hook, though. You wouldn't be working with a promise, as Ember would have dealt with that for you:
afterModel: function (model) {
model.get('content').forEach(function (item) {
console.log(item.get('content'));
});
}
Hope that helps.
Edit: Since you have one record try this:
afterModel: function (model) {
console.log(model.get('content'));
}

Ember sort an enumerable array or add an new object to a regular array

I have a todo list.
The main template has an input for entering new todos and an outlet.
The outlet is for the todos.index template which displays each todo. I have made them srtable using jquery sortable. I sort them using a model property.
todos route:
model: function(){
return this.store.filter("todo", {}, function(todo){
if(todo.get("user") !== null && parseInt(todo.get("user").get("id")) === user_id)
return todo;
});
todos index route:
model: function(){
return this.modelFor('todos').sortBy('idx');
}
controllers:
App.TodosController = Ember.ArrayController.extend();
App.TodosIndexController = Ember.ObjectController.extend();
But when I add this .sortBy() method my returning array of objects is no longer live and any new todos i create arent added to the template. (they do get created and are in ember data and in my db, but they just arent being added to the template) - When you do sortBy the live array of ember data gets copied into an immmutable regular array.
If I leave out the sortBy() my new objects populate my template just fine.
any ideas of how to sort an array but keep it enumerble or how to refresh the template?
sortProperties is not working - I dont know why. My list of todos is populated by the objectController.
EDIT -
My problem was my own fault. I thought I had this
App.TodosIndexController= Ember.ObjectController.extend();
But what I actually had was:
App.TodoController = Ember.ObjectController.extend();
This left me without an explicit controller to do the sorting for me, so for me the solution was to create a new arrayController:
App.TodosIndexController= Ember.ObjectController.extend();
and use that to do the sorting. Though it wasnt the solution to my problem, my issue was my own obliviousness, it is the right way to sort items so I am going to mark #Kingpins answer as correct.
Make your controller sorted, and iterate over the controller (not the model) in your template.
App.TodosIndexController = Ember.ArrayController.extend({
sortProperties: ['idx'],
sortAscending: true
});
return your model to just the modelFor
model: function(){
return this.modelFor('todos');
}
Or you could create a computed property on the controller and watch the model for updates, updating the computed property
App.TodosIndexController = Ember.ArrayController.extend({
sortedTodos: function(){
return this.get('model').sortBy('idx');
}.('model.[]')
});
and again, return your model to just the modelFor
model: function(){
return this.modelFor('todos');
}
The other way to manually update the template, is to just get the model for TodosIndexController and manually use pushObject to add the new todos to it. It's just an array of items.

Using ArrayController with multiple models

I have a Route which loads multiple models:
App.AppRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
models1: this.store.find('model1'),
models2: this.store.find('model2'),
models3: this.store.find('model3'),
})
}
});
I want to extend the ArrayController for this page:
App.AppController = Ember.ArrayController.extend();
And the error is thrown:
Error: Assertion Failed: ArrayProxy expects an Array or Ember.ArrayProxy, but you passed object
My question is how to write a controller which can handle multiple model objects.
Really an array doesn't make since. You have 3 different arrays.
So it would be an object controller, and you can access each item as an array in the application template or wherever it's appropriate.
App.AppController = Ember.ObjectController.extend();

Can you bind a simple javascript array to your ember.js template?

I'm using ember.js RC1 + ember-data rev 11 (but I also need some plain ajax for configuration like models). I want to loop over a simple objects list and display the records (note -here I create just a basic array)
The content I have bound has the following custom find method defined
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
return this.records;
},
find: function() {
var self = this;
$.getJSON('/api/foo/', function(response) {
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
});
return this.records;
}
});
My other model uses this directly
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property()
});
Now inside my template
{{#each foo in related.listings}}
{{foo.name}}<br />
{{/each}}
The list loads up with whatever I put in the array by default (say I add a simple object using createRecord like so)
add: function(record) {
this.records.addObject(App.Foo.createRecord(record));
},
and when the template is rendered I see anything listed here... but as I put in the comments above, if I decide to remove records or null out the list that is bound it doesn't seem to reflect this in any way.
Is it possible to bind a simple array as I have and yet remove items from it using something basic such as splice? or even a drastic self.records = []; ?
self.records.splice(i, 1);
Even when I query the client manually after the splice or empty work it returns 0
console.log(App.Foo.all().get('length'));
Initially I see records, but then I see they are gone (yet the html doesn't change)
I understood your question this way, that the following remark is the point your are struggling with:
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
You are wondering, why your template is showing no empty list? It's because you did not tell Ember when to update the template. You can tell Ember this way:
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property("App.Foo.records.#each")
});
Now Ember knows, whenever something is added or removed from your array, it should update the listings property of your model. And therefore it knows that your view needs rerendering.
One additional remark to the orignal question regarding "simple javascript arrays". When you use Ember, you actually do not instantiate simple js arrays. When you declare:
var a = []; // is the same as -> var a = Ember.A();
Ember does some magic and wraps in an enhanced ember version of an array (Ember.NativeArray), which enables you to use such property dependency declarations mentioned above. This enables Ember to use ArrayObservers on those arrays, although they may feel like a plain JS Array.
You need to use the set method when you modify properties and get when you return them, or else Ember won't be able to do its magic and update the template.
In your case, there is an additional problem, which is that in find(), you return a reference to records before your asynchronous getJSON call replaces it with a new empty array. The calling method will never see the new array of records. You probably want to use clear() instead.
Your model should look something like this:
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
// can't use 'this.get(...)' within a class method
return Ember.get(this, 'records');
},
findAll: function() {
var records = Ember.get(this, 'records');
$.getJSON('/api/foo/', function(response) {
records.clear();
// in this case my json has a 'foos' root
response.foos.forEach(function(json) {
this.add(json);
}, this);
}, this);
// this gets updated asynchronously
return records;
},
add: function(json) {
// in order to access the store within a
// class method, I cached it at App.store
var store = App.get('store');
store.load(App.Foo, json);
var records = Ember.get(this, 'records');
records.addObject(App.Foo.find(json.id));
}
});
Note that the addObject() method respects observers, so the template updates as expected. removeObject() is the corresponding binding-aware method to remove an element.
Here's a working jsfiddle.

Categories