I'm feeling stupid asking the question, but I'm with it like 3 days ago... I built a php api rest, that returns a json encode info. The json that the api returns is correct, is something like that:
[
{"pk_user":"1","name":"George","surname":"Littlewolf","profile":"Game designer - Developer"},
{"pk_user":"2","name":"Anne","surname":"Carpenter","profile":"Developer"},
{"pk_user":"3","name":"John","surname":"Sullivan","profile":"Designer"},
{"pk_user":"4","name":"Judith","surname":"Lenders","profile":"Developer"},
{"pk_user":"5","name":"Jason","surname":"Middleton","profile":"Manager"}
]
After that, I'm creating a backbone front-end to attack the api. I have developed only one view, that reads the json I wrote above. I have one model (User) and one Collection (Users) where I'd like to save the info read from the json info.
I'm going to put only the code of the render function of the view:
render: function() {
var users = new Users();
users.fetch();
$.each(users['models'], function(i, item) {
console.log(item);
});
this.template = _.template(template, {users : users});
this.$el.html(this.template);
return this;
}
Now, the console returns only what you see in the next image:
I'd like to access directly to the items, but i don't know why but all that I've tried is notworking... Thanks and excuse me for the newbie explanation...
EDIT
I've uploaded the code to a hosting to everyone who want to see the problem "on live".
fetch is an AJAX call so this:
users.fetch();
won't fill the collection right away, it has to wait for the server to respond. So when you go to look at users.models, there won't be anything there and your view ends up doing nothing.
The usual approach is to listen for events from users and render HTML in response to those events:
initialize: function() {
this.collection = new Users;
this.listenTo(this.collection, 'reset', this.render);
// bind to other events as needed...
this.users.fetch({ reset: true });
},
render: function() {
this.collection.each(function(user) {
// render the `user` model in here...
});
}
Note the switch to this.collection.each instead of messing around with the collection's models property directly.
Alternatively, use a success handler when you call fetch:
this.users.fetch({
success: function(collection) {
collection.each(function(user) {
// render the `user` model in here...
});
}
});
Not sure if it is what you want, but if you want to log the attributes of the User just do this:
$.each(users['models'], function(i, item) {
console.log(item.attributes);
});
Related
I am using iron:router in my app and I have a controller that subscribes to one document in a collection by using a parameter in the path. I can access all of the documents in my collection on the server, so I know that there is stuff in there, but when I try to access the data that I subscribe to in the waitOn method of the controller, the data is undefined. Here is the relevant code for this problem.
Router code:
this.route('unit', { path: 'unit/:unitId', template: 'unit', controller: 'UnitController' });
UnitController = BaseController.extend({
waitOn: function () {
return Meteor.subscribe('getUnit', this.params.unitId);
},
data: function () {
var id = this.params.unitId;
templateData = {
unit: Collections.units.model(Collections.units.getUnit(id))
};
return templateData;
}
});
Publication:
Meteor.publish('getUnit', function(id) {
return Collections.units.data.find({ unitId: id });
});
Here I have created an object for various things to do with my collection(I only included the important parts here):
Collections.units = {
data: new Mongo.Collection("units"),
getUnit: function (id) {
return this.data.findOne({ unitId: id });
},
model: function(unitEntity) {
return {
unitId: unitEntity.unitId,
createdAt: unitId.createdAt,
packets: unitEntity.packets,
getLastPacket: function (id) {
return _.last(this.packets);
}
};
}
};
I have been trying to debug this for quite a while and I can do all the things to the collection I want to on the server and in the publish method, but when it gets to the controller, I can't access any of the info. In the data method this.params.unitId returns exactly what I want so that isn't the issue. Where the exception gets thrown is when I try to read properties of unitEntity when I'm making the model but that is just because it is undefined.
Have any ideas what I am doing wrong? Thanks in advance for any responses.
The main problem that I was trying to solve's solution was to wrap the code inside the data method inside of if (this.ready()){ ... } and add an action hook with if (this.data()) { this.render(); }. After I got the subscription to work, I found that Christian was right in the comments with saying that my controller setup might mess things up. It was causing other strange exceptions which I fixed by just moving the hooks to each route instead of using the controller. As for my Collections setup, it may be unconventional, but all of that is working fine (as of right now). I may want to set them up the standard way at a later point, but as of right now its pretty handy for me to do things with the collections with the methods already written in the object.
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'));
}
I need to work with backbone.js, i can't go to "render" part inside my view here is my code:
var Vigne = {
Models:{},
Collections: {},
Views:{},
Templates:{}
}
Vigne.Models.Raisin = Backbone.Model.extend({})
Vigne.Collections.Grape = Backbone.Collection.extend({
model: Vigne.Models.Raisin,
url: "./scripts/data/vin.json",
initialize: function (){
console.log("grape initialised");
}
});
Vigne.Views.Grape= Backbone.View.extend({
initialize: function(){
_.bindAll(this,"render");
this.collection.bind("reset",this.render);
},
render: function(){
console.log("render");
console.log(this.collection.length);
}
})
Vigne.Router = Backbone.Router.extend({
routes:{
"": "defaultRoute"
},
defaultRoute: function(){
console.log("defaultRoute");
Vigne.grape = new Vigne.Collections.Grape()
new Vigne.Views.Grape ({ collection : Vigne.grape });
Vigne.grape.fetch();
console.log(Vigne.grape.length);
}
}
);
var appRouter= new Vigne.Router();
Backbone.history.start();
I am expecting it to display my collection's length in the debugger's console, it seem's like it doesn't reset. Any ideas?
Edit:
i added this within the fetch function:
success: function(){
console.log(arguments);
},
error: function() {
console.log(arguments);
}
});
and the fetch function succeed on getting the json file, but it doesn't trigger the reset function.
i solved this problem by setting the attribute within the fetch function to true:
Vigne.grape.fetch({
reset:true,
error: function() {
console.log(arguments);
}
}
);
This book helped me : http://addyosmani.github.io/backbone-fundamentals/
Backbone calls reset() on fetch success which in turns triggers reset event. But If your fetch fails due to some reason, you won't get any event. So you have to pass an error handler in fetch method and use it to identify the error and handle it.
Vigne.grape.fetch({
error: function() {
console.log(arguments);
}
});
You can also pass success call back and you will be able to know the problem in your fetch.
You can also use Charles proxy/Chrome Debuuger Tool to identify if you are getting proper response from your backend.
Can you please paste your response what you are getting from server. You may vary the data but just keep the format right.
Edit:
One more problem I can see is that you have not defined attributes in your model So after Backbone fetch, it refreshes your collection with the new models fetched from server. Fetch method expects an array of json objects from server and each json object in response array should match with the attributes you have set in defaults in your model Otherwise it won't be able to create new models and hence won't be able to refresh your collection. Can you please rectify this and let me know.
I have my views correctly displaying fake information and so I am now trying to apply asynchronus data loading to retrieve actual data. The problem is that I am uncertain about how I should go about this. Should I create AJAX calls myself? Should I use the Socket API? Should I use the built in REST api (and how to do so asynchronously)? The server side handler is still unimplemented so as far as how the server serves up the data, that is completely flexible.
i doubt your own ajax calls is what is needed here...
i can't tell about sockets however i know it is possible and a solid idea depending on your app.
i have been using the default REST functionality and it works well for me,
a small example as how I would do it,
to make it less complex i will just act as if it is from page load, instead of using routers and all.
var myView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
var v = this;
this.model.bind("change", function(e) {
this.render();
});
},
render: function() {
this.el.empty();
this.el.text(this.model.get('name'));
}
});
var myModel = Backbone.Model.extend({
url: "/api/myModel", // change to your server code...
defaults: {
name: "john"
}
});
$(function(){
var m = new myModel({}); // dummy model
var v = new myView({ model: m, el: $('#myDiv')});
v.render();
m.fetch(); // takes the url of the model or collection and fetches it from the server side ...
});
if you want to test what the fetch would do, you can for try this code from your console, (or add it to the jquery document load function:
m.set({ name: 'peter' });
this changes the model's property 'name' and you will immediately see the view update itself, because it listens to the change event of the model.
more info on these events can be found here: http://documentcloud.github.com/backbone/#Events
I've got the following model with nested collection
var Mdl = Backbone.Model.extend({
initialize: function() {
// collection
this.col1 = new NestedCollection();
},
...
});
I would like to send the data for both the model and the models in the collection in one request looking something like:
{
att1: val,
col1: [{obj1: val}, {...}]
}
I'm unsure about the best way to hand the data in the request to the nested collection (col1). I can't do ...
var Mdl = Backbone.Model.extend({
initialize: function() {
// collection
this.col1 = new NestedCollection(this.get('col1');
},
...
});
... because at the time of initialize is called the parse function of the model has not been called which means that the attribute col1 is empty, another solution I thought of was to listen for the change in the parent model like...
model.bind("change:tags", function() {
model.col1.refresh(model.get('col1'));
});
however this solution feels a little heavy handed and might potentially break any
this.col1.bind("add", function() {})
and
this.col1.bind("remove", function() {})
function set-up on the collection.
Has anyone got any idea of the 'official' way of doing this?
Thanks.
The "official" way is to override the parse method:
http://documentcloud.github.com/backbone/#Model-parse
In your specific case, what I would probably do is, in the parse method, build the nested collection from the col1 data, delete it from the results, then hand the results on. Backbone will then turn the rest of the data into properties.
I have not tried this, so I'm not 100% sure it works:
parse: function(response) {
this.col1 = new NestedCollection(response.col1);
delete response.col1
return response
}
Edit: Nov 28th 2012
Harm points out that this might not be the best way to do it any more. The original answer was written quite a while ago, and the original question indicated that the user wanted the collection to be a property on the model (not an attribute), but Harm has a point that having the collection as an attribute is a more accepted way of doing it these days.
Today, you could use something like Backbone-Relational to handle a lot of this stuff for you, or, if you wanted to do it yourself, and have the collection as a model attribute, you could do something like:
Building = Backbone.Model.extend({
parse: function(response) {
console.log("Parse Called");
response.rooms = new Rooms(response.rooms);
return response;
}
});
Room = Backbone.Model.extend({});
Rooms = Backbone.Collection.extend({
model: Room
});
science_building = new Building();
science_building.fetch(
{success: function(model,resp) {console.log(resp);}}
);
With a model fetch response like:
{ id: 1,
name: "Einstein Hall",
rooms: [
{id:101, name:'Chem Lab'},
{id:201, name:'Physics Lab'},
{id:205, name:'Bio Lab'}
]
}
Resulting in a Building model that allows:
science_building.get('rooms').get(101).get('name') // ==> "Chem Lab"
A working jsFiddle example: http://jsfiddle.net/edwardmsmith/9bksp/