Automatic population of Backbone.Collection using JSON objects - json

//Model
var Dog = Backbone.Model.extend({
name:'',
breed:''
});
//Collection
var Dogs = Backbone.Collection.extend({
model : Dog,
url : '/dogs'
parse : function(res)
{
alert('response' + res);
return res;
}
});
This is the JSON objec that I receive from server which is implemented using Jersey.
I return a List of DogModel from Server, it is converted to JSON
#Produces(MediaType.APPLICATION_JSON)
{"DogModel":[{"name":"Jane","breed":"Great Dane"},
{"name":"Rocky","breed":"golden Retriver"},
{"name":"Jim","breed":"Lab"}]}
Wonder I haven't understood the usage of Collection and its url attribute correctly.
My assumption is that, when ever fetch is called on Collection, it'll fetch the dogs details from the server and populate the collection.
I do get the response as stated above but the collection is not populated as expected.
What should I do to automatically populate the list of models with the collection?
Do I need to work on the representation of JSON objects?
Help Appreciated!!!

The parse function needs to return the array of dogs. So you can update your code as follows.
parse : function(res)
{
alert('response' + res);
return res.DogModel;
}
On a side note, you want to declare your model's default attribute values on the defaults hash like the code below shows (see documentation)
var Dog = Backbone.Model.extend({
defaults: {
name:'',
breed:''
}
});

Related

Unable to retrieve JSON array with AngularJS $resource

I've been trying retrieve values from JSON and so far, been unsuccessful. It does get called on the front-end when I refresh the page, but the information is not passing to the next method. I think the issue might be down to the promises.push... line, as I've tried to debug the method underneath and the information is not being passed on at all.
AngularJS:
var promises = [];
promises.push(SpringDataRestService.get({"collection": "subjects"}).$promise);
// Require each of these queries to complete before continuing
$q.all(promises).then(function (data) {
// Grab the first result
$scope.available = data[0].subjects;
$scope.selected = [];
// If this is an update, get the second result in set
if (data.length > 1) {
// For each permission that is assigned to this role, add ID (name) to selected
for (var i = 0; i < data[1].data.subjects.length; i++) {
var perm = data[1].data.subjects[i];
$scope.selected.push(perm.name);
}
}
$scope.tableEditOptions = new NgTableParams({}, {
dataset: $scope.available
});
$scope.available, 'name');
}).catch(function (data) {
// ERROR
});
JSON:
[
{
"name": "FWGWG",
"description": "WGWGWG",
"lockId": 0
},
{
"name": "QFQFQF",
"description": "QFQFQFQ",
"lockId": 0
}
]
I'm confident as well my for loop is wrong due to assigning the values as well, since I don't think it should be data.subjects, but I understand these threads are only 1 issue per question. Any help would be greatly appreicated.
Use the query method for arrays:
var promise = SpringDataRestService.query({"collection": "subjects"}).$promise;
promise.then(function (dataArr) {
console.log(dataArr);
//...
}).catch(function (errorResponse) {
console.log(errorResponse);
});
With the REST services, the get method returns a JavaScript object and the query method returns a JavaScript array.
From the Docs:
$resource Returns
A resource "class" object with methods for the default set of resource actions optionally extended with custom actions. The default set contains these actions:
{
'get': {method: 'GET'},
'save': {method: 'POST'},
'query': {method: 'GET', isArray: true},
'remove': {method: 'DELETE'},
'delete': {method: 'DELETE'}
}
...
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data.
For more information, see
AngularJS $resource Service API Reference

Reformat API data to Ember friendly array of objects | Ember data with unconventional endpoint

This is a question about molding some API data to fit some needs. I've heard it called "munging." I guess the heart of if is really re-formatting some JSON, but It would be ideal to do it the Ember data way...
I'm getting this data in an Emberjs setting - but it shouldn't really matter - ajax, ic-ajax, fetch, etc... I'm getting some data:
...
model: function() {
var libraryData = ajax({
url: endPoint,
type: 'GET',
dataType: 'jsonp'
});
// or most likely the ember-data way
// this.store.findAll(...
console.log(libraryData);
return libraryData;
}
...
The URL is getting me something like this:
var widgetResults = {
"settings": {
"amazonchoice":null,
"show":{
"showCovers":null,
"showAuthors":null
},
"style":null,
"domain":"www.librarything.com",
"textsnippets":{
"by":"by",
"Tagged":"Tagged","readreview":"read review","stars":"stars"
}
},
"books":{
"116429012":{
"book_id":"116429012",
"title":"The Book of Three (The Chronicles of Prydain Book 1)",
"author_lf":"Alexander, Lloyd",
"author_fl":"Lloyd Alexander",
// ...
The promise that is actually returned is slightly different.
My goal is to get to those books and iterate over them - but in my case it wants an array. that #each loops over must be an Array. You passed {settings: [object Object], books: [object Object]} - which makes sense.
In and ideal API the endpoint would be / http:/site.com/api/v2/books
and retrieve the data in this format:
{
"book_id":"116428944",
"title":"The Phantom Tollbooth",
"author_lf":"Juster, Norton",
"author_fl":"Norton Juster",
...
},
{
"book_id":"116428944",
"title":"The Phantom Tollbooth",
"author_lf":"Juster, Norton",
"author_fl":"Norton Juster",
...
},
{
... etc.
I would expect to just drill down with dot notation, or to use some findAll() but I'm just shooting in the dark. Librarything in specific is almost done with their new API - but suggest that I should be able to loop through this data and reformat it in an ember friendly way. I have just looped through and returned an array in this codepen - but haven't had luck porting it... something about the returned promise is mysterious to me.
How should I go about this? am I pointed in the wrong direction?
I've tried using the RESTAdapter - but didn't have much luck dealing with more unconventional endpoints.
Custom Adapters / Serializers ?
this article just appeared: "Fit any backend into ember with custom adapters and serializers
Full url with endpoint in question
model (just title to test)
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string')
});
route ( per #Artych )
export default Ember.Route.extend({
model() {
$.ajax({
url: endPoint,
type: 'GET',
dataType: 'jsonp'
}).then((widgetResults) => {
// modify payload to RESTAdapter
var booksObj = widgetResults.books;
var booksArray = Object.keys(booksObj).map((element) => {
var book = booksObj[element];
book.id = book.book_id;
delete book.book_id;
return book;
});
console.log(booksArray);
this.store.pushPayload({books: booksArray});
});
return this.store.peekAll('book');
}
});
template
{{#each model as |book|}}
<article>
<h1>{{book.title}}</h1>
</article>
{{/each}}
There is straightforward solution to process your payload in model():
Define book model.
Process your payload in model() hook:
model() {
$.ajax({
url: endPoint,
type: 'GET',
dataType: 'jsonp'
}).then((widgetResults) => {
// modify payload to RESTAdapter
var booksObj = widgetResults.books;
var booksArray = Object.keys(booksObj).map((element) => {
var book = booksObj[element];
book.id = book.book_id;
delete book.book_id;
return book;
});
this.store.pushPayload({books: booksArray});
});
return this.store.peekAll('book');
}
Iterate model in controller or template as usual.
Working jsbin:
ember 1.13
ember 2.0
You want a custom serializer to translate the data from that format into JSON-API. JSON-API is an extremely well thought-out structure, so well in fact that ember-data has adopted it as the default format used internally. Some of the benefits are that it defines a structure for objects themselves, separating attributes from relationships; a means for embedding or including associated resources; defines a place for errors and other metadata.
In short, for whatever you're trying to do, JSON-API probably has already done a lot of the decision-making for you. And, by subclassing from DS.JSONSerializer, you'll be mapping right into the format that ember-data needs.
To do this, you create a custom adapter using ember generate serializer books:
// app/serializers/book.js
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
// payload will contain your example object
// You should return a JSON-API document
const doc = {};
// ...
return doc;
}
});
For your example data, the output of the normalization should look something like this:
{
"data": [
{
"type": "books",
"id": 116429012,
"attributes": {
"title": "The Book of Three (The Chronicles of Prydain Book 1)",
"author_lf": "Alexander, Lloyd",
"author_fl": "Lloyd Alexander"
}
},
{
"type": "books",
"id": 1234,
"attributes": {
}
}
],
"meta": {
"settings": {
"amazonchoice":null,
"show":{
"showCovers":null,
"showAuthors":null
},
"style":null,
"domain":"www.librarything.com",
"textsnippets":{
"by":"by",
"Tagged":"Tagged","readreview":"read review","stars":"stars"
}
}
}
};
Then do
this.get('store').findAll('books').then((books) => {
const meta = books.get('meta');
console.log(meta.settings.domain);
books.forEach((book) => {
console.log(book.get('title'));
});
});
Code is not tested, but hopefully it gets you started.
Define settings and book models. Arrange for the API to respond to the endpoint /books returning data in the format:
{
settings: { ... },
books: [
{
id: xxx,
...
}
]
}
Retrieve the data in the model hook with this.store.findAll('book').
Iterate over the books in your template with {{#each model as |book|}}.

cannot create backbone js collection from server json response

OK.
I know many questions have been asked here for this problem but my question is a bit difference from it.
I have laravel as the back end server and a defined route:
Route::post('/allusers',array('uses'=>'UsersController#index');
#index method (laravel):
public function index()
{
//
$user=DB::table('users')->select('id','name','email')->get();;
if($user){
return response()->json($user);
}
return "failed";
}
and then, I have a backbonejs collection and model like bellow:
user model:
user = Backbone.Model.extend({
urlRoot: '/user/'+this.id,
default:{
'name':'',
'email':'unknown#no-name.com',
},
render:function(){
console.log(this.name);
}
, initialize:function(){
console.log('model user created'+this.cid);
console.log(this.name);
}
});
and backbone collection:
UserCollection = Backbone.Collection.extend({
model:user,
url:'/allusers/',
parse:function(resp,xhr){
rt= _.toArray(resp);
return rt;
},
initialize:function(){
this.fetch();
}
});
but the colletion cannot be create. the models attribute of collection is always 0.
here is the JSON response from laravel :
[Object { id=1, name="abc", email="someone#google.com"}, Object { id=2, name="anotheruser", email="anotheruser#gmail.com"}]
Please, any suggestion.
thank you in advance.
You are not returning the valid JSON from your laravel. Backbone assumes it is wrong and triggers error callback.use this snippet to return as JSON.
if($user){
return Response::json($user->toArray());
}
With perfect json returned you don't have to parse in model. so you can remove the parse function from your model (unless you want to manipulate on data).
So your model should look like this,
UserCollection = Backbone.Collection.extend({
model:user,
url:'/allusers/',
initialize:function(){
this.fetch();
}
});

Create a collection from inside a collection

I have a collection, which when fetched gets a json and puts into collection:
The JSON format is:
[
{
name: 'Hello',
age: '22',
bio: [{
interest: 'soccer',
music: 'r&B'
}]
}
]
I want to make another collection from bio (without fetching again).
The reason is I want to access both name, age and bio and a parse function can have only one return?
var user = new Backbone.Collection.extend({
url: '/user',
parse: function (response) {
//I want both of this?
//return response;
return response.bio;
}
});
I am passing this collection on success function of fetch into two different views.
//Controller File....
.............
mycollection.fetch({
success: function() {
//Details View wants response
PrimaryLayout.main.show(new detailsView{collection: mycoll});
//Bio View wants response.bio
PrimaryLayout.body.show(new bioView{collection: mycoll});
}
})
What would be the best way to tackle this? Can I clone a collection and just have bio in it?
I've generally solved this by instantiating the sub-collection in parse:
var User = Backbone.Model.extend({
parse: function(json) {
// instantiate the collection
json.bio = new Backbone.Collection(json.bio);
// now person.get('bio') will return a Collection object
return json;
}
});
var Users = Backbone.Collection.extend({
model: User,
// ...
});
I think this is what you are looking for : Backbone Associations. Have a look at the tutorials and examples there.

Render view with overwritten getters in Backbone

I am building a small Backbone.js application and added some custom getters to one of the models (the name getter returns a concatenated first- and last name for example):
PersonModel = Backbone.Model.extend({
get: function (attr) {
if (typeof this[attr] == 'function') {
return this[attr]();
}
return Backbone.Model.prototype.get.call(this, attr);
},
name: function() {
return firstName + " " + lastName;
}
})
I can now use person.get("name") to retrieve the name, nice. However, when I call toJSON on the the model these values aren't included (and I suppose that makes sense). Problem is I use this to render my views:
this.template({people: this.collection.toJSON()});
What's the best way to do this in Backbone.js? Manually creating the JSON with the overwritten getters?
Thanks!
You could provide your own toJSON method on PersonModel:
toJSON: function() {
var attr = Backbone.Model.prototype.toJSON.call(this);
attr.name = this.name();
return attr;
}
The collection toJSON just calls toJSON on each model:
// The JSON representation of a Collection is an array of the
// models' attributes.
toJSON : function() {
return this.map(function(model){ return model.toJSON(); });
},
so adding your own toJSON to your model should work.
You could also add name as a real attribute and then adjust your model's validate method to update name if firstName or lastName changes and to ignore any direct attempts to change name or complain about "an attempt to edit a read-only attribute" when someone tries to pass a name change to set. There's nothing that says that validate can't change the attribute object that it is given so you could be given {firstName: 'x'} and change it to {firstName: 'x', name: 'x ' + this.get('lastName')} before validate returns. This would be a bit of an abuse of validate but there is no explicit prohibition against validate altering the attribute set and it is the only hook you have. I suppose you could have the model listen to change events on its own firstName and lastName and then trigger a set({name: ...}) but then you could have event ordering problems if someone else is watching only the first and last names.
I choose to extend the backbone model and have all my apps models extend from a Base model which includes a method so enable setting virtual attributes which are merged into models toJSON method.
toJSON: function() {
return _.extend(toJSON.__super__.constructor.apply(this, arguments), this.getVirtualAttributes());
},
getVirtualAttributes: function() {
var attrs, key;
attrs = {};
for (key in this.virtualAttributes) {
attrs[key] = this.virtualAttributes[key].call(this);
}
return attrs;
}
This allows you to define a virtualAttributes object on a model which can be used for retrieving a virtual/dynamic attribute.
virtualAttributes: {
email_length: function() {
return this.get("email").length
}
}