Why the model can not be added to collection in BackboneJS - backbone.js

window.NCollection = Backbone.Collection.extend({
url: '/api/all',
model: N,
parse: function(data){
var that = this;
_.each(data, function(item){
switch(item.cat){
case 't1':
console.log(new Note(item));
that.add(new Type1(item));
break;
case 't2':
that.add(new Type2(item));
break;
default:
that.add(new T(item));
}
});
},
nextOrder: function() {
if (!this.length) return 1;
return this.last().get('id') + 1;
},
});
I fetch the data of the collection when the page loading.
But the collection length also be 0.

And my own question, the return should be added.
parse : function(data) {
.............
return data;
},

Related

Backbone collection on save error

I'm trying to get it so that when my backbone collection is saving if it hits an error I can do something with it. However when the form is saved the Render All Tasks button's click event is always triggered. How can I get it to stop iterating the collection if an error is found? Or otherwise how can I get it to call the fail function?
var EditTaskView = AddTaskView.extend({
template: _.template($("#individualTaskEditView").html()),
events: {
"submit": function (e) {
e.preventDefault();
if (this.model.isValid(true) && this.ScheduleView.isValid() && this.ProviderView.isValid()) {
$.when(this.model.save(), this.FiltersView.saveAll())
.done(function () {
$("#RenderAllTasks").trigger("click");
})
.fail(function (xhr, status, errorMessage) {
var message = JSON.parse(xhr.responseText).Message;
RenderError(message, "#EditTaskDetailsTabError");
});
}
}
}
};
var FieldCollectionAddView = Backbone.View.extend({
tagName: "div",
render: function () {
// iterate the collection
this.collection.each(function (field) {
// render the view and append to the collection
var view = new FieldAddView({ model: field });
var rendered = view.render();
this.$el.append(rendered.el);
}, this);
return this;
},
isValid: function () {
var valid = true;
_.each(this.collection.models, function (model) {
if (!model.isValid(true)) {
valid = false;
}
});
return valid;
},
saveAll: function () {
var errorsFound = false;
_.each(this.collection.models, function (model) {
model.save(null, {
error: function (error) {
//TODO: do soemthing with the error
var message = JSON.parse(el.responseText).Message;
RenderError(message, "#ProviderDetailsTabError");
}
}, { wait: true });
});
return errorsFound;
}
});
var ProviderAddView = Backbone.View.extend({
tagName: "div",
template: _.template($("#providerAddTemplate").html()),
render: function () {
// append the template to the element
this.$el.append(this.template);
//render provider types
this.ProviderTypes = RenderProviderTypes(this.model.attributes.ProviderTypes);
var providerTypesDiv = _.template($("#ProviderTypesTemplate").html());
$("#ProviderTypesDiv", this.$el).html(providerTypesDiv);
$("#ProviderTypesSelectDiv", this.$el).html(this.ProviderTypes.render().el);
$("#ProviderTypes", this.$el).val(this.model.attributes.ProviderType);
// render field collection
var collection = new FieldCollection(this.model.attributes.ProviderFieldList);
var fieldsView = new FieldCollectionAddView({
collection: collection
});
this.FieldsAddView = fieldsView;
// append the fields to the element
$("#fieldsDiv", this.$el).append(fieldsView.render().el);
this.stickit();
return this;
},
events: {
"submit #NewProviderForm": function (e) {
e.preventDefault();
if (this.FieldsAddView.isValid()) {
var fieldsView = this.FieldsAddView;
this.model.save(null, {}, { wait: true })
.success(function (result) {
var filters = new FilterCollection();
$.when(fieldsView.saveAll(),
filters.fetch({
data: $.param({
taskId: result.attributes.TaskId
})
}))
.done(function() {
if (!$("#FiltersForm").html()) {
var view = new FilterCollectionView({ collection: filters });
assign(view, "#FilterDetails");
$("#FiltersForm").append(buttonsTemplate);
$("#FilterDetailsTab").parent("li").removeClass("disabled");
$("#FilterDetailsTab").attr("data-toggle", "tab");
}
$("#FilterDetailsTab").tab("show");
});
})
.error(function (xhr, el, other) {
var message = JSON.parse(el.responseText).Message;
RenderError(message, "#ProviderDetailsTabError");
});
}
}
},
isValid: function () {
return this.model.isValid(true) && this.FieldsAddView.isValid();
},
save: function () {
this.model.save();
this.FieldsAddView.saveAll();
}
});
Ok, so I found how this has to be done. In short the code had to be changed so that it was syncing the entire collection at once using Backbone.sync instead of iterating over it and saving each model individually. Also when you're ready to save the collection you use the $.when function so it completes the whole sync before deciding what to do. Below is the relevant code showing the changes that were made.
var FieldCollection = Backbone.Collection.extend({
url: "/api/Field",
model: FieldModel,
syncAll: function () {
return Backbone.sync("create", this)
.error(function (xhr, el, other) {
var message = JSON.parse(xhr.responseText).Message;
var tab = "";
var activeTab = $("#Tabs li.active a").attr("id");
if (activeTab === "TaskListTab") {
tab = "#EditTaskDetailsTabError";
}
else if (activeTab === "NewTaskTab") {
tab = "#ProviderDetailsTabError";
}
RenderError(message, tab);
});
}
});
var FieldCollectionAddView = Backbone.View.extend({
saveAll: function () {
return this.collection.syncAll();
}
});
var ProviderAddView = Backbone.View.extend({
events: {
"submit #NewProviderForm": function (e) {
e.preventDefault();
if (this.FieldsAddView.isValid()) {
var fieldsView = this.FieldsAddView;
$.when(fieldsView.saveAll())
.done(function() {
// success
});
}
}
}
});

Ionic: [$injector:cdep] Circular dependency found

That error occur in a .factory function. In my project I have 2 .factory that both used each other. Below is my controller:
.controller('MyCtrl', function($scope, Factory_A) {
$scope.do = function(){
Factory_A.do_A();
};
})
Scenario 1:
.factory('Factory_A', function(Factory_B){
var value_A = 1;
return{
do_A: function(){
if(value_A == 1){
value_A++;
Factory_B.do_B();
}
else{
return "Success"
}
}
}
})
.factory('Factory_B', function(Factory_A){
var value_B = 0;
var do_B_1 = function(){
Factory_A.do_A();
};
return{
do_B: function(){
value_B++;
if(value_B > 0){
do_B_1();
}
return true;
}
}
});
Error: [$injector:cdep] Circular dependency found: Factory_A <- Factory_B <- Factory_A
Scenario 2:
.factory('Factory_A', function(){
var value_A = 1;
return{
do_A: function(){
if(value_A == 1){
value_A++;
Factory_B.do_B();
}
else{
return "Success"
}
}
}
})
.factory('Factory_B', function(Factory_A){
var value_B = 0;
var do_B_1 = function(){
Factory_A.do_A();
};
return{
do_B: function(){
value_B++;
if(value_B > 0){
do_B_1();
}
return true;
}
}
});
Error: Factory_B is not defined
Scenario 3:
.factory('Factory_A', function(Factory_B){
var value_A = 1;
return{
do_A: function(){
if(value_A == 1){
value_A++;
Factory_B.do_B();
}
else{
return "Success"
}
}
}
})
.factory('Factory_B', function(){
var value_B = 0;
var do_B_1 = function(){
Factory_A.do_A();
};
return{
do_B: function(){
value_B++;
if(value_B > 0){
do_B_1();
}
return true;
}
}
});
Error: Factory_A is not defined
Here is the demo for Scenario 3 shows how I simulate my project flow using a factory. Thanks.
use $injector to inject dependency runtime on self or circular dependency.
Full example code
Change you js in your demo page
angular.module('ionicApp', ['ionic'])
.controller('MyCtrl', function($scope, Factory_A) {
$scope.do = function(){
Factory_A.do_A();
};
})
.factory('Factory_A', function($injector){
var value_A = 1;
return{
do_A: function(){
if(value_A == 1){
value_A++;
var Factory_B = $injector.get('Factory_B');
Factory_B.do_B();
}
else{
return "Success"
}
}
}
})
.factory('Factory_B', function($injector){
var value_B = 0;
var do_B_1 = function(){
var Factory_A = $injector.get('Factory_A');
Factory_A.do_A();
};
return{
do_B: function(){
value_B++;
if(value_B > 0){
do_B_1();
}
return true;
}
}
});
I assume you're asking how to avoid the circular dependency.
In cases like these you might inject the $injector instead. And then when calling the methods inside each factory, you inject them at that time or cache them after the first use.
.factory( 'FactoryA', function($injector){
return {
funcFoo: function(){
var fb = $injecto.get('FactoryB')
fb.funcBar()
}
}
})
.factory( 'FactoryB', function($injector){
return {
funcFoo: function(){
var fa = $injecto.get('FactoryA')
fa.funcFoo()
}
}
})

AngularJS check object/array is empty

I've read many questions and answers, noone has helped me. I've this function:
var model = {};
var mediaReproductionApp = angular.module("mediaReproductionApp",["ui.event",'ngAnimate']);
mediaReproductionApp.run(function ($http) {
$http.get("movimenti_per_totem.json").success(function (data) {
model.items = data;
});
});
mediaReproductionApp.controller("MediaReproductionCtrl", function($scope, $http, $timeout) {
$scope.item = model;
$scope.playVideo = function(media) {
return media ? "../gallery/video/" + media : null;
}
$scope.reproductionCodeIsEmpty = function() {
return Object.keys($scope.item).length == 0;
}
$scope.endVideo = function() {
$timeout(function() {
$http.get("php/delete_record.php").success(function () {
$http.get("movimenti_per_totem.json").success(function (data) {
$scope.item.items = data;
});
});
if($scope.reproductionCodeIsEmpty()) {
prelevaDati('../json/52.json', 'spot_creator', 'sc1', modello_SC, {});
$scope.checkMediaData();
}
},1800);
}
$scope.checkMediaData = function() {
$http.get("movimenti_per_totem.json").success(function (data) {
$scope.item.items = data;
});
if($scope.reproductionCodeIsEmpty()) {
$timeout(function(){$scope.checkMediaData();},2000);
}
}
$scope.checkMediaData();
});
This is my JSON file when it is not empty:
[ {"media":"zafferano_VP8.webm"}, {"media":"amaretti_VP8.webm"}, {"media":"passata_VP8.webm"}]
It never return true when it is empty. I've tried also:
$scope.reproductionCodeIsEmpty = function() {
return $scope.item.length == 0;
}
$scope.reproductionCodeIsEmpty = function() {
return $scope.item == {};
}
$scope.reproductionCodeIsEmpty = function() {
return angular.isUndefined($scope.item) || $scope.item === null;
}
$scope.reproductionCodeIsEmpty = function() {
return angular.isUndefined($scope.item.items) || $scope.item.items === null;
}
Nothing works... can u tell me why?
Thank you!
After you added to your question:
You define model as: model.items = data;
So, you empty model is: model = { items: [] }.
That's why it isn't empty. You need to test for model.items being empty.
If you need a tested way to tell that the object is empty, I'd recommend lodash.isEmpty(). You can use it for "any Array-like values such as arguments objects, arrays, buffers, strings, or jQuery-like collections".
https://lodash.com/docs/4.15.0#isEmpty
Since I don't know what your model is, this would cover the most possible data types.
_.isEmpty(null);
// => true
_.isEmpty(true);
// => true
_.isEmpty(1);
// => true
_.isEmpty([1, 2, 3]);
// => false
_.isEmpty({ 'a': 1 });
// => false
If you want to check if object/array is empty, I use:
angular.equals({}, yourObject);
angular.equals([], yourArray);

Downloading data to Angular from Another URL

I am new to Angular and need to download data into a service. It works fine with local json file; however, obviously you want to get the data from another URL which then gives the issue of cross domain download. Is there a way to go around this? I need to download the data from here http://files.parsetfss.com/c2e487f5-5d96-43ce-a423-3cf3f63d9c5e/tfss-31564b7d-6386-4e86-97c5-cca3ffe988f3-phones.json rather than 'phones/phones.json' below.
'use strict';
/* Services */
function makeArray(Type) {
return function(response) {
var list = [];
angular.forEach(response.data, function(data) {
list.push(new Type(data));
});
return list;
}
}
function instantiate(Type) {
return function(response) {
return new Type(response.data);
}
}
angular.module('phonecatServices', []).
factory('Phone', function($http){
var Phone = function(data){
angular.copy(data, this);
};
Phone.query = function() {
return $http.get('phones/phones.json').then(makeArray(Phone));
}
Phone.get = function(id) {
return $http.get('phones/' + id + '.json').then(instantiate(Phone));
}
// Put other business logic on Phone here
return Phone;
});
Can this be put in the following query from parse.com (how can I write the http request bit to fit into Angular.
var query = new Parse.Query("coursesParse");
query.find({
success: function(results) {
},
error: function(error) {
}
});
You can do it this way.
Phone.query = function() {
var query = new Parse.Query("test");
query.find({
success: function(results) {
//makeArray(Phone(results));
for (var i = 0; i < results.length; i++) {
var object = {
"age": results[i].get('age'),
"carrier": results[i].get('carrier'),
"id": results[i].get('id1'),
"imageUrl": results[i].get('imageUrl'),
"name": results[i].get('name'),
"snippet": results[i].get('snippet')
};
makeArray(Phone(object));
}
},
error: function(error) {
}
});
}

BackboneJS + RequireJS: Undefined is not a function

I'm trying to implement RequireJS in my project and am running into some problems. I'm getting an error is my Item model. In the model, I'm trying to set an attribute to a new collection. In that line I get the following error: "Uncaught TypeError: undefined is not a function."
My router looks something like this:
define(['backbone', 'collections/items', 'views/itemsView'], function (Backbone, Items, ItemsView) {
var Workspace = Backbone.Router.extend({
routes: {
'*path': 'nearby'
},
nearby: function() {
var items = new Items();
items.fetch();
var itemsView = new ItemsView({
collection: items
});
});
return Workspace;
});
Collection:
define(['backbone', 'models/item', 'tastypie'], function(Backbone, Item) {
var Items = Backbone.Collection.extend({
url: function() {
var url;
url = '/api/v1/nearby/?lat=' + lat1 + '&lng=' + lon1;
return url;
},
model: Item,
});
return Items;
});
Model:
define(['backbone', 'collections/locations'], function (Backbone, Locations) {
var Item = Backbone.Model.extend({
urlRoot: '/api/v1/item',
url: function () {
// stuff here
},
set: function (attrs) {
// Making a new Locations collection ordered by distance
if (attrs.items && attrs.items.length > 0) {
// Sets the collection as an attrubute
// The following line is where I get the error
attrs.locations = new Locations(attrs.items);
Backbone.Model.prototype.set.apply(this, [attrs]);
// Sets the closest attribute
attrs.closest = this.get('locations').getClosestItem().get('distance');
Backbone.Model.prototype.set.apply(this, [attrs]);
attrs.numItems = this.get('items').length;
Backbone.Model.prototype.set.apply(this, [attrs]);
}
return Backbone.Model.prototype.set.apply(this, [attrs]);
},
});
return Item;
});
Anyone know why I'm getting the error? I made sure to define to Location collection in my model.
edit: adding in the locations.js file:
define(['backbone', 'models/location'], function (Backbone, LocationModel) {
var Locations = Backbone.Collection.extend({
model: LocationModel,
comparator: function(item) {
return item.get('distance') + ' ' + item.get('name');
},
getClosestItem: function() {
return this.at(0);
}
});
return Locations;
});
Thanks for the help.

Resources