Using AngularJs "ng-style" within "ng-repeat" iteration - angularjs

I am tring to conditionally set the colors of data elements in a table based on their value using ng-style. Each row of data is being generated using ng repeat.
So i have something like:
<tr ng-repeat="payment in payments">
<td ng-style="set_color({{payment}})">{{payment.number}}</td>
and a function in my controller that does something like:
$scope.set_color = function (payment) {
if (payment.number > 50) {
return '{color: red}'
}
}
I have tried a couple different things. and even set the color as a data attribute in the payment object, however it seems I cant get ng-style to process data from the data bindings,
Does anyone know a way I could make this work? Thanks.

Don't use {{}}s inside an Angular expression:
<td ng-style="set_color(payment)">{{payment.number}}</td>
Return an object, not a string, from your function:
$scope.set_color = function (payment) {
if (payment.number > 50) {
return { color: "red" }
}
}
Fiddle

use this code
<td style="color:{{payment.number>50?'red':'blue'}}">{{payment.number}}</td>
or
<td ng-style="{'color':(payment.number>50?'red':'blue')}">{{payment.number}}</td>
blue color for example

It might help you!
<!DOCTYPE html>
<html>
<head>
<style>
.yelloColor {
background-color: gray;
}
.meterColor {
background-color: green;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
var app = angular.module('ngStyleApp', []);
app.controller('ngStyleCtrl', function($scope) {
$scope.bar = "48%";
});
</script>
</head>
<body ng-app="ngStyleApp" ng-controller="ngStyleCtrl">
<div class="yelloColor">
<div class="meterColor" ng-style="{'width':bar}">
<h4> {{bar}} DATA USED OF 100%</h4>
</div>
</div>
</body>
</html>

Related

Switch between displayed data

I've started looking at AngularJS a few hours ago so I'm settling into how things work. As part of a basic example, I'm trying to figure out how I switch between displayed data in a table.
At the moment, I've got the following as my basic app;
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
</head>
<body>
<div ng-app="applicationMain" ng-controller="controllers.app.main">
<table>
<tr ng-repeat="item in items">
<button>Toggle</button>
<td>{{item.name}}<td>
</tr>
</table>
</div>
</body>
<script>
var controllers = {
app : {
main : function($scope){
var s = $scope;
s.items = [
{
name : "Pizza",
price : 100
},
{
name : "Burger",
price : 45
},
{
name : "Kebab",
price : 85
}
];
}
}
}
var app = angular.module("applicationMain", []);
app.controller('controllers.app.main', controllers.app.main);
</script>
</html>
Fairly simple. Scoped array of objects with name and price fields, where the name of each is displayed in a table using ng-repeat
What I'd like to do is when I click the Toggle button, it switches between displaying the data of item.name to displaying data of item.price.
Is this something that can be done within the angular expression of the <TD> tags, or would a function be the way to go?
If I was using regular old JS for example, I might do something like this;
var activeField = item.name;
if (activeField == item.name){
activeField = item.price
} else {
activeField = item.name
}
However, I tried something similar by creating a 'switchField' function in my controller, but Angular reports that 'item is not defined' (essentially a scoping issue) even when defining it at $scope.item.price and $scope.item.name respectively.
You can do something like this, using ngClick, ngShow and ngHide:
<table>
<tr ng-repeat="item in items">
<button ng-click="toggleIt()">Toggle</button>
<td ng-show="toggle">{{item.name}}<td>
<td ng-hide="toggle">{{item.price}}<td>
</tr>
</table>
And add this to the controller:
s.toggle = true;
s.toggleIt = function() {
s.toggle = !s.toggle;
}

Filtering ng-repeat result set is not working

I tried to create a textbox based filtering of a ng-repeat result. Though there is no errors listed, the filtering is not working. What is missing here?
Updated code after making following change:
<tbody ng-repeat="objReview in reviewsList | myCustomFilter:criteria" >
The filter is gettiing called two times for each row. How to make it call only once?
Code
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular-resource.js"></script>
<style type="text/CSS">
table
{border-collapse: collapse;width: 100%;}
th, td
{text-align: left;padding: 8px;}
tr:nth-child(even)
{background-color: #f2f2f2;}
th
{background-color: #4CAF50;color: white;}
</style>
<script type="text/javascript">
//defining module
var app = angular.module('myApp', ['ngResource']);
//defining factory
app.factory('reviewsFactory', function ($resource) {
return $resource('https://api.mlab.com/api/1/databases/humanresource/collections/Reviews',
{ apiKey: 'myKey' }
);
});
//defining controller
app.controller('myController', function ($scope, reviewsFactory)
{
$scope.criteria = "";
$scope.reviewsList = reviewsFactory.query();
});
app.filter('myCustomFilter', function ()
{
return function (input, criteria)
{
var output = [];
if (!criteria || criteria==="")
{
output = input;
}
else
{
angular.forEach(input, function (item)
{
alert(item.name);
//alert(item.name.indexOf(criteria));
//If name starts with the criteria
if (item.name.indexOf(criteria) == 0)
{
output.push(item)
}
});
}
return output;
}
});
</script>
</head>
<body ng-app="myApp">
<div ng-controller="myController">
<label>
SEARCH FOR: <input type="text" ng-model="criteria">
</label>
<table>
<thead>
<tr>
<th>Name</th>
<th>Review Date</th>
</tr>
</thead>
<tbody ng-repeat="objReview in reviewsList | myCustomFilter:criteria" >
<tr>
<td>{{objReview.name}}</td>
<td>{{objReview.createdOnDate}}</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
Further Reading
Is this normal for AngularJs filtering
How does data binding work in AngularJS?
As noted in the comments by Lex you just need to get rid of the prefix 'filter', so change
<tbody ng-repeat="objReview in reviewsList | filter:myCustomFilter:criteria" >
to
<tbody ng-repeat="objReview in reviewsList | myCustomFilter:criteria" >
In addition you should set an initial value for your controller's property criteria as otherwise your initial list will be empty as your filter will not match anything due to the comparison operator === which takes the operands' types into account and critiera will be undefined until you first enter something in your textbox.
app.controller('myController', function ($scope, reviewsFactory)
{
$scope.criteria = '';
$scope.reviewsList = reviewsFactory.data();
});

adding a cell to table using add button

I am trying to make a Dynamic table in Angular.js, in which the user inputs number of columns, in a text box provided. Then add buttons are provided under each column to add as much cells as user wants. The cell will only be added to that column only.
I am using ng-repeat to repeat the number of columns and add buttons.
I am able to get the response of the user in a variable, ie, the column under which the user wants to add the cell.
Can someone please give me a controller to add a cell to the selected column either by using ng-repeat , ng-model or without it.
my table code looks somewhat like this:
<table>
<tr>
<th ng-repeat="n in arr track by $index">SET {{n.setalpha}} </th><!--table heading, dont mind this-->
</tr>
<tr ng-repeat="<!--something goes here-->">
<!--<select ng-model="name" ng-options="options.topicname for options in topics"></select>-->
</tr>
<tr>
<td ng-repeat="n in arr track by $index">
<button ng-click="addSelected($index+1)">add{{$index+1}}</button>
</td>
</tr>
</table>
where: n in arr track by $index , is used to repeat the table heading and add button say 'n' number of times and addSelected($index+1) is a function whose controller is:
$scope.addSelected = function (setno) {
console.log(setno);
$scope.thisset = setno;
}
, $scope.thisset is the variable in which i have the response of the user, ie, the column under which the user wants to add a cell.
NOTE: I want to add the cell in column only under which the user wants. NOT in all columns. MY CODE:
var app = angular.module('topicSelector', []);
app.controller('topicController', function ($scope) {
$scope.arr = [];
$scope.thisset = -1; //tells in which set, cell has to added.
$scope.topics = [
{
topicid: "1",
topicname: "history"
},
{
topicid: "2",
topicname: "geography"
},
{
topicid: "3",
topicname: "maths"
}
];
$scope.DefineSets = function () {
for (var i = 1; i <= $scope.no_of_sets; i++) {
$scope.arr.push({
setno: i,
setalpha: String.fromCharCode(64 + i)
});
};
};
$scope.addSelected = function (setno) {
console.log(setno);
$scope.thisset = setno;
}
});
table {
width: 100%;
}
<!doctype html>
<html ng-app="topicSelector">
<head>
<title>
topic selector
</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="app.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h3>Enter number of sets:</h3>
<div ng-controller="topicController">
<form ng-submit="DefineSets()">
<input type="text" ng-model="no_of_sets" placeholder="number of sets" name="no_of_sets">
<button>Submit</button>
</form>
<br>
<br>
<table>
<tr>
<th ng-repeat="n in arr track by $index">SET {{n.setalpha}} </th>
</tr>
<!--<tr ng-repeat="">
<select ng-model="name" ng-options="options.topicname for options in topics"></select>
</tr>-->
<tr>
<td ng-repeat="n in arr track by $index">
<button ng-click="addSelected($index+1)">add{{$index+1}}</button>
</td>
</tr>
</table>
</div>
</body>
</html>
LINK TO PLUNK PLUNK
In the following example I used lists instead of tables with fle display, IMHO it is a better approach:
(function (angular) {
"use strict";
function GrowController ($log) {
var vm = this;
vm.cols = [];
vm.size = 0;
vm.sizeChanged = function () {
var size = vm.size, cols = vm.cols,
diff = size - cols.length;
$log.debug("Size changed to", size, cols);
if (diff > 0) {
for (var i = 0; i < diff; i++) {
cols.push([]);
}
} else {
cols.splice(diff, -diff);
}
};
vm.addCell = function (index) {
var cols = vm.cols;
$log.debug("Cell added in column", index);
cols[index].push(index + "." + cols[index].length);
};
}
angular.module("app",[])
.controller("GrowController", ["$log", GrowController]);
}(angular));
ul {
list-style: none;
padding: 0;
margin: 0;
}
.cols {
display: flex;
}
.cells {
display: flex;
flex-direction: column;
}
button.add-cell {
display: block;
}
<div ng-controller="GrowController as $ctrl" ng-app="app" ng-strict-di>
<p>
<label for="size">Number of columns(0-10):</label>
<input id="size" type="number" ng-model="$ctrl.size" ng-change="$ctrl.sizeChanged()" min="0" max="10">
</p>
<ul class="cols" ng-if="$ctrl.cols.length">
<li ng-repeat="col in $ctrl.cols">
<button class="add-cell-button" ng-click="$ctrl.addCell($index)">Add cell</button>
<ul class="cells" ng-if="col.length">
<li ng-repeat="cell in col">{{ ::cell }}</li>
</ul>
</li>
</ul>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
Anyway, I Angularjs 1.5.6 you should consider using "Components" instead of "Controllers" and "one-side" bindings.

AngularJS ng-click not firing controller method

I'm sure everyone has seen questions of a similar ilk, and trust me when I say I have read all of them in trying to find an answer. But alas without success. So here goes.
With the below code, why can I not get an alert?
I have an ASP.Net MVC4 Web API application with AngularJS thrown in. I have pared down the code as much as I can.
I know that my AngularJS setup is working correctly because on loading my view it correctly gets (via a Web API call) and displays data from the database into a table (the GetAllRisks function). Given that the Edit button is within the controller, I shouldn't have any scope issues.
NB: the dir-paginate directive and controls are taken from Michael Bromley's excellent post here.
I would appreciate any thoughts as my day has degenerated into banging my head against my desk.
Thanks,
Ash
module.js
var app = angular.module("OpenBoxExtraModule", ["angularUtils.directives.dirPagination"]);
service.js
app.service('OpenBoxExtraService', function ($http) {
//Get All Risks
this.getAllRisks = function () {
return $http.get("/api/RiskApi");
}});
controller.js
app.controller("RiskController", function ($scope, OpenBoxExtraService) {
//On load
GetAllRisks();
function GetAllRisks() {
var promiseGet = OpenBoxExtraService.getAllRisks();
promiseGet.then(function (pl) { $scope.Risks = pl.data },
function (errorPl) {
$log.error("Some error in getting risks.", errorPl);
});
}
$scope.ash = function () {
alert("Bananarama!");}
});
Index.cshtml
#{
Layout = null;
}
<!DOCTYPE html>
<html ng-app="OpenBoxExtraModule">
<head>
<title>Risks</title>
<link href="~/Content/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="~/Scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="~/Scripts/bootstrap.min.js"></script>
<script type="text/javascript" src="~/Scripts/angular.js"></script>
<script type="text/javascript" src="~/Scripts/AngularJS/Pagination/dirPagination.js"></script>
<script type="text/javascript" src="~/Scripts/AngularJS/module.js"></script>
<script type="text/javascript" src="~/Scripts/AngularJS/service.js"></script>
<script type="text/javascript" src="~/Scripts/AngularJS/controller.js"></script>
</head>
<body>
<div ng-controller="RiskController">
<table>
<thead>
<tr>
<th>Risk ID</th>
<th>i3_n_omr</th>
<th>i3_n_2_uwdata_key</th>
<th>Risk Reference</th>
<th>Pure Facultative</th>
<th>Timestamp</th>
<th></th>
</tr>
</thead>
<tbody>
<tr dir-paginate="risk in Risks | itemsPerPage: 15">
<td><span>{{risk.RiskID}}</span></td>
<td><span>{{risk.i3_n_omr}}</span></td>
<td><span>{{risk.i3_n_2_uwdata_key}}</span></td>
<td><span>{{risk.RiskReference}}</span></td>
<td><span>{{risk.PureFacultative}}</span></td>
<td><span>{{risk.TimestampColumn}}</span></td>
<td><input type="button" id="Edit" value="Edit" ng-click="ash()"/></td>
</tr>
</tbody>
</table>
<div>
<div>
<dir-pagination-controls boundary-links="true" template-url="~/Scripts/AngularJS/Pagination/dirPagination.tpl.html"></dir-pagination-controls>
</div>
</div>
</div>
</body>
</html>
you cannot use ng-click attribute on input with angularjs : https://docs.angularjs.org/api/ng/directive/input.
use onFocus javascript event
<input type="text" onfocus="myFunction()">
or try to surround your input with div or span and add ng-click on it.
I've got the working demo of your app, code (one-pager) is enclosed below, but here is the outline:
removed everything concerning dirPagination directive, replaced by ngRepeat
removed $log and replaced by console.log
since I don't have a Web API endpoint, I just populated $scope.Risks with some items on a rejected promise
Try adjusting your solution to first two items (of course, you won't populate it with demo data on rejected promise)
<!doctype html>
<html lang="en" ng-app="OpenBoxExtraModule">
<head>
<meta charset="utf-8">
<title></title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script>
var app = angular.module("OpenBoxExtraModule", []);
app.service('OpenBoxExtraService', function ($http) {
//Get All Risks
this.getAllRisks = function () {
return $http.get("/api/RiskApi");
}
});
app.controller("RiskController", function ($scope, OpenBoxExtraService) {
//On load
GetAllRisks();
function GetAllRisks() {
var promiseGet = OpenBoxExtraService.getAllRisks();
promiseGet.then(function (pl) { $scope.Risks = pl.data },
function (errorPl) {
console.log("Some error in getting risks.", errorPl);
$scope.Risks = [{RiskID: "1", i3_n_omr: "a", i3_n_2_uwdata_key: "b", RiskReference: "c", PureFacultative:"d", TimestampColumn: "e"}, {RiskID: "2", i3_n_omr: "a", i3_n_2_uwdata_key: "b", RiskReference: "c", PureFacultative:"d", TimestampColumn: "e"}, {RiskID: "3", i3_n_omr: "a", i3_n_2_uwdata_key: "b", RiskReference: "c", PureFacultative:"d", TimestampColumn: "e"} ];
});
}
$scope.ash = function () {
alert("Bananarama!");}
});
</script>
</head>
<body>
<div ng-controller="RiskController">
<table>
<thead>
<tr>
<th>Risk ID</th>
<th>i3_n_omr</th>
<th>i3_n_2_uwdata_key</th>
<th>Risk Reference</th>
<th>Pure Facultative</th>
<th>Timestamp</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="risk in Risks">
<td><span>{{risk.RiskID}}</span></td>
<td><span>{{risk.i3_n_omr}}</span></td>
<td><span>{{risk.i3_n_2_uwdata_key}}</span></td>
<td><span>{{risk.RiskReference}}</span></td>
<td><span>{{risk.PureFacultative}}</span></td>
<td><span>{{risk.TimestampColumn}}</span></td>
<td><input type="button" id="Edit" value="Edit" ng-click="ash()"/></td>
</tr>
</tbody>
</table>
<div>
<div></div>
</div>
</div>
</body>
</html>
Thank you all for your help, particularly #FrailWords and #Dalibar. Unbelievably, this was an issue of caching old versions of the javascript files. Doh!
You can't directly use then on your service without resolving a promise inside it.
fiddle with fallback data
this.getAllRisks = function () {
var d = $q.defer();
$http.get('/api/RiskApi').then(function (data) {
d.resolve(data);
}, function (err) {
d.reject('no_data');
});
return d.promise;
}
This will also fix your problem with getting alert to work.

angularjs custom filter on ng-class

Hello I want to create simple search table of data.
I want to highlight if data matched user type input. I done it by doc below since I'm starting using angular i wonder is there any better way? Some custom filter maybe ?
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html ng-app="myApp">
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js"></script>
<style type="text/css">
table td{
padding: 5px;
}
.true{
color: green;
background-color: blue;
}
</style>
</head>
<body>
<div ng-controller="filtrController">
<div>{{search}}<hr/></div>
<input type="text" ng-model="search"/>
<table>
<thead>
<tr>
<td>Name:</td>
<td>Param1:</td>
<td>Param2:</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="foo in data | filter:search">
<!--<td ng-class="{true:'true',false:''}[search === foo.Name]">{{foo.Name}}</td>-->
<td ng-class="customFilter(search,foo.Name)">{{foo.Name}}</td>
<td ng-class="customFilter(search,foo.param1)">{{foo.param1}}</td>
<td ng-class="customFilter(search,foo.param2)">{{foo.param2}}</td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript">
var myApp = angular.module('myApp',[]);
myApp.controller('filtrController',function ($scope)
{
$scope.customFilter = function(search,searchTo)
{
if(search != '')
{
if(searchTo.indexOf(search) > -1) return 'true';
return '';
}
};
$scope.data =
[
{
Name:'name foo1',
param1:'param of foo1',
param2:'param 2 of foo1'
},
{
Name:'name foo2',
param1:'param of foo2',
param2:'param 2 of foo2'
},
{
Name:'name sfoo3',
param1:'param of foo3',
param2:'param 2 of foo3'
},
]
});
</script>
</body>
You just need to custom filter like this:
$scope.customFilter = function(data, searchFor) {
if (angular.isUndefined(data) || angular.isUndefined(searchFor)) return data;
angular.forEach(data, function(item) {
if(angular.equals(item, searchFor)) item.highlighted = true;
});
return data;
}
and html like this
<tr ng-repeat="foo in data | customFilter:{something:1}" ng-class="{highlighted: foo.highlighted}">
Update:
So, I just need to explain my approach in more details. You have data some of which is needed to be highlighted. So you need to set new property in your data and highlight it (using css and ng-class) if property set to true.
For setting this property you can create custom filter that takes your data array, changes it internal state by setting this property and return this array as result. This is what I implemented for you.
Update#2
Same behaviour as ng-filter needed. So here it is.

Resources