I'm trying to load some data into a Backbone Collection from a local JSON file, using this very basic code:
window.Student = Backbone.Model.extend({
});
window.Students = Backbone.Collection.extend({
model: Student,
});
window.AllStudents = new Students();
AllStudents.fetch({ url: "/init.json"});
console.log('AllStudents', AllStudents);
In the console statement, AllStudents is empty. But init.json is definitely being loaded. It looks like this:
[
{ text: "Amy", grade: 5 },
{ text: "Angeline", grade: 26 },
{ text: "Anna", grade: 55 }
]
What am I doing wrong?
UPDATE: I've also tried adding a reset listener above the .fetch() call, but that's not firing either:
AllStudents.bind("reset", function() {
alert('hello world');
});
AllStudents.fetch({ url: "/init.json"});
No alert appears.
UPDATE 2: Trying this script (reproduced here in full):
$(function(){
window.Student = Backbone.Model.extend({
});
window.Students = Backbone.Collection.extend({
model: Student,
});
window.AllStudents = new Students();
AllStudents.url = "/init.json";
AllStudents.bind('reset', function() {
console.log('hello world');
});
AllStudents.fetch();
AllStudents.fetch({ url: "/init.json", success: function() {
console.log(AllStudents);
}});
AllStudents.fetch({ url: "/init.json" }).complete(function() {
console.log(AllStudents);
});
});
Only one console statement even appears, in the third fetch() call, and it's an empty object.
I'm now absolutely baffled. What am I doing wrong?
The JSON file is being served as application/json, so it's nothing to do with that.
The attribute names and non-numeric attribute values in your JSON file must be double quoted (" ") . Single quotes or no-quotes produces errors and response object is not created that could be used to create the models and populate the collection.
So. If you change the json file content to :
[
{ "text": "Amy", grade: 5 },
{ "text": "Angeline", grade: 26 },
{ "text": "Anna", grade: 55 }
]
you should see the non-empty collection object.
You can change your code to see both success and failure as below:
AllStudents.fetch({
url: "/init.json",
success: function() {
console.log("JSON file load was successful", AllStudents);
},
error: function(){
console.log('There was some error in loading and processing the JSON file');
}
});
For more details, probably it will be a good idea to look in to the way ajax response objects are created.
I/O operations in javascript are almost always asynchronous, and so it is with Backbone as well. That means that just because AllStudents.fetch has returned, it hasn't fetched the data yet. So when you hit your console.log statement, the resources has not yet been fetched. You should pass a callback to fetch:
AllStudents.fetch({ url: "/init.json", success: function() {
console.log(AllStudents);
}});
Or optionally, use the new promise feature in jQuery (fetch will return a promise):
AllStudents.fetch({ url: "/init.json" }).complete(function() {
console.log(AllStudents);
});
fetch() returns a 'success' notification as already stated, but that just means that the server request was successful. fetch() brought back some JSON, but it still needs to stuff it into the collection.
The collection fires a 'reset' event when it's contents have been updated. That is when the collection is ready to use...
AllStudents.bind('reset', function () { alert('AllStudents bind event fired.'); });
It looks like you had this in your first update. The only thing I did differently was to put fetch() in front of the event binding.
I think you need to add {add:true} to the options of fetch,
if you assigned the fetch to a variable, you would get the result as well,
but then its not inside the collection you wanted
Related
I am working on an ASP.NET MVC 4 app. In my app, I'm trying to replace my drop down lists with the Select 2 plugin. Currently, I'm having problems loading data from my ASP.NET MVC controller. My controller looks like this:
public class MyController : System.Web.Http.ApiController
{
[ResponseType(typeof(IEnumerable<MyItem>))]
public IHttpActionResult Get(string startsWith)
{
IEnumerable<MyItem> results = MyItem.LoadAll();
List<MyItem> temp = results.ToList<MyItem>();
var filtered = temp.Where(r => r.Name.ToLower().StartsWith(startsWith.ToLower());
return Ok(filtered);
}
}
When I set a breakpoint in this code, I notice that startsWith does not have a value The fact that the breakpoint is being hit means (I think) my url property below is set correct. However, I'm not sure why startsWith is not set. I'm calling it from Select 2 using the following JavaScript:
function formatItem(item) {
console.log(item);
}
function formatSelectedItem(item) {
console.log(item);
}
$('#mySelect').select2({
placeholder: 'Search for an item',
minimumInputLength: 3,
ajax: {
url: '/api/my',
dataType: 'jsonp',
quietMillis: 150,
data: function (term, page) {
return {
startsWith: term
};
},
results: function (data, page) {
return { results: data };
}
},
formatResult: formatItem,
formatSelection: formatSelectedItem
});
When this code runs, the only thing I see in the select 2 drop down list is Loading failed. However, I know my api is getting called. I can see in fiddler that a 200 is coming back. I can even see the JSON results, which look like this:
[
{"Id":1,"TypeId":2,"Name":"Test", "CreatedOn":"2013-07-20T15:10:31.67","CreatedBy":1},{"Id":2,"TypeId":2,"Name":"Another Item","CreatedOn":"2013-07-21T16:10:31.67","CreatedBy":1}
]
I do not understand why this isn't working.
From the documentation:
Select2 provides some shortcuts that make it easy to access local data
stored in an array...
... such an array must have "id" and "text" keys.
Your json object does not contain "id" or "text" keys :) This should work though i have not tested it:
results: function (data, page) {
return { results: data, id: 'Id', text: 'Name' }
}
There's further documentation following the link on alternative key binding... I believe thats where your problem lies.
Hopefully this helps.
I am new to backbone and nodejs, I have made a demo which used backbone and nodejs for updating and inserting data. I'm able to send put request with single data at a time.
this.model.set({
user_id:Session.get('userid'),
seat_id:seatId
});
this.model.save({
success: function() {
// do some stuff here
alert("a")
},
error: function() {
// do other stuff here
alert("b")
}
})
The above code post single row info to server. I want to send multiple info to server at a time. Can we set model something like below
this.model.set([{
user_id:Session.get('userid'),
seat_id:2
},{
user_id:Session.get('userid'),
seat_id:3
}]);
Thanks
You can do that:
arr = [{
user_id:Session.get('userid'),
seat_id:2
},{
user_id:Session.get('userid'),
seat_id:3
}];
this.model.set(sessions, arr);
I'm using an API that returns JSON data in this format:
{
paging: {
previous: null,
next: null
},
data: [
{ title: 'First Item' },
{ title: 'Second Item' },
...
]
}
I'm using Angular's $resource service to fetch this data.
My code - which is located in a controller - goes something like this:
var Entity = $resource('/api/entities');
var entities = $scope.entities = Entity.get();
And then, in the view, I can display the data like this:
<ul>
<li ng-repeat="entity in entities.data">{{entity.title}}</<li>
</ul>
It all works fine, but:
I'd rather expose only the contents of entities.data to the view, instead of the whole entities object. How can I intercept the data returned by the GET request to modify it before it populates $scope.entities?
Correlated question: since I am fetching an array of data, it would be cleaner to use Entity.query() instead of Entity.get(). But if I use Entity.query() in the code above, I get an error "TypeError: Object # has no method 'push'". This makes sense, since the API is returning an object instead of an array (hence, no 'push' method on the object). Again, if I could extract the .data attribute from the response, I'd have an array.
Following these indications by Dan Boyon, I managed to customize the default $resource service and to override the .get() or .query() methods, but I'm not sure where to go from there.
I don't think you need to modify the get or query defaults. Just use the success callback to do what you want. It should be more robust as well.
Entity.get(
{}, //params
function (data) { //success
$scope.entities = data.data;
},
function (data) { //failure
//error handling goes here
});
Html will be cleaner, too:
<ul>
<li ng-repeat="entity in entities">{{entity.title}}</<li>
</ul>
By the way, I usually declare services for my resources and then inject them into my controllers as I need them.
myServices.factory('Entity', ['$resource', function ($resource) {
return $resource('/api/entities', {}, {
});
}]);
You can use the Response Transformer (transformResponse) like this:
$resource('/api/entities', {}, {
query: {
method: 'GET',
responseType: 'json',
isArray: true,
transformResponse: function (response) {
return response.data;
}
}
});
This code modifies the "query" method behaviour, you can do the same for "get" ... !
So far i have the following code, but it doesn't seem to be working, and I don't know when the asynchronous fetch is completed:
var item = Backbone.Model.extend({
defaults: {
id: 0,
an_id: 0,
strval: null,
th_id: 0,
text: null
},
url: 'page.php',
options: {
success: function(data) {
alert('s: ' + dump(data));
// the dump function is my way of dumping objects into a string,
// use console.log if you want, as I have that disabled
},
error: function(x, t, e) {
alert('e: ' + t + ', ' + e);
}
}
});
var coll = Backbone.Collection.extend({
model: item
});
var options = new Options();
Backbone.sync("create", coll, item.options); // 'undefined' is not an object (evaluating c.url) in backbone-min.js
Update
I have modified the original code to what I have now, and the backend can now tell the difference between New, Update, Save and Delete requests.
I still cannot find out how to populate the collection coll.
Backbone.Collection is for keeping multiple items - you seem to be trying to have your Collection "inherit" from your model, which isn't the right approach.
Collections are ordered sets of models. You can bind "change" events
to be notified when any model in the collection has been modified,
listen for "add" and "remove" events, fetch the collection from the
server, and use a full suite of Underscore.js methods.
you can add a success handler to your fetch call. try this:
coll.fetch({
success: function() {
alert("success");
console.log(coll.toJSON());
},
error: function(){
alert("error")}
});
Have checked some backbone.js tutorials and can't
understand how to get model id from the server within
the model saving process. I have a model:
var Game = Backbone.Model.extend({
defaults: {
name: '',
releaseDate: ''
},
url: function(){
return '/data.php';
}
});
How to implement getting the id algorithm? It seams to me, there should
be a kind of callback function, but can't realise where to put it.
See Backbone's documentation on model save.
You can pass a success callback function to save, something like this:
var game = new Game({
name: 'Duke Nukem 3D',
releaseDate: '1996'
});
game.save({}, {
success: function(model, response) {
// get model id from response?
}
);