I need to have two url properties inside my Backbone.Collection.extend() because if a collection is fetched then I need to use a specific url if the collection gets a new model then I want to change the url
module.exports = MessagesCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
if (fetch method is called) {
return '/api/messages/' + this.id;
} else {
// here if a model is being added?
return '/api/messages'
}
},
model: MessageModel
});
The reason for this is because I only want to pull down the models from the server based on the user.
var me = new MeModel();
me.fetch({
success: function(response) {
App.data.me = me;
var messages = new MessagesCollection([], { id: response.get('user_id') });
messages.fetch({
success: function() {
App.data.messages = messages;
App.core.vent.trigger('app:start');
}
});
}
});
When the user creates a new model within the app I want it to go into the main collection?
Does this mean I should create a sub collection based on the main collection somehow?
Edit:
My create looks like this somewhere else in the app window.App.data.messages.create(Message); I am thinking maybe I could write something like
var me = new MeModel();
me.fetch({
success: function(response) {
App.data.me = me;
var messages = new MessagesCollection([], { id: response.get('user_id') });
var allMessages = new MessagesCollection();
messages.fetch({
success: function() {
App.data.messages = messages;
App.data.allMessages = allMessages;
App.core.vent.trigger('app:start');
}
});
}
});
Then create window.App.data.allMessages.create(Message);> It sounds like it can cause problems IDK any ideas?
Edit:
The above worked but I had to create a new Backbone.Collection.extend() passing the same model but just writing it like
var Backbone = require('backbone'),
MessageModel = require('../models/message');
module.exports = AllMessagesCollection = Backbone.Collection.extend({
model: MessageModel,
url: '/api/messages'
});
So let me really break this question down, is this solution problematic. What is the best way to do this? The worst thing I can think of is bandwidth, using this method I would constantly be sending requests!
If you need to use different url only when create new model you can override collection.create method:
var MessagesCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
return '/api/messages/' + this.id;
},
create: function(model, options){
var extendedOptions = _.extend(options || {}, {url: '/api/messages'});
return this.constructor.__super__.create.call(this, model, extendedOptions);
}
});
Related
How can I load different attributes for a model using different URLs?
E.g. I have 3 different and independent URLs that will return some attributes and I want to add all of those to a single model, suppose that I have the promise returned by all of them like this:
var model = //...
$.when(nameAttributes, addressAttributes, metaAttributes).then(
function(nameData, addressData, metaData) {
return _.extend({}, nameData, addressData, metaData);
})
.done(function(allData) {
model.set(allData);
doStuffWith(model);
});
Is there a way to turn that into this:
model.fetch().done(function(){ doStuffWith(model); });
Well.. Ok, this should do what you want. I STRONGLY suggest you do NOT take this route. Keep the data separately in individual Model()s that way you can update it and pull it when ever you need to. If you take this approach saving data will be a disaster.
http://jsfiddle.net/kjhvwxg4/
PS. I tried to not add any more code then i had to, keep in mind this will probably not handle error handling very well since xhr will never throw an error, you need to catch it yourself -- i didnt want to spend the time coding that since this is just a proof of concept.
var Model1 = Backbone.Model.extend({
fetch: function(options) {
options = _.extend({
parse: true
}, options);
var model = this;
var success = options.success;
var error = options.error;
options.success = function(resp) {
var serverAttrs = options.parse ? model.parse(resp, options) : resp;
if (!model.set(serverAttrs, options)) return false;
if (success) success.call(options.context, model, resp, options);
model.trigger('sync', model, resp, options);
};
options.error = function(resp) {
if (error) error.call(options.context, model, resp, options);
model.trigger('error', model, resp, options);
};
// custom code starts here
var call1 = $.getJSON('http://jsonplaceholder.typicode.com/posts/1');
var call2 = $.getJSON('http://jsonplaceholder.typicode.com/comments/2');
var call3 = $.getJSON('http://jsonplaceholder.typicode.com/albums/3');
var xhr = $.when(call1, call2, call3);
// mimics the same triggers the normal backbone does
model.trigger('request', model, xhr, options);
xhr.done(function(one, two, three){
var resp = _.extend(one[0], _.extend(two[0], _.extend(three[0], {})));
options.success(resp);
});
// we still need to send back an event handler
return xhr;
}
});
var model = new Model1();
model.fetch();
model.on('sync', function(model) {
alert(JSON.stringify(model.toJSON()));
});
My data model is consisting of two objects; project and task.
I load my data from the db via json and MVC-services and map my observableArrays like this:
viewModel = function () {
var self = this;
// some code...
// projects
self.Projects = ko.observableArray();
var mappedProjects = [];
$.ajax({
url: "myService/GetProjectsByUserId",
data: "userID=" + meID,
dataType: 'json',
async: false,
success: function (allData) {
mappedProjects = $.map(allData, function (item) {
return new Project(item);
});
}
});
self.Projects(mappedProjects);
// tasks
self.Tasks = ko.observableArray();
var mappedTasks = [];
$.ajax({
url: "myService/GetTasksByUserID",
data: "userid=" + meID,
dataType: 'json',
async: false,
success: function (allData) {
mappedTasks = $.map(allData, function (item) {
return new Task(item, self.Projects); // is there a smarter way to access self.Projects from the Scene prototype?
//return new Task(item);
});
}
});
self.Tasks(mappedTasks);
//some more code...
};
where
Project = function (data) {
this.projectID = data.projectID;
this.type = ko.observable(data.type);
};
Task = function (data, projects) {
this.taskID = data.taskID;
this.projectID = data.projectID;
//this.projecttype = ??? simpler solution?
this.projecttype = ko.computed(function () { // Is there a simpler way to access 'viewModel.Projects' from within 'Task'?
var project = ko.utils.arrayFirst(projects, function (p) {
return p.projectID === self.projectID;
});
if (!project) {
return null;
}
else {
return project.headerType();
}
});
};
The thing is (as you see) I want to access the projectType inside the Task-object. Is there a simpler way to do this than instantiating the object with the self.Projects as input?
Could self.Projects be bound when defined in some way so I could access it via the DOM?
From your comments, it looks like that you have multiple view models dependent on Task and Project objects. For decoupling between components, i would say to use ko.postbox plugin. You can easily have synchronization between viewmodels and non-knockout components using publishOn and subscribeTo extensions.
So your Task object will subscribe to Projects observableArray in viewModel like
Task = function (data) {
this.taskID = data.taskID;
this.projectID = data.projectID;
var projects = ko.observableArray().subscribeTo("projectsLoaded",true);
//this.projecttype = ??? simpler solution?
this.projecttype = ko.computed(function () { // Is there a simpler way to access 'viewModel.Projects' from within 'Task'?
var project = ko.utils.arrayFirst(projects(), function (p) {
return p.projectID === self.projectID;
});
if (!project) {
return null;
}
else {
return project.headerType();
}
});
};
and in your viewModel, you just have to make Projects observable array publish "projectsLoaded" topic/event. Like
viewModel = function () {
var self = this;
// some code...
// projects
self.Projects = ko.observableArray().publishOn("projectsLoaded");
// ....
}
Whenever the projects array changes in viewModel, you will always have the latest value in Task project array.
JsFiddle: http://jsfiddle.net/newuserjs/wffug341/3/
I ve created a backbone model, which fetch json from a server. However, i want to update view with the new data, in specific time interval, not every time that the server sends data. What should i use with purpose to update backbone view every n milliseconds? I ve got the above code.
$(function() {
var Profile = Backbone.Model.extend();
var ProfileList = Backbone.Collection.extend({
model: Profile,
url: 'data.php'
});
var ProfileView = Backbone.View.extend({
el: "#profiles",
template: _.template($('#profileTemplate').html()),
render: function(eventName) {
_.each(this.model.models, function(profile){
var profileTemplate = this.template(profile.toJSON());
$(this.el).append(profileTemplate);
}, this);
return this;
}
});
var profiles = new ProfileList();
var profilesView = new ProfileView({model: profiles});
profiles.fetch({reset: true});
//profiles.bind('reset', function () { console.log(profiles); });
profiles.bind('reset', function () {
profilesView.render();
});
});
A simple solution would be:
profiles.fetch({reset: true});
setInterval(
function() {
profiles.fetch({reset: true});
}, 1000 // Time in milliseconds
);
I wouldn't say that it's a beautiful solution but I hope you get the idea. As far as I know there is no interval fetch, or something similar, implemented in Backbone - so you pretty much have to build your own.
EDIT
This is probably a better solution, I like it more atleast.
var ProfileList = Backbone.Collection.extend({
model : Profile,
url : "data.php",
xhr : null,
interval: null,
fetchWithInterval: function(options) {
var options = options || {},
self = this;
this.interval = setInterval(function(){
if(self.xhr !== null && self.xhr.readyState !== 4) {
return;
}
self.xhr = self.constructor.__super__.fetch.apply(self, options);
}, 1000);
this.xhr = self.constructor.__super__.fetch.apply(self, options);
},
stopFetchWithInterval: function() {
clearInterval(this.interval);
}
});
Use it with profiles.fetchWithInterval({reset: true}); and you can stop it with profiles.stopFetchWithInterval().
It also manages the xhr, so if the AJAX call isn't finished it will not start a new one. This is pretty handy if you want to fetch with a small interval or when your API is slow for some reason.
I try to fetch this server: http://cshosting.webfactional.com/api/v1/projects/?format=json
to a backbone.js collection.
Then I try to console.log it, but it's not working.
It is very important to me, please help.
NEWS: I figured out it's something with JSONP. will be glad to hear more information about that. thanks.
this is parts of my code in short:
window.ProjectList = Backbone.Collection.extend({
model: Project,
url:"http://cshosting.webfactional.com/api/v1/projects",
parse: function(response) {
return response.objects;
}
});
another part:
window.HomeView = Backbone.View.extend({
initialize:function () {
this.projectList = new ProjectList();
this.projectList.fetch({success : function() {console.log(this.projectList); }});
this.homeListView = new HomeListView({model: this.projectList});
}
});
The this on the fetch callback is not going to refer to your HomeView instance. Try using another variable to ensure you are referencing the desired object.
initialize:function () {
var self = this;
this.projectList = new ProjectList();
this.projectList.fetch({success : function() {console.log(self.projectList); }});
this.homeListView = new HomeListView({model: this.projectList});
}
If that doesn't solve the problem, please describe what happens. Use the webkit inspector's network tab to make sure the correct GET request and response are being called. Make sure your parse function is being called and the response object is what you expect.
It seems like you would want to do something more like this:
window.Project = Backbone.Model.extend({
url:"http://cshosting.webfactional.com/api/v1/projects/?format=json"
});
window.ProjectList = Backbone.Collection.extend({
model: Project,
url:"http://cshosting.webfactional.com/api/v1/projects/?format=json"
});
window.HomeView = Backbone.View.extend({
initialize:function () {
_.bindAll(this, 'render');
},
render: function(){
var self = this;
console.log(self.collection);
return this;
}
});
var project = new Project();
var collection = new ProjectList();
collection.fetch({
success: function(result_collection, resp) {
var view = new HomeView ({ collection: result_collection });
view.render();
}
});
I have some code like this:
var User = function () {
_.bindAll(this)
this.UserList = Backbone.Collection.extend({
model: UserModel
// ...
});
this.UserListView = Backbone.View.extend({
// ... etc
}
User.prototype.display = function() {
var self = this
self.collection = new self.UserList
self.collection.fetch({
success: function(collection, response) {
self.users = new self.UserListView({
collection: self.collection
});
}
})
};
var user = new User()
route("users", function () {
user.display()
})
My question is, is this going to cause memory issues? Every time the user arrives on the user/:userPage route the view, collection, etc are all going to be recreated. Will the old ones be deleted or do I have to delete it manually?
Should I be doing this:
User.prototype.display = function() {
var self = this
delete self.collection
self.collection = new self.UserList
self.collection.fetch({
success: function(collection, response) {
delete self.users
self.users = new self.UserListView({
collection: self.collection
});
}
})
};
Also other general advice on my example code is appreciated too.
This is utterly useless:
delete self.collection
self.collection = new self.UserList
All delete does is remove collection field from the self object, and then you immediately recreate it. Just do the assignment.
I think that you think that delete self.collection should somehow cause the garbage-collection of the object pointed to by the field. It doesn't.
Also, don't use self as a variable name. The browser uses it to mean, uh, something.