ng-repeat not updating when ng-model input updates - angularjs

Below I have code example that will successfully group data based on a model property for an ng-repeat, but only on the initial page load. So for example on load I do
$scope.filter_items.group_1 = 'propb'; , which correctly repeats and groups by propb. Same if I switch it to propa.
The problem is that after the page loads, changing the filter_items model via an input has no effect on he ng-repeat, which doesn't update. I've tried to work a bit with the .$apply() method, but nothing that worked (and from what I read I shouldn't have to use it). How can I get the ng-repeat to redraw after a model input change?
Note: I use the memoize function to avoid Angular digest errors, which will happen without it.
http://plnkr.co/N41D2Y
<!DOCTYPE html>
<html>
<head>
<script src="//code.angularjs.org/1.3.0/angular.js" data-semver="1.3.0" data-require="angular.js#*"></script>
<script data-require="underscore.js#*" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script>
var app = angular.module("MyApp", []);
app.controller('MyCtrl', function($scope) {
$scope.filter_items = {};
$scope.filter_items.group_1 = 'propb';
$scope.items = [{id:"1", propa:"one", propb:"uno"},{id:"2", propa:"one", propb:"tres"}]
return
});
app.filter('myFilter', function() {
return _.memoize(function(items, filter_items) {
return _.groupBy(items, filter_items.group_1);
}
);
});
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="MyCtrl">
Group by<br>
<select ng-model="filter_items.group_1">
<option value="propa">A</option>
<option value="propb">B</option>
</select>
<div ng-repeat="(group1, g_items) in items| myFilter:filter_items">
<h2>{{group1}}</h2>
</div>
</div>
</body>
</html>

I can't get your code working or then there's just something I don't understand or missed. But if custom filter is giving you headache, why don't you just put simple grouping function inside your controller?
Say, your controller
var app = angular.module("MyApp", []);
app.controller('MyCtrl', function($scope) {
$scope.items = [{
id:1,
propa:"one",
propb:"uno"
},{
id:2,
propa:"one",
propb:"uno"
},{
id:3,
propa:"two",
propb:"dos"
},{
id:4,
propa:"two",
propb:"dos"
}];
$scope.notGrouped = angular.copy($scope.items);
$scope.options = ['propa','propb'];
$scope.groupBy = function (option) {
$scope.items = _.groupBy($scope.notGrouped, option);
}
});
and related HTML template
<body ng-app="MyApp">
<div ng-controller="MyCtrl">
Group by<br>
<select ng-model="selectedOption"
ng-options="option as option for option in options"
ng-change="groupBy(selectedOption)">
</select>
<br><br>
<div ng-hide="!!selectedOption">Not grouped yet...</div>
<div ng-repeat="item in items">
<span>{{ item | json }}</span>
</div>
</div>
</body>
Would give you

Related

Is there a way in AngularJS call function in select?

I need something like this:
<div ng-repeat="customer in customers">
<select ng-options="option.Name for option in **getOptions(customer.ID)**"></select>
</div>
Is it possible?
Yes , as long as the function is synchronous. Using a filter expression in ng-options is also an alternative
const app = angular.module('myApp', []);
app.controller('myCtrl', function() {
this.getNames = () => ["Emil", "Tobias", "Linus"]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl as $ctrl">
<select ng-model=" $ctrl.selectedName" ng-options="item for item in $ctrl.getNames()" size=5>
</select>
<p ng-if=" $ctrl.selectedName">Selected: {{ $ctrl.selectedName}}</p>
</div>
const app = angular.module('myApp', []);
app.controller('myCtrl', function() {
this.getNames = () => ["Emil", "Tobias", "Linus"]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl as $ctrl">
<select ng-model=" $ctrl.selectedName" ng-options="item for item in $ctrl.getNames()" size=5>
</select>
<p ng-if=" $ctrl.selectedName">Selected: {{ $ctrl.selectedName}}</p>
</div>
Thank you for your answer. But I can't work it. My code below.
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Test</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script>
var app = angular.module("MainModule", []);
app.controller('TestController', function ($scope, $http, $filter) {
$scope.one = ["Sam", "Samantha", "George"]
$scope.two = ["Jack", "Sandra", "Bill"]
$scope.getOptions = function (id) {
if (id === 1) {
return $scope.one;
}
else if (id ===2) {
return $scope.two;
}
};
});
</script>
</head>
<body ng-app="MainModule" ng-controller="TestController>
<div>
<select ng-options="item for item in TestController.getOptions(1)"></select>
<select ng-options="item for item in TestController.getOptions(2)"></select>
</div>
</body>
</html>

Angular js two way data binding with function

This is my code
index.html
<html ng-app="myApp">
<head>
<script src="https://code.angularjs.org/1.6.0-rc.2/angular.min.js"</script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="mainController">
<label>Who is your favorite player</label><br>
<input type="text" ng-model="handle"/>
<hr/>
<h1>Favorite Player - {{ lchandle }}</h1>
</div>
</body>
app.js
var myApp = angular.module("myApp" , []);
var controller = myApp.controller( 'mainController' , [ '$scope' , '$filter' , function( $scope , $filter )
{
$scope.handle = "";
var tolower = function(){
return $filter('lowercase')($scope.handle);
}
$scope.lchandle = tolower();
}]);
The favorite player does update when I change in the input
Can you please tell me what I am doing wrong?
You can achieve this by adding a $watch for the variable and variable
DEMO
var myApp = angular.module("myApp" , []);
var controller = myApp.controller( 'mainController' , [ '$scope','$filter' , function( $scope , $filter )
{
$scope.$watch('handle', function() {
$scope.handle = $filter('lowercase')($scope.handle);
});
}]);
<html ng-app="myApp">
<head>
<script src="https://code.angularjs.org/1.6.0-rc.2/angular.min.js"</script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="mainController">
<label>Who is your favorite player</label><br>
<input type="text" ng-model="handle"/>
<hr/>
<h1>Favorite Player - {{ handle }}</h1>
</div>
</body>
You need to pass a function instance, instead of its result after calling it. So change it from $scope.lchandle = tolower(); to $scope.lchandle = tolower;.
Then you can call (execute) it with <h1>Favorite Player - {{ lchandle() }}</h1>
var myApp = angular.module("myApp", []);
var controller = myApp.controller('mainController', ['$scope', '$filter', function($scope, $filter) {
$scope.handle = "";
var tolower = function() {
return $filter('lowercase')($scope.handle);
}
$scope.lchandle = tolower;
}]);
<html ng-app="myApp">
<head>
<script src="https://code.angularjs.org/1.6.0-rc.2/angular.min.js"></script>
</head>
<body>
<div ng-controller="mainController">
<label>Who is your favorite player</label><br>
<input type="text" ng-model="handle" />
<hr/>
<h1>Favorite Player - {{ lchandle() }}</h1>
</div>
</body>
Unless you are planning to change your filter, I suggest to use a different syntax for filters:
<h1>Favorite Player - {{ handle | lowercase }}</h1>
Based on previous answer by Sajeetharan I made version with ng-change, which I think is easier to figure out and more modern.
var myApp = angular.module("myApp" , []);
var controller = myApp.controller( 'mainController' , [ '$scope','$filter' , function( $scope , $filter )
{
$scope.lowercase = function() {
$scope.lhandle = $filter('lowercase')($scope.handle);
};
}]);
<html ng-app="myApp">
<head>
<script src="https://code.angularjs.org/1.6.0-rc.2/angular.min.js"</script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="mainController">
<label>Who is your favorite player</label><br>
<input type="text" ng-model="handle" ng-change="lowercase()"/>
<hr/>
<h1>Favorite Player - {{ lhandle }}</h1>
</div>
</body>

How dynamically get the value of a JSON variable in Angular?

Hi I want to do some like this:
{{item.price.{{comparator}}}}
I mean, take a value from type of price using a {{comparator}} variable.
this is an example of my code (the data is bigger and I have more types of prices):
var items = [
{"name":"Item1",price:{public:10,private:15,other1:16.3,other2:17.5}},
{"name":"Item2",price:{public:20,private:45}},
]
var angularApp = angular.module('angularApp', [
'angularAppControllers',
]);
var angularAppControllers = angular.module('angularAppControllers', []);
angularAppControllers.controller('ComparationCtrl', ['$scope',
function ($scope) {
$scope.comparator = "private";
$scope.data = items;
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-App="angularApp">
<div ng-controller="ComparationCtrl">
<select ng-model="comparator">
<option value="public">Public</option>
<option value="private">Private</option>
<option value="other1">Other 1</option>
<option value="other2">Other 2</option>
<option value="othern">....</option>
</select>
{{comparator}}
<br />
<ul>
<li ng-repeat="item in data">
{{item.name}} - {{item.price.public}} - <strong>(item.price.{{comparator}})</strong>
</li>
</ul>
</div>
</div>
Hum, assuming item.price and comparator are defined in your $scope, try:
{{ item.price[comparator] }}
Like this: https://plnkr.co/edit/IrheJrHz8eOfb5LmTx7x
angular.module('app', [])
.config(function() {
})
.controller('ctrl', function($scope) {
$scope.obj = {
"name": "Item1",
"price": {
"public": "10",
"private": "15",
"other1": "16.3",
"other2": "17.5"
}
};
$scope.prop = 'public';
});
And the HTML:
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://code.angularjs.org/1.5.8/angular.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body ng-controller="ctrl">
<pre>{{obj.price[prop]|json}}</pre>
<script src="script.js"></script>
</body>
</html>
Basically, {{obj[aPropertyDefinedInScope]}}

Angular indexOf not outputting text

I'm trying to output the name "Sam" on the screen in a ng-show using indexOf, but nothing ever appears. Any help is appreciated.
html
<head></head>
<body>
<div ng-controller="ArrayController">
<div ng-repeat="product in products">
<div ng-show="product.name.indexOf('Sam') == 2">
</div>
</div>
</div>
<script src="bower_components/angular/angular.js"></script>
<script src="app.js"></script>
</body>
</html>
angular
var app = angular.module('myApp', []);
app.controller('ArrayController', ['$scope', function($scope){
$scope.products = [
{
name: 'Joe'
},
{
name: 'Bill'
},
{
name: 'Sam'
}
];
}]);
Possible solution using ng-if instead of ng-show
<body ng-app="myApp">
<div ng-controller="ArrayController">
<div ng-repeat="product in products">
<div ng-if="product.name.indexOf('Sam') > -1">{{product.name}}
</div>
</div>
</div>
</body>
var app = angular.module('myApp', []);
app.controller('ArrayController', ['$scope', function($scope){
$scope.products = [
{
name: 'Joe'
},
{
name: 'Bill'
},
{
name: 'Sam'
}
];
}]);
<div ng-show="product.name.indexOf('Sam') == 2"></div>
This will show the div if product.name contains the string "Sam" starting at its third character (i.e. "xxSam"). That probably isn't what you want.
The div is empty, so will not be visible to the user even if the ng-show is truthy. That, too, is probably not what you want.
I'm pretty sure what you do want is this:
<div>{{product.name}}</div>

unable to get angular ng-repeat group by filter working with dynamic group term

I have been trying for days to get a working version of a grouping filter for Angular. The goal is to group and filter a list of items, using dynamic group and search terms. The end product will have two levels of ng-repeats as well as filter terms, but below I've distilled the core issue to just a single ng-repeat and no search filtering.
The issue is described in this question, namely that I get "10 digest" errors in the JS console. I have tried the suggestion in that thread, namely that I use _.memoize(). This works for the initial load, but somehow doesn't update the ng-repeat when I update the model. I'm also unable to determine how "track by" would work for me.
For example code desired output is:
A selected: "One"
B selected: "tres uno"
Here is the version with digest errors (check the JS console), but with output working as desired:
http://plnkr.co/9TJvZk
<!DOCTYPE html>
<html>
<head>
<script src="//code.angularjs.org/1.3.0/angular.js" data-semver="1.3.0" data-require="angular.js#*"></script>
<script data-require="underscore.js#*" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script>
var app = angular.module("MyApp", []);
app.controller('MyCtrl', function($scope) {
$scope.filter_items = {};
$scope.filter_items.group_1 = 'propb';
$scope.items = [{id:"1", propa:"one", propb:"uno"},{id:"2", propa:"one", propb:"tres"}]
return
});
app.filter('myFilter', function() {
return function(items, filter_items) {
return _.groupBy(items, filter_items.group_1);
}
});
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="MyCtrl">
Group by<br>
<select ng-model="filter_items.group_1">
<option value="propa">A</option>
<option value="propb">B</option>
</select>
<div ng-repeat="(group1, g_items) in items| myFilter:filter_items">
<h2>{{group1}}</h2>
</div>
</div>
</body>
</html>
And error free, but not updating when model does:
http://plnkr.co/N41D2Y
<!DOCTYPE html>
<html>
<head>
<script src="//code.angularjs.org/1.3.0/angular.js" data-semver="1.3.0" data-require="angular.js#*"></script>
<script data-require="underscore.js#*" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script>
var app = angular.module("MyApp", []);
app.controller('MyCtrl', function($scope) {
$scope.filter_items = {};
$scope.filter_items.group_1 = 'propb';
$scope.items = [{id:"1", propa:"one", propb:"uno"},{id:"2", propa:"one", propb:"tres"}]
return
});
app.filter('myFilter', function() {
return _.memoize(function(items, filter_items) {
return _.groupBy(items, filter_items.group_1);
}
);
});
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="MyCtrl">
Group by<br>
<select ng-model="filter_items.group_1">
<option value="propa">A</option>
<option value="propb">B</option>
</select>
<div ng-repeat="(group1, g_items) in items| myFilter:filter_items">
<h2>{{group1}}</h2>
</div>
</div>
</body>
</html>
How do I get working dynamic grouping in AngularJS without the digest errors?
you mean like this
<!DOCTYPE html>
<html>
<head>
<script src="//code.angularjs.org/1.3.0/angular.js" data-semver="1.3.0" data-require="angular.js#*"></script>
<script data-require="underscore.js#*" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script>
var app = angular.module("MyApp", []);
app.controller('MyCtrl', function($scope) {
$scope.filter_items = {};
$scope.filter_items.group_1 = 'propb';
$scope.items = [{id:"1", propa:"one", propb:"uno"},{id:"2", propa:"one", propb:"tres"}];
return;
});
app.filter('myFilter', function() {
var m = {};
return function(items, filter_items) {
return (filter_items.group_1 in m) ? m[filter_items.group_1] : (m[filter_items.group_1] = _.groupBy(items, filter_items.group_1) );
}
});
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="MyCtrl">
Group by<br>
<select ng-model="filter_items.group_1">
<option value="propa">A</option>
<option value="propb">B</option>
</select>
<div ng-repeat="(group1, g_items) in items| myFilter:filter_items">
<h2>{{group1}}</h2>
</div>
</div>
</body>
</html>
I updated the code above as well as the plunker, just to illustrate your code is workable. But this angular-filter project is awesome, check it out.
The reason for the digest error is that each call to _.groupBy returns a new object, so every time angular checks it against the old value it is different, leading to another digest.
As the list is only filtered when filter_items.group_1 changes, it's simple to watch this in the controller and perform the group by there:
app.controller('MyCtrl', function($scope) {
$scope.filter_items = {};
$scope.filter_items.group_1 = 'propb';
$scope.items = [{id:"1", propa:"one", propb:"uno"},{id:"2", propa:"one", propb:"tres"}];
$scope.$watch('filter_items.group_1', function (group) {
$scope.groupedItems = _.groupBy($scope.items, group);
});
});
Then:
<div ng-repeat="(group1, g_items) in groupedItems">
<h2>{{group1}}</h2>
</div>
I believe you can achieve the same result without using a filter. I have successfully achieved this by using a watcher in the controller and grouping in the watcher function. This doesn't have the 10 digest errors and would work when your selection changes.
<head>
<script src="//code.angularjs.org/1.3.0/angular.js" data-semver="1.3.0" data-require="angular.js#*"></script>
<script data-require="underscore.js#*" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script>
var app = angular.module("MyApp", []);
app.controller('MyCtrl', function($scope) {
$scope.filter_items = {};
$scope.filter_items.group_1 = 'propb';
$scope.items = [{id:"1", propa:"one", propb:"uno"},{id:"2", propa:"one", propb:"tres"}]
$scope.$watch('filter_items.group_1', function(newValue){
$scope.grouped = _.groupBy($scope.items, newValue)
})
return
});
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="MyCtrl">
Group by<br>
<select ng-model="filter_items.group_1">
<option value="propa">A</option>
<option value="propb">B</option>
</select>
<div ng-repeat="(group1, g_items) in grouped">
<h2>{{group1}}</h2>
</div>
</div>
</body>
</html>

Resources