Backbone model when created already has attributes - javascript

In my my application I do something like this to create a new model,
this.model = new App.Models.Organisation;
The code for the model looks like this,
'use strict'
App.Models.Organisation = Backbone.Model.extend({
urlRoot: "http://" + App.API_ROOT + "/organisations",
defaults: {
//members : new App.Collections.Users
},
initialize: function() {
//Gets
var members = this.get('users');
var projects = this.get('projects');
var teams = this.get('teams');
var clients = this.get('clients');
console.log(members);
console.log(projects);
console.log(teams);
console.log(clients);
//Sets
if(members != undefined) {
this.set('members', App App.Collections.Users(members));
} else {
this.set('members', App App.Collections.Users);
}
if(projects != undefined) {
this.set('projects', new App.Collections.Projects(projects));
} else {
this.set('projects', new App.Collections.Projects);
}
if(teams != undefined) {
this.set('teams', new App.Collections.Teams(teams));
} else {
this.set('teams', new App.Collections.Teams);
}
if(clients != undefined) {
this.set('clients', new App.Collections.Clients(clients));
} else {
this.set('clients', new App.Collections.Clients);
}
},
validate: function() {
}
});
However when log the new model where I expect to see empty attributes I get the following:
Why would teams and projects have a value when the model is newly created?
The teams collections looks like this,
'use strict'
App.Collections.Teams = Backbone.Collection.extend({
url: 'http://' + Pops.API_ROOT + '/teams',
model: Pops.Models.Team,
initialize: function() {
var members = this.get('members');
this.set('members', new App.Collections.Users(members));
},
search: function(filterValue) {
var matcher = new RegExp(filterValue);
var found_models = this.filter(function(model) {
return matcher.test(model.get('name'));
});
return found_models;
},
});
and the projects collection like this,
App.Collections.Projects = Backbone.Collection.extend({
url: 'http://' + App.API_ROOT + '/project',
model: App.Models.Project,
sort_key: "name",
sort_order: 1,
parent_filter: false,
filters: [1,2,3],
initialize:function() {
var pm = this.get('projectmanager');
this.set('project_manager', new App.Models.User(pm));
var sp = this.get('salesperson');
this.set('sales_person', new App.Models.User(sp));
this.sortByField('created_at', 'desc');
},
comparator: function (item1, item2) {
var val1 = item1.get(this.sort_key);
var val2 = item2.get(this.sort_key);
if (typeof (val1) === "string") {
val1 = val1.toLowerCase();
val2 = val2.toString().toLowerCase();
}
var sortValue = val1 > val2 ? 1 : -1;
return sortValue * this.sort_order;
},
sortByField: function(fieldName, orderType) {
this.sort_key = fieldName;
this.sort_order = orderType == "desc" ? -1 : 1;
console.log(this.sort_order);
this.sort();
},
sortStatus: function( filters ) {
this.filters = filters;
this.each(function(project){
project.set('visible', _.contains(filters, parseInt(project.get('status'))));
});
},
myProjects: function() {
this.each(function(project){
if(project.get('user_id') == '1' && project.get('organisation_id') == null) {
project.set('visible', true);
} else {
project.set('visible', false);
}
}, this);
},
status: function( status ) {
if(this.parent_filter == false) {
//Filter all projects on the dashboard
this.each(function(project){
project.get('visible', true);
project.set('visible', project.get('status') == String(status) );
});
} else {
//Filter only projects that are currently visible
this.each(function(project) {
if(project.get('visible')) {
project.set('visible', project.get('status') == String(status) );
}
});
}
},
otherProjects: function() {
this.each(function(project){
if(project.get('organisation_id') != null) {
project.set('visible', true);
} else {
project.set('visible', false);
}
}, this);
},
sortBy: function(filterBy, orderBy) {
this.sortByField(filterBy, orderBy);
this.sort();
},
search: function(filterValue) {
var matcher = new RegExp(filterValue);
var found_models = this.filter(function(model) {
return matcher.test(model.get('name'));
});
return found_models;
},
});

I see what's going on now, in your teams collection initialize method you have this line:
this.set('members', new App.Collections.Users(members));`
So this is calling set on a collection which is different from calling set on an individual model.
On a collection set treats the first element as an array of models. You are passing 'members' as the first parameter and this adding a model to the collection with every character in the string as one attribute of that model
On a model, set expects either an attributes hash to be passed or 2 parameters attribute name and value to be passed, and will set the model attributes accordingly.
Basically you cannot treat the collection as an individual model.
If you want to keep a reference to the members from the teams collection, why not keeping a reference like this.members = new App.Collections.Users(members) that you can access from other places in the teams collection?

Related

backbone js find by collection and update

my model :-
App.bkModel = Backbone.Model.extend({});
collection :-
App.contactsCollection = Backbone.Collection.extend({
model: App.bkModel,
localStorage: new Backbone.LocalStorage("LibraryCollections"),
fetch: function(options) {
// check if localStorage for this collection exists
// (if needed, also check whether it's empty)
if(!localStorage.getItem("LibraryCollections")) {
var self = this;
$.ajax({
url: 'data.json',
}).done(function(response) {
$.each(response.items, function(i, item) {
self.create(item);
});
});
} else {
return Backbone.Collection.prototype.fetch.call(this, options);
}
}
});
var bkCollection = new App.contactsCollection;
for add and edit using addView
App.addView = Backbone.View.extend({
el: 'div.abPanel',
events: {
'submit form#frmAddContact': 'saveBook'
},
initialize: function () {
this.template = _.template(tpl.get('add-book'));
},
addBook: function (id) {
var book = {},
model = bkCollection.get(id);
//console.log(bkCollection.get(id));
if (id !== undefined && model !== undefined) {
book = model.toJSON();
}
this.$el.html(this.template({book: book}));
},
getInputValues : function () {
return {
title : $('#title').val(),
author : $('#author').val(),
quantity : $('#quantity').val(),
price : $('#price').val(),
description : $('#description').val(),
id : $('#id').val()
};
},
saveBook: function (event) {
var title = $('#title').val(),
author = $('#author').val(),
quantity = $('#quantity').val(),
price = $('#price').val(),
description = $('#description').val(),
id = $('#id').val();
if (id == '') {
bkCollection.create({title: title,quantity: quantity,author: author,price: price,description: description});
} else {
//this.collection.add(model, {at: position});
//var values = this.getInputValues();
//_.each(values, function (value, key) {
// bkCollection.set(key, value);
//});
//how to update here
}
return false;
}
});
for create working fine, how to update
update url like :- localhost/test1/#edit/cd235c1e-a153-8039-aedb-67609b5bcdef
help to solve this

How to use jQuery $.extend(obj1, obj2)

I'm trying to create a button class that extends an AbstractComponent class using $.extend() but the functions in AbstractComponent aren't available when I'm constructing the button.
The specific error I'm receiving is:
Uncaught TypeError: Object [object Object] has no method 'setOptions'
var Button = {};
var abstract = new AbstractComponent;
$.extend(Button,abstract);
//debugger;
//this.setOptions is available here
Button = function(options) {
'use strict';
var defaultOptions = {
templateName: '#button-tmpl',
title: "Label goes here",
type: "primary",
size: "medium",
disabled: null,
autosave: null,
href: null,
onclick: null
};
//debugger
//this.setOptions is not available here
this.setOptions(options, defaultOptions);
this.checkRequiredKeys('title');
return this;
};
Button.prototype.updateOptions = function() {
var options = this.options;
if (options.href === null) {
options.href = 'javascript:;';
}
if (options.disabled === null) {
options.disabled = 'disabled';
}
if (options.autosave === true) {
options.autosave = 'ping-autosave';
}
};
AbstractComponent.js
var AbstractComponent = function() {
console.log('this will be the constructor for elements extending this class');
};
AbstractComponent.prototype.show = function() {
this.render();
};
AbstractComponent.prototype.close = function() {
// stop listeners and remove this component
this.stopListening();
this.remove();
};
AbstractComponent.prototype.getTemplateName = function() {
return this.options.templateName;
};
AbstractComponent.prototype.checkRequiredKeys = function() {
var errors = new Array();
if (typeof this.getTemplateName() === "undefined") {
errors.push('templateName');
}
for (var i = 0; i < arguments.length; i++) {
if (!this.options.hasOwnProperty(arguments[i])) {
errors.push(arguments[i]);
}
}
if (errors.length > 0) {
throw new Exception("Required property(s) not found:" + errors.join(', ') + " in " + this.toString());
}
};
AbstractComponent.prototype.getElement = function() {
'use strict';
if(!this.options.updated) {
this.updateOptions();
}
return new AbstractView(this.options).render().$el;
};
AbstractComponent.prototype.updateOptions = function() {
this.options.updated = true;
return true;
};
AbstractComponent.prototype.getHtml = function() {
return this.getElement().html();
};
AbstractComponent.prototype.setOptions = function(options, defaultOptions) {
this.options = _.defaults(options, defaultOptions);
};
AbstractComponent.prototype.toString = function() {
return "Component" + this.getTemplateName() + "[id=" + this.options.id + "]";
};
jQuery extend is for moving properties from one (or more) object(s) to another object.
$.extend({}, {
foo: 10,
bar: 20
});
You should use prototypal inheritance isntead
function Button(options) {
'use strict';
var defaultOptions = {
templateName: '#button-tmpl',
title: "Label goes here",
type: "primary",
size: "medium",
disabled: null,
autosave: null,
href: null,
onclick: null
};
//debugger
//this.setOptions is not available here
this.setOptions(options, defaultOptions);
this.checkRequiredKeys('title');
return this;
};
Button.prototype = new AbstractComponent;

Updating a view in backbone when a collection is updated

I have a web app that I am building, I have form input that allows you to enter a name, on entering this name, I want to update a list with that inputted name, my problem is however that if add one name and then another the previos name that is outputted to the view, is overwritten (but if I refresh the page I get the full list). Here is my code,
GroupModalHeaderView.prototype.render = function() {
this.$el.empty();
if (this.model.isNew()) {
this.$el.append(this.template({
m: this.model.toJSON()
}));
return this.edit();
} else {
this.$el.append(this.template({
m: this.model.toJSON()
}));
this.$el.find(".modal-header-menu").show();
return this.$el.find(".icon-button-close-modal").show();
}
};
GroupModalHeaderView.prototype.save = function(e) {
var $collection, $this;
if (e) {
e.preventDefault();
}
$this = this;
if (this.$("#group-name").val() !== "") {
$collection = this.collection;
if (this.model.isNew()) {
this.collection.push(this.model);
}
return this.model.save({
name: this.$("#group-name").val(),
async: false,
wait: true
}, {
success: function() {
return $this.cancel();
}
});
}
};
GroupListView.prototype.events = {
"click .list-header-add": "add",
"click .list-header-expander": "showHide",
"keyup #search-query": "keyup"
};
GroupListView.prototype.initialize = function() {
//console.log("fired");
this.collection.on("change", this.renderList, this);
this.collection.on("reset", this.render, this);
return this.renderList();
};
GroupListView.prototype.renderList = function(collection) {
var responsiveHeight = $("body").height() - 400;
if($("#people-network-requests").is(":visible")) {
this.$el.find("#people-groups-list").height($("#people-people-list").height()-250+"px");
} else {
this.$el.find("#people-groups-list").height($("#people-people-list").height()+"px");
}
var $collection, $this;
if (!collection) {
collection = this.collection;
}
this.$el.find(".list-items").empty();
$this = this.$el.find("#people-groups-list");
this.$el.find(".list-items").removeClass("list-items-loading").empty();
$collection = collection;
if ($collection.length < 1) {
/*this.$el.find("#people-groups-inner").hide();
$(".activity-no-show").remove();
return this.$el.find("#people-groups-inner").append('<div class="activity-no-show">\
<p>To add a new group, click the + in the top right hand corner to get started.</p>\
</div>');*/
} else {
this.collection.each(function(item) {
var displayView;
displayView = new app.GroupListDisplayView({
model: item,
collection: $collection
});
console.log($this);
return $this.append(displayView.render());
});
return this;
}
};
return GroupListView;
})(app.BaseView);
GroupListDisplayView.prototype.render = function() {
//console.log(this.$el);
//alert("1");
var $body;
this.$el.html(this.template({
m: this.model.toJSON()
}));
$body = this.$el.find(".card-body");
$text = $body.text();
$.each(this.model.get("people"), function(i, person) {
var personTile;
this.person = new app.Person({
id: person.id,
avatar: person.avatar,
first_name: person.first_name,
last_name: person.last_name
});
personTile = new app.PersonTileView({
model: this.person
});
if(person.id) {
$body.append(personTile.render()).find(".instruction").remove();
}
});
return this.$el.attr("id", "group-card-" + this.model.id);
};
GroupListView.prototype.keyup = function() {
this.filtered = $collection.searchName(this.$el.find("#search-query").val());
//console.log(this.filtered);
return this.renderList(this.filtered);
};
this.collection.on("add", this.addDisplayView, this);
Then create a function addDisplayView that accepts the model for the view. You will need to refactor the this.collection.each(function(item)... part of your code to use the addDisplayView function.
GroupListView.prototype.addDisplayView = function(model){
var displayView = new app.GroupListDisplayView({
model: model,
collection: this.collection
});
// use this.$, as it is already mapped to the context of the view
return this.$("#people-groups-list").append(displayView.render());
}
You should also change this.collection.push(this.model); to this.collection.add(this.model);
addcollection.add(models, [options])
Add a model (or an array of models) to the collection, firing an "add" event. If a model property
is defined, you may also pass raw attributes objects, and have them be
vivified as instances of the model. Pass {at: index} to splice the
model into the collection at the specified index. If you're adding
models to the collection that are already in the collection, they'll
be ignored, unless you pass {merge: true}, in which case their
attributes will be merged into the corresponding models, firing any
appropriate "change" events.
http://documentcloud.github.io/backbone/#Collection-add

Constructing fuelux datagrid datasource with custom backbone collection

I am trying to build datagrid with sorting, searching and paging enabled. Therefore, I am using fuelux-datagrid.
MY backbone view looks like this:
var app = app || {};
$(function ($) {
'use strict';
// The Players view
// ---------------
app.PlayersView = Backbone.View.extend({
template: _.template( $("#player-template").html() ),
initialize: function () {
if(this.collection){
this.collection.fetch();
}
this.listenTo(this.collection, 'all', this.render);
},
render: function () {
this.$el.html( this.template );
var dataSource = new StaticDataSource({
columns: [
{
property: 'playername',
label: 'Name',
sortable: true
},
{
property: 'age',
label: 'A',
sortable: true
}
],
data: this.collection.toJSON(),
delay: 250
});
$('#MyGrid').datagrid({
dataSource: dataSource,
stretchHeight: true
});
}
});
});
The player template just contain the template as given in fuelux datagrid . My routing code somewhere instantiate app.playerview with collection as
new app.PlayersView({
collection : new app.PlayersCollection
}));
My players collection contains list of player model as below
[{
"id":1,
"playername":"rahu",
"age":13
},
{
"id":2,
"playername":"sahul",
"age":18
},
{
"id":3,
"playername":"ahul",
"age":19
}]
My datasource class/function to construct datasoruce with columns and data method is as given in datasource constructor
However, I get the error the " datasource in not defined ". Can anybody help me?
I just wanted to hack the code so that instead of datasource constructed from local data.js in given example, I want to construct the datasource so that it takes data from playercollection.
Also, how to add the one extra column so that we can put edit tag insdie and its should be able to edit the particular row model on clicking that edit.
I have been stucking around these a lot. It would be great help to figure out the answer.
I was stucking around datasource.
I modified the datasource as follows and then it worked.
var StaticDataSource = function (options) {
this._formatter = options.formatter;
this._columns = options.columns;
this._delay = options.delay || 0;
this._data = options.data;
};
StaticDataSource.prototype = {
columns: function () {
return this._columns;
},
data: function (options, callback) {
var self = this;
setTimeout(function () {
var data = $.extend(true, [], self._data);
// SEARCHING
if (options.search) {
data = _.filter(data, function (item) {
var match = false;
_.each(item, function (prop) {
if (_.isString(prop) || _.isFinite(prop)) {
if (prop.toString().toLowerCase().indexOf(options.search.toLowerCase()) !== -1) match = true;
}
});
return match;
});
}
// FILTERING
if (options.filter) {
data = _.filter(data, function (item) {
switch(options.filter.value) {
case 'lt5m':
if(item.population < 5000000) return true;
break;
case 'gte5m':
if(item.population >= 5000000) return true;
break;
default:
return true;
break;
}
});
}
var count = data.length;
// SORTING
if (options.sortProperty) {
data = _.sortBy(data, options.sortProperty);
if (options.sortDirection === 'desc') data.reverse();
}
// PAGING
var startIndex = options.pageIndex * options.pageSize;
var endIndex = startIndex + options.pageSize;
var end = (endIndex > count) ? count : endIndex;
var pages = Math.ceil(count / options.pageSize);
var page = options.pageIndex + 1;
var start = startIndex + 1;
data = data.slice(startIndex, endIndex);
if (self._formatter) self._formatter(data);
callback({ data: data, start: start, end: end, count: count, pages: pages, page: page });
}, this._delay)
}
};
Infact, I just removed following code and its associated braces.
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['underscore'], factory);
} else {
root.StaticDataSource = factory();
}
}(this, function () {
I dont know what exactly the above code is doing an what dependdencies they have over.

Backbone.js not saving to database

I am running Django + Backbone.js and .save() has no effect. What am I doing wrong? This is my backbone javascript code. I am trying to implement a prioritized to do list and I cannot figure out how to POST back to the server. Chromium isn't even seeing an attempted post when I try:
T = new Task();
T.save();
In the console.
//$(function() {
/**
* Model: Task
* name, date, importance
*/
window.Task = Backbone.Model.extend({
urlRoot: '/api/v1/task/',
initialize: function() {
console.log("New task: " + JSON.stringify(this.toJSON()));
}
, defaults: function() {
return {
date: new Date()
, name: "New event"
, importance: 0
};
}
});
/**
* Collections: Calendar
*/
window.Calendar = Backbone.Collection.extend({
//urlRoot: '/api/v1/calendar',
initialize: function() {
console.log("New calendar: " + JSON.stringify(this.toJSON()));
}
, model: Task
, comparator: function(task) {
return task.get("date");
}
/*
, before: function(thresholdDate) {
return this.filter( function(task) {
task.get('date') < thresholdDate;
});
}
*/
});
window.TaskView = Backbone.View.extend({
tagName: "li"
});
now = new Date();
Day = Backbone.Collection.extend({
model: Task,
url: '/api/v1/task/?format=json&calendar__id=1&date='+ now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate(),
parse: function(response) {
return response.objects;
},
comparator: function(task){
return task.get('priority');}
});
Month = Backbone.Collection.extend({
model: Task,
url: 'api/v1/task/?format=json&date__month='+(now.getMonth()+1),
parse: function(response){
return response.objects;
},
comparator: function(task){
return task.get('priority');}
});
Year = Backbone.Collection.extend({
model: Task,
url: 'api/v1/task/?format=json&date__year='+now.getFullYear(),
parse: function(response){
return response.objects;
},
comparator: function(task){
return task.get('priority');}
});
// required for saving
Backbone.sync = function(method, model) {
console.log(method + ": " + JSON.stringify(model));
model.id = 1;
};
$.fn.serializeObject = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
$(function() {
$('form').submit(function() {
var dict = $('form').serializeObject();
var new_task = new Backbone.Model({
date: toString(dict.date),
name: toString(dict.name),
priority: toString(dict.priority)});
console.log("new_task =" + new_task);
new_task.save();
console.log(dict);
return false;
});
});
TaskView = Backbone.View.extend({
el: $("div#app"),
render: function() {
$(thi.el).html(this.template(this.model.toJSON()));
}
});
//});
You have overridden the Backbone.sync method to only log a console message.
If you override Backbone.sync then you need to manually perform the save logic within that method.
So either delete the code where you override Backbone.sync or add the ajax call within that code to perform the save.

Categories

Resources