Angular JS render variable in scope function to view and make comparison - javascript

Hi I have been using this tag to change my css style, if the condition totalAsset and sortedAsset are same
<div class="table-row" ng-repeat="x in myData" ng-click="sort()"
ng-class="{'lightblue': x.totalAsset == sortedAsset}">
totalAsset is my data in like this
$scope.myData = [
{
totalAsset: "23557"
},
{
totalAsset: "4512190",
},
{
totalAsset: "2190",
},
{
totalAsset: "1256790",
}
]
i have create a function that self sort the totalAsset
$scope.sort = function (){
$scope.unsortedAsset = $scope.myData.totalAsset;
$scope.sortedAsset=$scope.unsortedAsset.split("").sort().join("");
}
in the logic only the first and last row will become blue the other two rows remain same.

In sort() you directly access $scope.myData.totalAsset. This gets resolved as a reference to the last object in $scope.myData that has a totalAsset member.
Instead you wanted to iterate over all objects in myData. That can be achieved by supplying a parameter to the sort function like in the code below.
$scope.sort = function (totalAsset){
$scope.unsortedAsset = totalAsset;
$scope.sortedAsset=$scope.unsortedAsset.split("").sort().join("");
}
Then you also must call the sort function by supplying the parameter value.
<div class="table-row" ng-repeat="x in myData" ng-click="sort(x.totalAsset)" ng-class="{'lightblue': x.totalAsset == sortedAsset}">

Related

Unexpected modify of non primitive values in loop

I'm working with an angular service of the type:
services.factory('SaveHistory', function($rootScope, $localForage){
return {
videoAccessed: function(idPillola) {
$localForage.getItem('trainings_user_'+$rootScope.user.id)
.then(function(succ, err) {
for (var item in succ) {
[].forEach.call(succ[item], function(el, index) {
el.pillole.forEach(function(el, index){
if (el.idPercorso == idPillola) {
console.log(idPillola);
el.tracking.completion_status = 1;
}
});
});
}
var newTrainings = succ;
...
});
}
When the function is fired with the correct idPillola , console.log logs the correct idPillola value one single time, so it seems that the cycle works correctly. But : if the attribute in the object (object or rather 'el' in the nested forEach cycle) that i want to change is a primitive , there are no problems, if the attribute is not primitive but an another object attribute, like tracking.completion_status in this case, all elements are updated ! (Like the if control had been ignored).
It is related to Angular or Javascript itself?

How to create dependency with Mithril.js?

I have a long list of items that I want to show in a <ul>. I want to add a "filter" input, so the user can narrow down the list of items to those matching the filter.
My controller contains a filter prop and a list array:
function Ctrl() {
this.filter = m.prop('');
this.list = [];
}
I've added an update method to the controller, which looks at the filter prop and updates the contents of the list array:
Ctrl.prototype.update = function (value) {
var _this = this;
if (this.filter()) {
searchItems(this.filter(), function (items) {
_this.list = items;
});
} else {
this.list = [];
}
};
Finally, my view iterates over the list array and renders the items. Additionally, it displays an input on top, bound to the filter prop:
var view = function (ctrl) {
return m('#content', [
m('input', {
oninput: m.withAttr("value", ctrl.filter),
value: ctrl.filter()
}),
m('ul', [
ctrl.list.map(function (item, idx) {
return m('li', m('span', item.getName()));
})
])
]);
};
My question is, how to make the update function fire when the value of filter changes, so that I get the updated list of items?
Do I need to position two oninput events? One to update filter and one to fire update?
Should I use a single oninput event and update the filter property within he update function?
Anything else?
When you use m.withAttr, what you're saying is that when the event handler fires (oninput), you will take some attribute of the element (value) and pass it into your second argument, which is a function (ctrl.filter). Your current sequence of events:
filter property gets updated
mithril redraws
What you want to do, is call the update function (instead of the getter/setter ctrl.filter function), and bind it so you can retain the proper context in the function:
m('input', {
oninput: m.withAttr("value", ctrl.update.bind(ctrl)),
value: ctrl.filter()
}),
Then, in your update function the value will be passed to the function and you can set it there.
Ctrl.prototype.update = function (value) {
this.filter(value);
...
Now what'll happen:
ctrl.filter property gets updated
ctrl.list gets filtered based on ctrl.filter
mithril redraws
Another way to handle this is to not have any "list" property in your controller / model, but to let the view grab a filtered list instead. There's only one thing really changing, after all, and that's the "filter" property. The filtered list is derived from that, so by creating another property on the controller, you're effectively duplicating the same state.
Additionally, you could keep m.withAttr('value', ctrl.filter) and benefit from that simplicity.
Something like:
var filteredItems = ctrl.getFilteredItems();
var view = function (ctrl) {
return m('#content', [
m('input', {
oninput: m.withAttr("value", ctrl.filter),
value: ctrl.filter()
}),
m('ul', [
filteredItems.map(function (item, idx) {
return m('li', m('span', item.getName()));
})
])
]);
};

Update array in forEach angularjs

I am having a problem when trying to filter an array in angular. I'm using typescript.
I have a parent page that contains a directive. The directive has a property of an Array of items which it displays in a datatable.
On the parent page, I want to filter the list that is being passed to the directive. Here is how I am doing it....
<table items="vm.items"></table>
In my parent controller, I have a button which when you press it executes the following function:
applyFilters() {
var filteredItems=[];
this.items.forEach((value, key) => {
if (value.item!== 'test') {
this.filteredItems.push(value);
}
});
console.log(this.filteredItems);
this.items = this.filteredItems;
}
But the value in the directive does not update when I update the filter?
What am I doing wrong here?
Here:
if (value.item!== 'test') {
this.filteredItems.push(value);
}
The variable filteredItems is defined through var filteredItems = [];, while you assign through this.filteredItems. Just use:
filteredItems.push(value);
...
this.items = filteredItems;

Angular's orderBy filter and an array of object that has only methods

I have an array of objects. The only exposed interface of these objects are their methods.
obj = {
getId : function() { ... },
getName : function() { ... },
getAvgHours : function() { ... }
}
In fact, there are some fields that contain the actual values. But these are generated code so these fields are cryptically named, are buried under another layer of field. The access to their values should be done via supplied functions only.
I'm looking for something like below, but it doesn't work:
<tr ng-repeat="p in people | orderBy:p.getAvgHours()">
I could add a named function getAvgHours(p) { return p.getAvgHours(); } to the scope, which works. But it seems redundant, and I'd need to add a dozen of these functions.
Is there any better way?
You could simply make use of a predicate and pass the method you wish to call as a string.
The markup:
orderBy:fnValue('getId')
And the predicate function:
$scope.fnValue = function (fn) {
return function (obj) {
return obj[fn]();
};
};
Inside the function you return from the predicate, you will have access to the object being sorted, so you would invoke the method there and fetch the value.
DEMO
There may be a better way, but one solution would be to write your own filter that wraps the normal orderBy. Here's an example:
myApp.filter("orderByObjFunc", ["$filter", function($filter) {
return function(input, funcName, reverse) {
input = input.map(function(a) { a.zSortField = a[funcName](); return a; });
return $filter('orderBy')(input, 'zSortField', reverse);
};
}]);
This would save you from making a dozen helper functions. You'd use it like this:
<tr ng-repeat="p in people | orderByObjFunc:'getAvgHours'">
Here's a working fiddle.

Passing arguments to angularjs filters

Is it possible to pass an argument to the filter function so you can filter by any name?
Something like
$scope.weDontLike = function(item, name) {
console.log(arguments);
return item.name != name;
};
Actually there is another (maybe better solution) where you can use the angular's native 'filter' filter and still pass arguments to your custom filter.
Consider the following code:
<div ng-repeat="group in groups">
<li ng-repeat="friend in friends | filter:weDontLike(group.enemy.name)">
<span>{{friend.name}}</span>
<li>
</div>
To make this work you just define your filter as the following:
$scope.weDontLike = function(name) {
return function(friend) {
return friend.name != name;
}
}
As you can see here, weDontLike actually returns another function which has your parameter in its scope as well as the original item coming from the filter.
It took me 2 days to realise you can do this, haven't seen this solution anywhere yet.
Checkout Reverse polarity of an angular.js filter to see how you can use this for other useful operations with filter.
From what I understand you can't pass an arguments to a filter function (when using the 'filter' filter). What you would have to do is to write a custom filter, sth like this:
.filter('weDontLike', function(){
return function(items, name){
var arrayToReturn = [];
for (var i=0; i<items.length; i++){
if (items[i].name != name) {
arrayToReturn.push(items[i]);
}
}
return arrayToReturn;
};
Here is the working jsFiddle: http://jsfiddle.net/pkozlowski_opensource/myr4a/1/
The other simple alternative, without writing custom filters is to store a name to filter out in a scope and then write:
$scope.weDontLike = function(item) {
return item.name != $scope.name;
};
Actually you can pass a parameter ( http://docs.angularjs.org/api/ng.filter:filter ) and don't need a custom function just for this. If you rewrite your HTML as below it'll work:
<div ng:app>
<div ng-controller="HelloCntl">
<ul>
<li ng-repeat="friend in friends | filter:{name:'!Adam'}">
<span>{{friend.name}}</span>
<span>{{friend.phone}}</span>
</li>
</ul>
</div>
</div>
http://jsfiddle.net/ZfGx4/59/
You can simply do like this
In Template
<span ng-cloak>{{amount |firstFiler:'firstArgument':'secondArgument' }}</span>
In filter
angular.module("app")
.filter("firstFiler",function(){
console.log("filter loads");
return function(items, firstArgument,secondArgument){
console.log("item is ",items); // it is value upon which you have to filter
console.log("firstArgument is ",firstArgument);
console.log("secondArgument ",secondArgument);
return "hello";
}
});
Extending on pkozlowski.opensource's answer and using javascript array's builtin filter method a prettified solution could be this:
.filter('weDontLike', function(){
return function(items, name){
return items.filter(function(item) {
return item.name != name;
});
};
});
Here's the jsfiddle link.
More on Array filter here.
You can pass multiple arguments to angular filter !
Defining my angular app and and an app level variable -
var app = angular.module('filterApp',[]);
app.value('test_obj', {'TEST' : 'test be check se'});
Your Filter will be like :-
app.filter('testFilter', [ 'test_obj', function(test_obj) {
function test_filter_function(key, dynamic_data) {
if(dynamic_data){
var temp = test_obj[key];
for(var property in dynamic_data){
temp = temp.replace(property, dynamic_data[property]);
}
return temp;
}
else{
return test_obj[key] || key;
}
}
test_filter_function.$stateful = true;
return test_filter_function;
}]);
And from HTML you will send data like :-
<span ng-bind="'TEST' | testFilter: { 'be': val, 'se': value2 }"></span>
Here I am sending a JSON object to the filter.
You can also send any kind of data like string or number.
also you can pass dynamic number of arguments to filter ,
in that case you have to use arguments to get those arguments.
For a working demo go here - passing multiple arguments to angular filter
You can simply use | filter:yourFunction:arg
<div ng-repeat="group in groups | filter:weDontLike:group">...</div>
And in js
$scope.weDontLike = function(group) {
//here your condition/criteria
return !!group
}

Categories

Resources