Backbone.js How to retrieve a model in a nested collection? - backbone.js

I have a PizzaType model that has a nested collection of Pizzas. The Pizzas Collection is listed based on the Pizza Type. I would like to be able to click on a pizza in the pizzas collection and display its attributes.
What would be the best way to set the url params dynamically?
The url does not need a route to navigate to for bookmarking and sharing, just to retrieve the specific resource.
I have it so that if someone wants to view the pizza type the url is pizza_type/:id
:id is the id belonging to the Pizza Type (parent model)
I currently have it so if a pizza is clicked on in the Pizzas Collection (that belongs to the Pizza Type Model), the path to the pizza resource is not followed; just a region on the page is updated. The url path is needed so jQuery can get the resource to update that region. The url to the pizza is pizza_types/:pizza_type_id/pizzas/:id Here, the :id is the id belonging to the Pizza Model, and the :pizza_type_id is the foreign key that members of the Pizzas Collection share to group them into the collection, that belong to the Pizzas Type Model.
When I click on the pizza (id = 3), I get "NetworkError: 404 Not Found - http://localhost:3000/pizza_types/3/pizzas"
Here is the Model and Collection Code:
#Pizzeria.module "Entities", (Entities, App, Backbone, Marionette, $, _) ->
class Entities.PizzaType extends Backbone.Model
urlRoot: "pizza_types/"
# creates the nested collection
initialize: ->
#pizzas = new Entities.PizzasCollection
#pizzas.url = #urlRoot + #id + '/pizzas'
#pizzas.fetch
reset: true
parse: (response) ->
response
class Entities.PizzaTypesCollection extends Backbone.Collection
model: Entities.PizzaType
url: 'pizza_types'
parse: (response) ->
response
# Is there a way to pass in a :pizza_type_id and :id params to pass to the url() so
# that the specific pizza model can be retrieved from the collection?
class Entities.Pizza extends Backbone.Model
url: -> "pizza_types/" + 2 + "/pizzas/" + 4 # <-- Hard coded works, but how to set the params dynamically?
parse: (data) ->
data
class Entities.PizzasCollection extends Backbone.Collection
model: Entities.Pizza
url: 'pizzas'
parse: (data) ->
data
Any suggestions? Is this the proper way, I tried to do this as well:
class Entities.Pizza extends Backbone.Model
urlRoot: -> "pizza_types"
# I thought I could pass these params in and fetch the correct pizza model, but not working.
fetch
pizza_type_id: pizza_type_id
id: id
reset: true
parse: (data) ->
data
PizzaType Attributes with example data:
PizzaType: {
id: 2,
name: "Gourmet",
pizzas: [
0: {
id: 4,
pizza_type_id: 2
name: "gourmet pizza 1"
},
1: {
id: 5,
pizza_type_id: 2,
name: "gourmet pizza 2"
}
]

For url in pizza model you can specify an attribute like pizza_type for model in initialize function and change url function like this
class Entities.Pizza extends Backbone.Model
initialize: (options)->
#pizza_type = options.pizza_type if options.pizza_type
url: ->
"pizza_types/" + #pizza_type + "/pizzas/" + #id
parse: (data) ->
data
class Entities.PizzaType extends Backbone.Model
urlRoot: "pizza_types/"
url: ->
#urlRoot+#id+'/pizzas' if #id
initialize: ->
#pizzas = new Entities.PizzasCollection
#pizzas.fetch reset: true
parse: (response) -> response
In PizzasCollection add addOptions so when adding models to collection then backbone add with this default options
class Entities.PizzasCollection extends Backbone.Collection
model: Entities.Pizza
addOptions:
'pizza_type': #id
url: 'pizzas'
parse: (data) -> data
"NetworkError: 404 Not Found -
http://localhost:3000/pizza_types/3/pizzas"
This is a problem with your server, maybe mistyping, or trailing-slash problems or any server problems.
P.S : I recommend using a relational model (with any plugin can do that like BackboneRelationals)

Related

How to fetch and console json date on Backbone.js

I am trying to pull JSON data and display on my console just to see if its working. I am seeing an error message.
I am trying to fetch a Model with id 1:
var TeamModel = Backbone.Model.extend({
urlRoot: '/json/team.json'
});
//Create Team Model Instance with id of team
var teamModel = new TeamModel({
id: 1
});
//Fetch the json data
teamModel.fetch();
var director = teamModel.get('name');
Here is the JSON file :
{
"id" : 1,
"name" : "Sam",
"img_small" : "images/sam.jpg",
"location" : "NYC",
"role" : "Director, Producer, & Writer",
}
This yields the following error :
GET http://localhost:9000/json/team.json/1 404 (Not Found)
You should use url, not urlRoot:
var TeamModel = Backbone.Model.extend({
url: '/json/team.json'
});
Backbone uses urlRoot to generate resource URLs based on the operation you perform (fetch, save, delete) and Model id. When you fetch a single Model, the URL it generates is urlRoot + '/' + id.
Therefore when you attempt to fetch a Model with id 1, the constructed URL is /json/team.json/1
If you set url, however, Backbone always uses that url. It does not change it based on operation or model attributes. This is the behaviour that you need, because you have a single static resource.

add and remove existing models to a has_many relationship using nested attributes

In a rails 4 application I have two models :
class Gallery
include Mongoid::Document
has_many :thumbnails
end
class Thumbnail
include Mongoid::Document
belongs_to :gallery
end
I populated the mongodb database with a bunch of galleries with thumbnails in them and some unused thumbnails (with a nil gallery_id).
Now on the client side I use Marionette with backbone-associations and I represent the Gallery as so :
class Entities.Gallery extends Backbone.AssociatedModel
idAttribute: '_id'
urlRoot: '/galleries'
paramRoot: 'gallery'
relations: [
type: Backbone.Many
key: 'thumbnails'
remoteKey: 'thumbnails_attributes'
relatedModel: -> Entities.Thumbnail
]
initialize: ->
#on 'add:thumbnails', (thumbnail) => thumbnail.set 'gallery_id', #get('_id')
class Entities.Thumbnail extends Backbone.AssociatedModel
idAttribute: '_id'
But I also have a collection of unused thumbnails :
class Entities.UnusedThumbnails extends Backbone.Collection
model: Entities.Thumbnail
initialize: ->
#on 'add', (thumbnail) -> thumbnail.set 'gallery_id', null
I can move thumbnails around between the gallery and the UnusedThumbnails collection just fine, but how do I persists them ?
If I just add a thumbnail from the UnusedThumbnails collection to the gallery thumbnails and save the gallery using :
gallery.save([], patch: true)
i get a 404 response saying "Document(s) not found for class Thumbnail with id(s) ..." which make sense since rails only search for a thumbnail with this id inside the gallery.
Same for removing thumbnails from the gallery, if I post the gallery with missing thumbnails the rails update method will just assume these thumbnails are unchanged.
Do I need to save each added / removed thumbnails separately?
What's the proper way to do this ?
Edit:
I realize I'll probably need to create a specialized update action, like update_strict (for lack of a better name)
def update_strict
new_ids = gallery_params[:thumbnails_attributes].map(&:_id)
existing_ids = #gallery.thumbnails_ids
ids_to_add = new_ids - existing_ids
ids_to_remove = existing_ids - new_ids
#gallery.thumbnails.find(ids_to_remove).each |thumbnail| do
thumbnail.gallery = nil
thumbnail.save
end
ids_to_add.each |id| do
thumbnail = Thumbnail.find(id)
thumbnail_params = (gallery_params[:thumbnails_attributes].select { |t| t._id == id })[0]
thumbnail.update(thumbnail_params)
end
gallery_params[:thumbnails_attributes].delete_if { |thumbnail| ids_to_add.include?(thumbnail._id) }
respond_to do |format|
if #gallery.update(gallery_params)
format.html { redirect_to #gallery, notice: 'Gallery was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
But is there a proper, cleaner way ?
I finally ended up overwriting thumbnails_attributes= in the Gallery model :
def thumbnails_attributes=thumbnails_attributes)
ids = thumbnails_attributes.map { |t| t['id'] }
(ids - thumbnail_ids).each do |id|
thumbnail = Thumbnail.find id
thumbnails << thumbnail
end
super(thumbnails_attributes)
end
This allow me to add existing thumbnails to the gallery.
The simplest way to allow their removal was to switch to backbone-nested-attributes which add a _destroy attribute to destroyed models.

Fetch Subset of Backbone Collection with Unique Model URL (Code & Live Example Inside)

I'm looking to fetch a subset of a backbone collection using the model's URL.
Similar to the tutorial of "Nested Collections" found at:
http://liquidmedia.org/blog/2011/01/backbone-js-part-1/
Since this requires data api, models, collections, views i've included my full code of what Im trying to create:
Code (In Node/Mongoose/Express/Rest): https://github.com/1manStartup/Backbone-URLRoot
Live Example (on my ec2): http://54.251.104.185/
I believe my problem lies somewhere with the rendering of views and fetching of model data.
What Im Trying To Do: Render all Articles Unique to Dog. Route Is:
/api/makearticle/:id
If you don't use NodeJs, my JSON Api looks like this:
{
"articlename": "feed chocolate",
"_dog": {
"name": "pitbull",
"_id": "51d0b9ad6fd59c5059000002"
},
"_id": "51d0c22a6fd59c5059000007",
"__v": 0
},
{
"articlename": "took to vet",
"_dog": {
"name": "chihuaha",
"_id": "51d0b9af6fd59c5059000003"
},
"_id": "51d0c22e6fd59c5059000008",
"__v": 0
},
Here are my models and Collections:
Rest of backbone code found at:
https://github.com/1manStartup/Backbone-URLRoot
https://github.com/1manStartup/Backbone-URLRoot/blob/master/public/javascripts/demo-routerurl.js
Dog = Backbone.Model.extend({
idAttribute: '_id',
urlRoot:'/api/makedog' ,
initialize:function () {
this.dogs = new Dogs();
this.dogs.url = this.urlRoot + "/" + this.id ;
}
});
Dogs = Backbone.Collection.extend({
model: Dog,
url: '/api/makedog'
});
Article = Backbone.Model.extend({
idAttribute: '_id',
urlRoot:'/api/makearticle' ,
initialize:function () {
this.merr = new Articles;
this.merr.url = this.urlRoot + "/" + this.id ;
}
});
Please Help ive been on this for several days. Thanks.
Your API doesn't seem RESTful, so you're probably causing yourself more trouble than necessary: Backbone expects a RESTful API by default.
In any case, you need to do something like
var Article = Backbone.Model.extend({
idAttribute: '_id'
});
var ArticleCollection = Backbone.Collection.extend({
model: Article
});
var Dog = Backbone.Model.extend({
idAttribute: '_id',
initialize:function () {
this.articles = new ArticleCollection({
url: "api/dogs/" + this.get('id');
});
}
});
Then, you can do (e.g.):
var doggy = new Dog({id: 51d0b9ad6fd59c5059000002});
doggy.articles.fetch();
var articlesForDoggy = doggy.articles;
As a side node, why are you creating a new collection of dogs each time you instanciate a new dog model instance?
Also to be noted: I would seriously rething your API design. If you're using different routes for creating models (which seems to be indicated by the 'make...' routes), you're going to have to write a whole bunch of code to get the Backbone persistence layer to work with your API.
In addition, this API doesn't really follow conventions: api/dogs/XXX shouldn't be returning a list of articles: it should be returning the dog instance with id XXX. Using a URL like api/dogs/XXX/articles would make a lot more sense for the list of articles associated with a dog.
Friendly advice: although the code above should get you on your way, you really should rethink how you're designing your API. Using a non-RESTful API with Backbone, coupled with non-standard naming conventions will basically guarantee you'll be entering a world of pain with your project.

Backbone Collection URL is Undefined

trying to pass the url to the fetchFromURL function but URL is undefined when loggin it. NO IDEA why this is happening!!
class Device extends Backbone.Model
class Devices extends Backbone.Collection
model: Device
localStorage: new Backbone.LocalStorage 'devices'
url: "http://192.168.2.17:8030/profile/RestProfile.svc/GetDeviceList/"
fetchFromURL: =>
collection = #
console.log #url
getView = =>
devices = new Devices
devices.fetch()
devices.fetchFromURL()

Backbone.js set URL parameters in model & use it with fetch

I would like to fetch model from specific url with parameter:
url: server/somecontroller/id/?type=gift
Simple working way is:
collection.fetch({ data: { type: 'gift'} });
But I want to set it in model:
...
if(id){
App.coupon = new AffiliatesApp.Coupon({id: id});
} else {
App.coupon = new AffiliatesApp.Coupon({id: 'somecontroller'}, {type: 'gift'});
}
App.coupon.fetch();
How can I achieve it?
The easiest way to achieve this is to override Backbone's url method on the Coupon model with one defined by you. For example you can do :
Affiliates.Coupon = Backbone.Model.extend({
urlRoot : "server/somecontroller/",
url : function(){
var url = this.urlRoot + this.id;
if(this.get("type")){
url = url + "/?type=" + this.get("type");
}
return url;
}
});
This solution is easy to implement but has a drawback: the generated URL will be used for every action that syncs to the server (fetch,save,..).
If you need to have a finer control over the generation of the URL depending on what action you are doing you will need to override Backbone's Sync method for your model.
It can be done by overriding the fetch method in model to use some custom data. Using CoffeeScript it could look like this:
class AffiliatesApp.Coupon extends Backbone.Model
fetch: ->
super(data: { type: #get('type') })
Note that this example will ignore any attributes passed to coupon.fetch(), however it can be easily adjusted for any override logic.

Resources