How to create a generic table template for BackboneJS use - backbone.js

So my template thus far:
<script id="table-row" type="x-handlebars-template">
<tr>
{{#each this}}
<td>{{.}}</td>
{{/each}}
</tr>
</script>
And my render code:
that.collection.each(function(model){
var template = Handlebars.compile( $('#table-row').html() );
that.table.append(template(model.toJSON()));
});
Any my dummy data collection:
this.collection = new Backbone.Collection([
{name: "Tim", age: 5},
{name: "Ben", age: 26},
{name: "Rob", age: 55}
]);
Now, how can I create a template that can output as many <td>s as there are object keys in my Model? ie. I have 'name' and 'age' now, but what I add 'height', I have to specifically update this template, how can I create a generic table row template?

I think you'll need a custom Handlebars helper to iterate over object keys. I haven't used it but something like this should do the trick.
Here's the code from that link:
// HELPER: #key_value
//
// Usage: {{#key_value obj}} Key: {{key}} // Value: {{value}} {{/key_value}}
//
// Iterate over an object, setting 'key' and 'value' for each property in
// the object.
Handlebars.registerHelper("key_value", function(obj, fn) {
var buffer = "",
key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
buffer += fn({key: key, value: obj[key]});
}
}
return buffer;
});

Related

AngularJS ng-repeat and indexOf() to check objects

I have two json objects (data1 and data2) that have related information. Namely, both objects have properties (arrays) which in turn can have identical data. So, I am trying to figure out how to display those data with highlighting them properly, i.e. identical data with green color and non-identical with red color. Somehow it wrongly highlights all data with red color.
Here is the html:
<ul>
<li ng-repeat="item in vm.data2.features"
ng-class="vm.data1.features.indexOf(item) !== -1 ? 'check' : 'uncheck'">
<span ng-bind="item.id"></span>
</li>
</ul>
and objects:
vm.data1 = {
id: '4569',
name: 'Given data',
features: [
{id: "TEST_TEXT2", desc: 'smth12'},
{id: "TEST_PPP", desc: 'smthsmthsmth'},
{id: "TEST_ECASH", desc: "somelongtexthere"}
]
};
vm.data2 = {
id: '1305',
name: 'Base data',
features: [
{id: "TEST_BP", desc: 'smth'},
{id: "TEST_TEXT2", desc: 'smth12'},
{id: "TEST_PPP", desc: 'smthsmthsmth'},
{id: "TEST_TEXT1", desc: 'blahblah'},
{id: "TEST_ECASH", desc: "somelongtexthere"}
]
};
The full demo is here.
Any help would be appreciated.
Indexof() method will look for similarity in object references not the id itself. findIndex() method can help you here instead.
vm.hasFeature = function(item){
var hasElements= vm.data1.features.findIndex(function(e){
return e.id == item.id;
});
console.log(item, hasElements);
return hasElements;
}
And in html
<li ng-repeat="item in vm.data2.features"
ng-class="vm.hasFeature(item) > -1 ? 'check' : 'uncheck'">
vm.hasFeature = function(item){
var hasElements= vm.data1.features.findIndex(function(e){
return e.id == item.id;
});
console.log(item, hasElements);
return hasElements;
}
CodePen Link: https://codepen.io/anon/pen/ewgLBN?editors=1010
None of the objects will be the same because indexOf(item) will compare object references of item. You'll need to do a deep equals comparison of the items.
i.e.
{id: "TEST_TEXT2", desc: 'smth12'} === {id: "TEST_TEXT2", desc: 'smth12'} // false
vm.data1.features[0] === vm.data1.features[1] // false
Example using lodash would be something like:
_.some(vm.data1.features, otherItem => _.isEqual(item, otherItem))
Because
_.isEqual(vm.data1.features[0], vm.data2.features[1]) // true
Docs for Lodash:
_.some
_.isEqual

How to filter values of <select> in angularjs?

I want to filter the values of a <select>.
I have a table with first column <select> .
For eg: object for the <select> is
JSON:
json1 = [{id: 1, name: 'ABC'}, {id: 2, name: 'DEF'}, {id: 3, name: 'XYZ'}, {id: 4, name: 'ASD'}, {id: 5, name: 'QWE'}]
json2 = [{id: 1, name: 'ABC'}, {id: 2, name: 'DEF'}]
My requirement is: We need to show values from json1 in ng-options but which object should not be there in json2.
For eg: First 2 rows will be filled with json2. So we need to provide options 'XYZ' 'ASD' and 'QWE' in the following rows.
Suppose if name 'XYZ' is selected in the dropdown of the third row. then 4th row <select> should show only 'ASD', and 'QWE'. Similarly what ever object selected in other rows shouldn't be shown in option of other rows dropdown.
I have tried something like this
<select ng-model="obj"
ng-options="obj.id as obj.name for obj in json1 | myFilter:json2">
</select>
myApp.filter('myFilter', function(json2) {
return function(json1) {
var filtered = [];
json1.forEach((d) => {
var exists = false;
json2.forEach((ad) => {
if(ad.id == d.id) {
exists = true;
}
});
if(!exists) filtered.push(d);
});
console.log(filetered);
return filtered.length > 0 ? filtered : json1;
};
});
In filter console.log() values are filtered correctly as expected. However in ng-options all options from json1 are still available not updated with filtered values.
What's wrong?
Is this what you are looking for?
For example:
<table>
<thead>
<tr>
<th>Unique</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products">
<td>
<select ng-model="product.unique" ng-options="obj.id as obj.name for obj in (json1 | myfilter:products:$index)">
<option value="">No select</option>
</select>
</td>
<td ng-bind="product.name"></td>
</tr>
</tbody>
</table>
vm.products = [{name: 'Product 1'}, {name: 'Product 2'}, {name: 'Product 3'}, {name: 'Product 4'}, {name: 'Product 5'}];
vm.json1 = [{id: 1, name: 'ABC'}, {id: 2, name: 'DEF'}, {id: 3, name: 'XYZ'}, {id: 4, name: 'ASD'}, {id: 5, name: 'QWE'}];
App.filter('myfilter' , [function() {
return function(json1, products, index) {
// Filter all products, except the mime
productsFilter = products.filter(function(product, i) {
return i !== index;
});
// filter those that have not yet been selected
var json1Filter = json1.filter(function(item) {
// ask if there is the id in a product
return !productsFilter.some(function(product) {
return item.id == product.unique;
});
});
return json1Filter;
};
}]);
Example Codepen: https://codepen.io/anon/pen/vMJyLz
I think you have the parameters to your filter slightly wrong. The filter function takes a first parameter as the value to be filtered and the other parameters are the ones after myFilter:.
I'm not 100% sure what you want to happen here, but your filter function is called for each value in the dropdown list and should return a replacement for the given value.
However, your code is returning an array of items. This is not how a filter works in AngularJS.
Your filter needs to be updated to check if the item being passed into the filter should be shown or not, and if not, return false.
You can look at the script example from the AngularJS docs to see how they show to do this, or this other Stack Overflow question/answer.

How to filter collection based on another collection using underscore.js

I have a collection called `mainItems``
this.mainItems;
which contains 18 models. And also one more collection object which contains selected items:
this.seletedItems;
I need to filter the main collection object based on other collection.
I have tried the below approach:
var that = this;
var model = _.reject(that.filteredItems.models, function(model1){
return _.filter(that.collection.models, function(model2) {
return model1.id == model2.id;
});
});
but this approach is not working properly. Is it possible to filter the main items by avoiding second iteration ?
Please help!
You can use the Underscore's methods proxied by Backbone to simplify your filter.
For example, to list the models in mainItems without the models in selectedItems, you could use
// reject the models found in selectedItems
var filtered = mainItems.reject(function(m) {
return selectedItems.get(m.id);
});
Note that Collection.get is a hash lookup, making this a Backbone equivalent to the answer pointed by #GruffBunny in the comments.
And a demo
var mainItems = new Backbone.Collection([
{id: 1},
{id: 2},
{id: 3},
{id: 4}
]);
var selectedItems = new Backbone.Collection([
{id: 2}
]);
var keep = mainItems.reject(function(m) {
return selectedItems.get(m.id);
});
console.log(_.pluck(keep, 'id'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
You can use difference to get all the models that are not selected.
var mainItems = [{ a: 1 }, { b: 2 }, { c: 3 }];
var selectedItems = mainItems.slice(1);
console.log(_.difference(mainItems, selectedItems));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

AngularJS filter to ng-repeat, exclude elements, if they in second array

I need filter for ng-repeat, that explode elements in "general" array, if element exist in "suggest" array (by id field).
$scope.general= [{id: 21323, name: 'alex'}, {id: 8787, name: 'maria'}, {id: 8787, name: 'artem'}];
$scope.suggest = [{id: 21323, name: 'alex'}, {id: 8787, name: 'maria'}];
<div ng-repeat="elem in general">{{elem.name}}</div>
You should create your own custom filter and you'll probably want to use Array.prototype.filter.
You said you wanted to exclude by the property id. The following filler optionally allows specifying a property. If the property is not specified, then the objects are excluded by strict equality (the same method used by the ===, or triple-equals, operator) of the objects.
angular.module('myFilters', [])
.filter('exclude', function() {
return function(input, exclude, prop) {
if (!angular.isArray(input))
return input;
if (!angular.isArray(exclude))
exclude = [];
if (prop) {
exclude = exclude.map(function byProp(item) {
return item[prop];
});
}
return input.filter(function byExclude(item) {
return exclude.indexOf(prop ? item[prop] : item) === -1;
});
};
});
To use this filter in your html:
<div ng-repeat="elem in general | exclude:suggest:'id'">{{elem.name}}</div>
Here is an example jsfiddle:
https://jsfiddle.net/6ov1sjfb/
Note that in your question artem's id matches maria's thus both artem and maria were filtered. I changed artem's id in the plunker to be unique to show that the filter works.
Try this :
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.general = [{
id: 21323,
name: 'alex'
}, {
id: 8787,
name: 'maria'
}, {
id: 8787,
name: 'artem'
}];
$scope.suggest = [{
id: 21323,
name: 'alex'
}, {
id: 8787,
name: 'maria'
}];
$scope.filteredArray = function () {
return $scope.general.filter(function (letter) {
for (i = 0; i < $scope.suggest.length; i++) {
return $scope.suggest[i].id !== letter.id
}
});
};
}
and
<div ng-repeat="elem in filteredArray(letters)">{{elem.name}}</div>
check out the fiddle : http://jsfiddle.net/o2er6msv/
Note: please chk ur id, they are duplicated

toJSON on Backbone.Collection#where?

I'm not sure why, but I can't get this to work.
var friends = new Backbone.Collection([
{name: "Athos", job: "Musketeer"},
{name: "Porthos", job: "Musketeer"},
{name: "Aramis", job: "Musketeer"},
{name: "d'Artagnan", job: "Guard"},
]);
friends.where({job: "Musketeer"}).toJSON()
I'm getting Uncaught TypeError: Object [object Object] has no method 'toJSON'.
What I'm I doing wrong and how do I convert my filtered collection into JSON?
What the Underscore.where method returns is an Array not a Backbone.Collection so it has not the toJSON method defined.
So you can make two things:
Iterate over elements and map the result:
var result = friends.where({job: "Musketeer"});
_.map( result, function( model ){ return model.toJSON(); } );
jsFiddle code
Implement a Collection searcher method that returns a proper Backbone.Collection:
var Friends = Backbone.Collection.extend({
search: function( opts ){
var result = this.where( opts );
var resultCollection = new Friends( result );
return resultCollection;
}
});
var myFriends = new Friends([
{name: "Athos", job: "Musketeer"},
{name: "Porthos", job: "Musketeer"},
{name: "Aramis", job: "Musketeer"},
{name: "d'Artagnan", job: "Guard"},
]);
myFriends.search({ job: "Musketeer" }).toJSON();​
jsFiddle code
toJSON is a confusing method name : http://documentcloud.github.com/backbone/#Collection-toJSON
toJSON collection.toJSON()
Return an array containing the attributes hash of each model in the collection. This can be used to serialize and >persist the collection as a whole. The name of this method is a bit confusing, because it conforms to JavaScript's >JSON API.
if you want to convert your collection to a JSON string, use JSON.stringify
var friends = new Backbone.Collection([
{name: "Athos", job: "Musketeer"},
{name: "Porthos", job: "Musketeer"},
{name: "Aramis", job: "Musketeer"},
{name: "d'Artagnan", job: "Guard"},
]);
JSON.stringify( friends.where({job: "Musketeer"}) );
Note that where returns an array, not a Backbone collection, you would have to build a new collection to use the toJSON method.

Resources