backbone js getByCid not working - backbone.js

The backbone getByCid is not working when i viewed in console.log it showing the error as TypeError: test.getByCid is not a function. By using at(0).cid it shows the output as 'c1' why its not working.
var test=new Backbone.Collection([
{name:'gowtham',age:10}
]);
console.log(test.at(0).cid); // output as c1
console.log(JSON.stringify(test.getByCid('c1'))); //TypeError: test.getByCid is not a function

the Backbone.Collection does not have method getByCid, thats why you are getting this error.
But a collection has get method for getting a model.
it accepts one argument: model | id | cid
So if you want to get a model by cid, you can do it like this collection.get(cidValue)
let collection = new Backbone.Collection();
let test1 = new Backbone.Model({ id: 1, value: 'first' });
let test2 = new Backbone.Model({ id: 2, value: 'second' });
collection.add([test1, test2]);
console.log(collection.get(2));
// find test2 by id
console.log(collection.get('c2'));
// find test2 by cid
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone.js"></script>

Related

Group objects by field, then group by a conditional field

I have a list of objects that I'm using an ng-repeat to display.
I need to be be able to group these objects by a field, such as an departmentID.
I then need to be able to loop through each department, and then by the 2nd field, which will either be preliminary or final. If there is a final record for a given department, the preliminary record should be hidden. If there is no final for a given department, show the preliminary.
Ex.
Department 1
Preliminary - not visible
Department 1
Final - visible
Department 2
Final - visible
Department 3
Preliminary - not visible
Department 3
Final - visible
Department 4
Preliminary - visible
Data Sample
var data = [
{ departmentID: 1, status: 'preliminary' },
{ departmentID: 1, status: 'final' },
{ departmentID: 2, status: 'final' },
{ departmentID: 3, status: 'preliminary' },
{ departmentID: 3, status: 'final' },
{ departmentID: 4, status: 'preliminary' },
];
Current code below
if (item.result_status !== "preliminary") {
return item;
}
else if (item.result_status === "final") {
return item;
}
else {
return false;
}
<div ng-repeat="(key, value) in Data | groupBy: 'department_ID'">
<div ng-repeat="v in value | filter:prelimFilter(value)">
<a ng-click="showPopUp(key)">{{Some Other Field}} </a>
</div>
</div>
<!-- begin snippet: js hide: false console: true babel: false -->
You are close! Your first half of the code is correct where you group the object based on departmentID using groupBy filter. The issue is with your custom filter. You have created a filter function instead of custom filter and the issue is filter function does not have access to entire array. It has access only to individual elements. This blog explains clearly the different types of filter that can be created in angularjs.
You can create a custom filter which has access to the array and you can return the filtered array based on status
.filter('myFilter', function () {
return function(inputArr) {
var outputArr = [];
outputArr = inputArr.filter(function(obj){return obj.status === "final"});
outputArr = outputArr.filter(function(e){return e}); //To remove empty elements
if(outputArr.length === 0){
outputArr = inputArr; //inputArr does not have obj with status 'final'
}
return outputArr;
};
});
Working jsfiddle here. Good luck!
NOTE: groupBy filter is not part of angular.js You have to include angular.filter module.

Angular .extend changing object reference

I have an array of objects:
var items = [{id:1,name:'one'},{id:2,name:'two'}];
I then select one and make a copy:
var item = items[0];
var copy = angular.copy(item);
I then change a property:
item.name = 'changed';
What are the values?
console.log(item.name); // changed
console.log(items[0].name); // changed
The array element is the same object reference as the item, so the properties are the same.
Now I want to undo the change from the copy:
item = angular.extend(copy);
What are the values?
console.log(item.name); // one
console.log(items[0].name); // changed
By using .extend, I thought I was setting the properties on the item, and NOT changing the object reference, so I was expecting the array item to still be the same object reference as the item and thus to have the same name property, i.e. 'one'.
What went wrong?
If you have a look at angular.extend, it takes two args, dst and src. It will copy src object to dst, right? So, in this case, instead of doing following thing,
item = angular.extend(copy);
What you should be doing is,
item = angular.extend(items, copy)[0];
Here's code snippet:
var app = angular.module('myapp', []);
app.controller('MainCtrl', function($scope) {
var items = [{
id: 1,
name: 'one'
}, {
id: 2,
name: 'two'
}];
var item = items[0];
var copy = angular.copy(item);
item.name = 'changed';
console.log(item.name); // changed
console.log(items[0].name); // changed
console.log("===================");
item = angular.extend(items, copy)[0];
console.log(item.name); // one? (nope!)
console.log(items[0].name); // changed
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="MainCtrl">
</div>
I think what I'm needing is:
Object.assign(item, copy);

ngRepeat with custom index coming up blank

I'm using a large amount of arrays in a large form in my application. In order to make splicing out specific data from my datasets based on the user's selections, I've structured my arrays like this example:
var userList = [];
userList[user1.id] = user1;
userList[user2.id] = user2;
This lets me splice out specific elements without looping through the entire collection by using:
userList.splice(user1.id, 1);
However, when I try to make a list of Users in my HTML using an ng-repeat statement, it comes up blank. My HTML is:
<div data-ng-repeat="user in userList">{{user.name}}</div>
I suspect that ngRepeat uses 0,1,2.. by default and doesn't know how to handle my custom indexes. I've checked several sources but I can't really make sense of things. It did work when I added my users by simply pushing them into the array instead of assigning them to a specific index, so I know the rest of my code works fine.
Help? D:
EDIT:
The addition of a trackBy "track by user.id" didn't fix it.
Plunkr! http://plnkr.co/edit/8hANBvXAIplHsq0Ph6GX?p=preview
Your code isn't working because Array's indexes are zero-based meaning, they go from 0, 1, 2, ... n and you're trying to put alphanumeric indexes if you check the below code snippet the length of the array is zero.
var user1 = {
id: 'A1B2',
name: 'Pete'
};
var user2 = {
id: 'A2B3',
name: 'Jeff'
};
var userList = [];
userList[user1.id] = user1;
userList[user2.id] = user2;
console.log(userList);
console.log('length: ' + userList.length);
console.log(userList['A1B2']);
console.log(userList.A1B2); // behaving as JavaScript Object not array as property set using userList[user2.id] = user2;
So you need to set the data structure properly, you can set it as follows specifying the index of the array or by using the push function on the array to add a new item to the array.
var user1 = {
id: 'A1B2',
name: 'Pete'
};
var user2 = {
id: 'A2B3',
name: 'Jeff'
};
$scope.userList = [];
$scope.userList[0] = user1; // $scope.userList.push(user1);
$scope.userList[1] = user2; // $scope.userList.push(user2);
I suggest you change the collection name from userList to users it looks clean, you don't need to suffix a collection with the List keyword I think it looks untidy, just make the name plural.
angular
.module('demo', [])
.controller('DefaultController', DefaultController);
function DefaultController() {
var vm = this;
var pete = {
id: 'A1B2',
name: 'Pete'
};
var jeff = {
id: 'A2B3',
name: 'Jeff'
};
vm.users = [];
vm.users[0] = pete;
vm.users[1] = jeff;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demo">
<div ng-controller="DefaultController as ctrl">
<div ng-repeat="user in ctrl.users">
<span>{{user.id}}, {{user.name}}</span>
</div>
</div>
</div>
Do following
Controller:
$scope.userList = [];
$scope.userList.push(user1);
$scope.userList.push(user2);
View:
<tr ng-repeat="user in userList track by $index">
<td>{{user.id}}</td>
<td>{{user.name}}</td></tr>
change the id of each individual user to numeric from alpha-numeric as pointed by Adbul, array's indexes are number based
for eg:-
var user1 = {
id: 0, //Any numeric Value
name: 'Pete'
}
and then try $scope.userList[user1.id] = user1;
<div data-ng-repeat="user in userList">{{user.name}}</div>

AngularFire: How to filter a synchronized array?

Consider a set of items and users. Each user can have one or more items. The set of items can be quite big, but every user will normally have small amount of items.
app:
items:
item1:
name: 'table'
color: 'white'
createdAt: '2014-08-09T12:54:58.803Z'
item2:
name: 'macbook air'
color: 'silver'
createdAt: '2014-06-09T12:54:58.803Z'
item3:
name: 'firebase t-shirt'
color: 'yellow'
createdAt: '2014-07-09T12:54:58.803Z'
users:
user1:
items:
item1: true
item3: true
user2:
items:
item2: true
Given a user id, e.g. user1, I would like to display a list of user's items, sorted by createdAt:
yellow firebase t-shirt
white table
Every update on the Firebase server should be reflected in the app view.
I guess the view should look like this:
<div ng-repeat="item in items | orderBy:'createdAt'">
{{ item.color }} {{ item.name }}
</div>
But, I can't figure out an easy way to set up $scope.items.
This is what I currently do:
var userItemsRef = new Firebase(FIREBASE_ROOT + '/users/user1/items');
$scope.userItems = $firebase(userItemsRef).$asArray();
$scope.userItems.$loaded(function() {
$scope.userItems.$watch(setItems);
setItems();
});
function setItems() {
var promises = $scope.userItems.map(function(userItem) {
var itemRef = new Firebase(FIREBASE_ROOT + '/items/' + userItem.$id);
return $firebase(itemRef).$asObject().$loaded();
});
$q.all(promises).then(function(items) {
$scope.items = items;
});
}
Is this the best way to utilize AngularFire?
It would probably be simplest to store the items by user. So a structure like this:
/app/items/$user_id/$item_id/...
This would allow for the items belonging to a particular user to be retrieved like so:
var userId = 'user1';
var ref = new Firebase(FIREBASE_ROOT + '/items/' + userId);
$scope.items = $firebase(ref).$asArray();
If this isn't possible, because items are shared between users, it's probably simplest to use a join lib.
var userId = 'user1';
var userIndexRef = new Firebase(FIREBASE_ROOT + '/users/' + userId + '/items');
var itemsRef = new Firebase(FIREBASE_ROOT + '/items/');
var ref = Firebase.util.intersection(userIndexRef, itemsRef);
$scope.items = $firebase(ref).$asArray();
But generally, it's safest and simplest to take the first route.

Pass array of object properties to filter

I have a custom angular filter that I'm trying to make more generic. The filter looks a collection of employees and filters down the list based on the number of months that have passed since they were hired. I'm trying to make the filter more generic so I can use it on other lists that have dates but will have a different property name (for example: filter invoices that have been created in the last xx months). How can I pass in the name of the property I want to filter on, or how do I pass in the collection of just the date property instead of the whole object?
html
<html ng-app="dateFilterTest">
<head>
</head>
<body ng-controller="filterController">
<select ng-model="maxLookBack">
<option value="6">6 Months</option>
<option value="12" selected="selected">1 Year</option>
<option value="24">2 Year</option>
<option value="999">All</option>
</select>
<ul>
<li ng-repeat="person in people | DateAgeFilter:maxLookBack">{{person.name}}</li>
</ul>
</body>
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript" src="js/controllers/dateFiltercontroller.js"></script>
</html>
js
var app = angular.module('dateFilterTest', []);
app.filter('DateAgeFilter', function(){
return function(items, maxage) {
var minDate = new Date()
minDate.setMonth(minDate.getMonth() - maxage);
var result = [];
for (var i=0; i<items.length; i++) {
if (items[i].hireDate.getTime() > minDate.getTime()) {
result.push(items[i]);
}
}
return result;
};
});
app.controller('filterController', function($scope){
$scope.people = [
{'name': 'Person 1',
'hireDate': new Date('Jun 1, 2013')},
{'name': 'Person 2',
'hireDate': new Date('Aug 1, 2012')},
{'name': 'Person 3',
'hireDate': new Date('May 1, 2011')},
{'name': 'Person 4',
'hireDate': new Date('Jun 1, 2012')},
{'name': 'Person 5',
'hireDate': new Date('Mar 1, 2014')},
{'name': 'Person 6',
'hireDate': new Date('Mar 1, 2014')},
];
});
plunker:
http://plnkr.co/edit/9zOWxJdMNg5zHz7qYf7c
You can pass the property name in as an additional parameter to the filter- like so (using an additional colon separator):
DateAgeFilter:maxLookBack:'hireDate'
Below is an example of changing your filter to make use of that, with 2 changes:
1) The additional parameter- I've called it field here.
2) In order to use the parameter to specify the property, we switch from dot notation to bracket notation- changing items[i].hireDate.getTime() into items[i][field].getTime()
app.filter('DateAgeFilter', function(){
return function(items, maxage, field) {
var minDate = new Date()
minDate.setMonth(minDate.getMonth() - maxage);
var result = [];
for (var i=0; i<items.length; i++) {
if (items[i][field].getTime() > minDate.getTime()) {
result.push(items[i]);
}
}
return result;
};
});
updated plunker

Resources