Reputation: 756
I am generally a backend guy working on J2EE (servlet/ejb) frameworks and libraries. Completely new to front-end and especially Backbone. Hence, the question is somewhat a noob one. I would appreciate any opinions/tutorials/examples for that matter. I will try to explain my objective/requirement as clearly and elaborately as possible.
This is my first SO question and hence apologies in advance for breaking any rules.
Q. I have a backend eCommerce engine which exposes REST URLs as part of product catalog API. API 1 - /rest/repository/atg/commerce/catalog/ProductCatalog/product/. API 1 responds back with a list of products as a single JSON array. The interesting bit here is that it sends only the individual REST URLs for each product. Sample Response:
{"product": [
"http://example.com/rest/repository/atg/commerce/catalog/ProductCatalog/product/prod90004",
"http://example.com/rest/repository/atg/commerce/catalog/ProductCatalog/product/prod90005",
"http://example.com/rest/repository/atg/commerce/catalog/ProductCatalog/product/prod100027"
"http://example.com/rest/repository/atg/commerce/catalog/ProductCatalog/product/prod70011",
"http://example.com/rest/repository/atg/commerce/catalog/ProductCatalog/product/prod70013"
]}
API 2 - http://example.com/rest/repository/atg/commerce/catalog/ProductCatalog/product/productId viz. http://example.com/rest/repository/atg/commerce/catalog/ProductCatalog/product/prod90004 Basically one of the individual product URL from API 1's response Sample Response:
{
"dateAvailable": "2000-05-04 15:51:42.521",
"displayName": "Warm Black Gloves ",
"longDescription": "Protects you from the elements and keeps you looking good, these durable gloves won't let you down.",
"repositoryId": "prod90004"
}
Required objective: If API 1 responds back with a JSON array containing 4 URLs i.e 4 products, then I need to display the attributes of 4 products in a view and so on. The attibutes can be retreived by making 4 calls to API 2; in other words making 4 requests to the URLs received in API 1.
Issue: I have looked into a few tutorials and examples. I could not somehow get anything on this specific use case. The examples that I have seen baically assume that there will be one REST API which would respond back with a JSON array containing all models (products in this case) and their attributes in one single JSON response. However, that is not the case here as I have to first make a call to API 1 to get the list of product URLs and then call all these URLs (retrieved in API 1's response) to get attributes of the corresponding model. Hence, I am somehow not able to decide as to how to define the models, collections and url properties etc.
I have given a try as below:
(function ($) {
var ProductView = Backbone.View.extend({
tagName:"div",
className:"bookContainer",
template:$("#bookTemplate").html(),
render:function () {
console.log("inside render of ProductView");
var tmpl = _.template(this.template);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
var ProductModel = Backbone.Model.extend({
defaults:{
displayName:"displayName",
longDescription:"longDescription",
dateAvailable:"dateAvailable",
repositoryId:"repositoryId",
},
initialize: function(){
this.on("reset", this.renderModelView, this);
this.fetch({
reset:true,
error:function () {
console.log(arguments);
}
});
},
renderModelView: function(){
var that = this;
new ProductView.render({
model: that
});
}
});
var ProductCollection = Backbone.Collection.extend({
model: ProductModel,
initialize: function(){
this.on("reset", this.debugTempfunc, this);
this.fetch({
reset:true,
error:function () {
console.log(arguments);
}
});
},
debugTempfunc: function(){
console.log("collection length: "+this.length);
}
});
var ProductURLModel = Backbone.Model.extend({
defaults:{
product: "ProductCatalogProductURLs"
}
});
var ProductURLCollection = Backbone.Collection.extend({
model: ProductURLModel,
url: "http://example.com/rest/repository/atg/commerce/catalog/ProductCatalog/product",
initialize: function(){
this.on("reset", this.populateModels, this);
this.fetch({
reset:true,
error:function () {
console.log(arguments);
}
});
},
populateModels: function(){
var ProductCollectionIns = new ProductCollection();
var that = this;
console.log("inside populateModels of ProductURLCollection: "+that);
_.each(that.models[0].attributes.product,function(item){
console.log("populateModels, item value: "+item);
ProductCollectionIns.url=item;
ProductCollectionIns.fetch();
});
}
});
var ProductURLCollectionTestIns = new ProductURLCollection();
})(jQuery);
My plan was to first define a ProductURLCollection as a collection with the URL set to API 1's URL. This collection will have a single model ProductURLModel, which will have n number of ProductURLModel(s)... n being number of URLs retrieved in API 1 which is also equivalent to number of products. This indicates that now I have to make n number of API 2 calls. For this I decided to make another collection ProductCollection containing n number of ProductModel(s). ProductCollection's url property will be dynamically set while iterating ProductURLCollection models' as in below code:
populateModels: function(){
var ProductCollectionIns = new ProductCollection();
var that = this;
console.log("inside populateModels of ProductURLCollection: "+that);
_.each(that.models[0].attributes.product,function(item){
console.log("populateModels, item value: "+item);
ProductCollectionIns.url=item;
ProductCollectionIns.fetch();
});
}
With this I hoped that I could make n ProductModel(s) dynamically each time changing ProductCollectionIns url. However, I am getting "Error: A "url" property or function must be specified". Does this mean that ProductCollectionIns url property is not being set?
I believe there must be an easier way to achieve the above in Backbone. Please any solution or better implementation would be appreciated.
Upvotes: 1
Views: 953
Reputation: 35730
Let me explain how Backbone's AJAX system works, first with a Model
(but I'll get to Collection
s in a minute). In short, Backbone was designed for you to be able to do:
var FooModel = Backbone.Model.extend({url: 'www.example.com'});
var someModel = new FooModel({id: 5});
someModel.fetch().done(function() {
// Now someModel should be populated with the AJAX data for model #5
});
Behind the scenes what happens there is when you call fetch
Backbone looks for the URL associated with the Model
. Since you never defined one (or a urlRoot
), you get the error you're having, because Backbone doesn't know where to make the AJAX call to.
Once you tell Backbone where to find the data for that type of model via a url
property, Backbone will make a call to 'www.example.com/5' (because the model has an id
of 5). Backbone will then expect that URL to return a response that looks something like:
{id: 1, someAttribute: 2, someOtherAttribute: 'spam'}
That JSON will become someModel
's attributes, so you'll be able to do:
someModel.get('someAttribute'); // == 2
Collection
s work almost the same way, except they expect a JSON response of:
[{id: 1, someAttribute: 2, someOtherAttribute: 'spam'},
{id: 2, someAttribute: 55, someOtherAttribute: 'eggs'}]
and they convert each member of the returned array in to a Model
.
As you surmised, there is an easier way than your populateModels
method: if you just follow the pattern I described, your collection should create models automatically with no extra effort. If you do need to customize things (eg. you want to change the "foo" attribute that comes from the server to a "bar" attribute) you can overwrite either the Model
's or Collection
's parse
method, which takes the "raw" AJAX data and returns your converted version:
var someModel = new FooModel({
id: 5,
parse: function(raw) {
raw.bar = raw.foo;
delete raw.foo;
return raw;
}
});
Hope that helps.
Upvotes: 2