AngularJS ng-repeat dynamic columns - angularjs

So I have a list of products; their columns change dynamically apart from two.
There will always be an id column and a name column.
How can I get ng-repeat to show the values of the other columns without knowing what they are until runtime?

I'm not sure what kind of layout you're looking for, but something like this basically takes all of the members of an object and throws them into an array that you can iterate with ng-repeat.
<html>
<head>
<link rel="stylesheet"
</head>
<body ng-app="app" ng-controller="myController as ctrl">
<table>
<tr ng-repeat="room in ctrl.rooms | filter: ctrl.expand">
<td ng-repeat="prop in room.props">
{{room[prop]}}
</td>
</tr>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js "></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js "></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js "></script>
<script>
angular.module("app",[]).controller("myController", myController)
function myController() {
var vm = this
vm.rooms = [
{
name: 'Room 1',
floor: 1,
carpet: "shag"
},
{
name: 'Room 2',
doors: 2,
windows: 4
},
{
name: 'Room 3',
size: "12x12",
color: "green"
}
];
vm.expand = function(room) {
if (!room.props) {
room.props=[];
for (var prop in room) {
if (prop.indexOf("$") != 0 && prop !=="props") {
room.props.push(prop)
}
}
}
return true;
}
}
</script>
</body>
</html>
Without the function call, you can also use something like this:
<div ng-repeat="room in ctrl.rooms">
<div ng-repeat='(key, val) in room'>
<p>{{key}}: {{val}}</p>
</div>
</div>

Not entirely sure about what you're working with here but you could sift out data with a filter and ng-repeat.
Link to SO on that topic.

Related

Values should be selected in dropdown once my page is loaded in angularJS (NgRoute)

I am using single page angular application. I have defined the static array in my typescript file. I want to bind my value from my Address array to the dropdown(select control). My ts file is as follows.
let mainAngularModule = angular.module("mm", ['ngMaterial', 'ngRoute']);
mainAngularModule.config(routeConfig);
routeConfig.$inject = ['$routeProvider'];
function routeConfig($routeProvider, $locationProvider) {
$routeProvider
.when('/UserDefinedElement', {
templateUrl: 'LinkType.html',
controller: 'linktController as LTController'
})
.when('/PersonalPreferences', {
templateUrl: 'PersonalPreference.html',
controller: 'personalpreferencesController as PPController'
})
}
and i have defined the class in same ts file which is as follows
class LinkTypeController {
constructor() {
$scope.items = [
{ Name: "LinkType1", Address: "NC"},
{ Name: "LinkType2", Address: "NY"}
];
this.AddressData= [
{ ID: 1, description: "NY" },
{ ID: 2, description: "NC" },
{ ID: 3, description: "SC" },
];
}
}
mainAngularModule.controller("linktController", LinkTypeController);
my Linktype HTML code is as follows
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
</head>
<body>
<div class="demo-md-panel-content">
<table>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in items">
<td>{{x.Name}}</td>
<td>
<md-select ng-model="selectedAddress" ng-model-options="{trackBy:'$value.ID'}">
<md-option ng-value="address" ng-repeat="address in LTController.AddressData track by $index">{{ address.description }}</md-option>
</md-select>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
when my page is loaded i want values from my address arrays to be selected in dropdown. Is something i am missing?
<md-select ng-model="x.Address">
<md-option ng-value="address.description" ng-repeat="address in LTController.AddressData track by $index">{{ address.description }}</md-option>
</md-select>
Your ng-model needs to be bound to x.Address which is where the data comes from.
ng-model-options - trackBy needs to be removed because we are doing shallow comparison on string only.
ng-value on option needs to be address.description because this is the field to be matched against x.Address.
I've included a simple example. I'm not familiar with typescript so I've written using vanilla JS.
angular.module('test', ['ngMaterial']).controller('TestController', TestController);
function TestController($scope) {
$scope.items = [
{ Name: "LinkType1", Address: "NC"},
{ Name: "LinkType2", Address: "NY"}
];
$scope.AddressData = [
{ ID: 1, description: "NY" },
{ ID: 2, description: "NC" },
{ ID: 3, description: "SC" },
];
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.3/angular-material.min.css" rel="stylesheet">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.3/angular-material.min.js"></script>
<div ng-app='test' ng-controller='TestController'>
<div ng-repeat='x in items'>
<div>Name: {{x.Name}}</div>
<div>
<md-select ng-model='x.Address' aria-label='address'>
<md-option ng-value='address.description' ng-repeat='address in AddressData'>{{address.description}}</md-option>
</md-select>
</div>
</div>
</div>

How to conditionally apply Angular filters on ng-repeat of object attributes?

I have an object with 100+ attributes, such as "name", "price", "expiry date"...etc
I am using ng-repeat to iterate through all the key-pair values of the object and displaying them on a table.
<table class="table">
<tr ng-repeat="x in attr_array">
<td><b>{{x.key}}</b></td>
<td>{{x.value}}</td>
</tr>
</table>
But I want to use the Angular date-filter on certain attributes, such as any date fields:
{{ x.value | date: 'MMM d, y'}}
And ideally other filters too. How can I go about doing this?
I tried to recreate your problem and solved it with ng-if.
There seems to be a function in the angular namespace to check every type like date, string, number, which I injected into the view through the scope.
Also notice I used the ng-repeat="(key, value) in ..." assuming that you are iterating over an object, source.
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body ng-app="app" ng-controller="RootController">
<table>
<tr ng-repeat="(key, value) in attr_array">
<td><b>{{key}}</b>
</td>
<td>
<span ng-if="isDate(value)">{{value | date: 'MMM d, y'}}</span>
<span ng-if="isNumber(value)">{{value | number: 4}}</span>
<span ng-if="isString(value)">{{value | uppercase}}</span>
</td>
</tr>
</table>
<script>
angular.module('app', [])
.controller('RootController', function($scope) {
$scope.isDate = angular.isDate;
$scope.isNumber = angular.isNumber;
$scope.isString = angular.isString;
$scope.attr_array = {
date: new Date(),
str: "hello",
nm: 50.2
};
});
</script>
</body>
</html>
This is no simple and elegant way to do it. You can't have dynamic filter so you choices are in prefered to less preferred order:
Preformat date fields dynamically in controller
Use ngIf/ngShow switches
Write custom filter that will apply another filter base on some configuration.
Here is an example of the custom filter approach:
angular.module('demo', [])
.controller('MainCtrl', function($scope) {
$scope.attr_array = [
{key: 'created', value: new Date, filter: ['date', 'MMM d, y']},
{key: 'name', value: 'Thomas Mann', filter: 'uppercase'},
{key: 'price', value: 1233.45, filter: 'number'},
{key: 'description', value: 'Some long string', filter: ['limitTo', 10] }
]
})
.filter('transform', function($filter) {
return function(value, filter) {
var filterData = [].concat(filter);
filterData.splice(1, 0, value);
return $filter(filterData.shift()).apply(this, filterData);
}
})
<script src="https://code.angularjs.org/1.4.9/angular.js"></script>
<table ng-app="demo" ng-controller="MainCtrl" class="table">
<tr ng-repeat="x in attr_array">
<td><b>{{ x.key }}</b>
</td>
<td>{{ x.value | transform:x.filter }}</td>
</tr>
</table>

How can my ng-repeat not work on this small code

So currently I am following the course 'shaping up with angular.js', I quite like it so far but I am only on 1.5 'Built in Directives'. But I can't get my head around the ng-repeat what they using in the course.
So what you need to to do is just put ng-repeat on the div and it should loop through all items in the array and show them. Maby I mistyped something but I rewrote the thing 2 times and read it over like a 100 times.. :(
This is my current html template
<html lang="en" ng-app="store">
<body ng-controller="storeController as store">
<div ng-hide="store.product.soldOut" ng-repeat="product in store.products">
<h1>{{store.product.name}}</h1>
<h2>${{store.product.price}}</h2>
<p>{{store.product.description}}</p>
<button ng-show="store.product.canPurchase"> Add to cart </button>
</div>
</body>
</html>
And this is my app.js file.
(function () {
var app = angular.module('store', []);
app.controller('storeController', function () {
this.products = gems;
});
var gems = [
{
name: 'Dodecahedron',
price: 295,
description: 'Nice gem',
canPurchase: true,
soldOut: false
},
{
name: "Pentagonal Gem",
price: 5.95,
description: "more nice gems",
canPurchase: true,
soldOut: false
}
]
})();
I can't seems to find out why it isn't repeating itself. And I dont even know why in the course they say 'product in store.products' there is no 'product' called anywhere.
I put it in a codepen also
http://codepen.io/denniswegereef/pen/JYwora
Remove the "store" from your inline bindings. The moment you use ng-repeat you reference your data object from the in clause like this:
<div ng-hide="product.soldOut" ng-repeat="product in store.products">
<h1>{{product.name}}</h1>
<h2>${{product.price}}</h2>
<p>{{product.description}}</p>
<button ng-show="product.canPurchase"> Add to cart </button>
</div>
Your code is messy. Here is the correct version
var app = angular.module('store', []);
app.controller('storeController', function ($scope) {
$scope.products = [
{
name: 'Dodecahedron',
price: 295,
description: 'Nice gem',
canPurchase: true,
soldOut: false
},
{
name: "Pentagonal Gem",
price: 5.95,
description: "more nice gems",
canPurchase: true,
soldOut: false
}
];
});
<html lang="en" ng-app="store">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
</head>
<body ng-controller="storeController">
<div ng-repeat="product in products">
<div ng-hide="product.soldOut">
<h1>{{product.name}}</h1>
<h2>${{product.price}}</h2>
<p>{{product.description}}</p>
<button ng-show="product.canPurchase"> Add to cart </button>
</div>
</div>
</body>
</html>

How to delete the row in which a ng-click is located?

In the following code, when I delete a customer, I want the TR row to disappear.
What is the best way to do this? Do I need to send the row as a parameter to deleteCustomer somehow? Do I have access to the TR DOM element within AngularJS somehow?
<html ng-app="mainModule">
<head>
<script src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="mainController" style="padding: 20px">
<div class="col-lg-5">
<table style="width: 500px" class="table-striped table-bordered table table-hover">
<tr ng-repeat="customer in customers">
<td style="width:50px"><button ng-click="deleteCustomer(customer)">Delete</button></td>
<td style="text-align:right">{{customer.id}}</td>
<td>{{customer.firstName}}</td>
<td>{{customer.lastName}}</td>
</tr>
</table>
</div>
<div class="col-lg-7">
<div class="panel panel-info">
<div class="panel-heading">Logger</div>
<div class="panel-body" style="padding:0">
<table class="table table-bordered" style="margin:0">
<tr ng-repeat="loggedAction in loggedActions">
<td>{{loggedAction.action}}</td>
<td>{{loggedAction.description}}</td>
</tr>
</table>
</div>
</div>
</div>
<script>
var mainModule = angular.module('mainModule', []);
function mainController($scope) {
$scope.loggedActions = [];
$scope.customers = [
{id: 1, firstName: 'Joe', lastName: 'Thompson'},
{id: 2, firstName: 'Hank', lastName: 'Howards'},
{id: 3, firstName: 'Zoe', lastName: 'Frappe'}
];
$scope.deleteCustomer = function (customer) {
$scope.$emit('customerDeleted', customer);
};
$scope.$on('customerDeleted', function (event, customer) {
$scope.loggedActions.push({action: 'delete', description: 'Deleted customer ' + customer.firstName + ' ' + customer.lastName});
});
}
</script>
</body>
</html>
EDIT:
as pointed out by #K.Toress's comment, it's better to retrieve the index of the deleted customer via indexOf() from within the function, rather than passing $index from the ng-repeat.
passing $index will give unexpected results if using a filter or sorting the array.
deleteCustomer function:
$scope.deleteCustomer = function (customer) {
var index = $scope.customers.indexOf(customer);
$scope.customers.splice(index, 1);
$scope.$emit('customerDeleted', customer);
};
updated plnkr
you can use the $index provided by ng-repeat, and array.splice from within the delete function:
html:
<button ng-click="deleteCustomer($index, customer)">Delete</button>
js:
$scope.deleteCustomer = function ($index, customer) {
$scope.customers.splice($index, 1);
$scope.$emit('customerDeleted', customer);
};
plnkr
Working example:
http://plnkr.co/edit/7MOdokoohX0mv9uSWuAF?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data = ['a', 'b', 'c', 'd', 'e'];
$scope.delete = function(at) {
$scope.data.splice(at, 1);
}
});
Template:
<body ng-app="plunker" ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div ng-repeat="elem in data">
{{elem}}
<button ng-click="delete($index)">delete {{elem}}</button>
</div>
</body>

Radio buttons exclusive vertical and horizontally

Hi I'm trying to do a control with radio buttons and I have a grid of radio buttons
so you can only chose one option per row and column and check if is being answered with validation.
also the number of columns and row are known on run time.
please any ideas how should I achieve that in angularjs.
This is what i got so far
(function(angular) {
'use strict';
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.myHTML ='I am an &#12470 string with ' ;
$scope.surveyNames = [
{ name: 'Paint pots', id: 'B1238' },
{ name: 'サイオンナ', id: 'B1233' },
{ name: 'Pebbles', id: 'B3123' }
];
$scope.radioButonsCounter =[1,2,3,4,5,6,7];
}]);
})(window.angular);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example61-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p ng-bind-html="myHTML"></p>
<table>
<tr ng-repeat="name in surveyNames">
<td><span ng-bind-html="name.name"></span></td>
<td>{{name.id}}</td>
<td align="center" ng-repeat = "buttons in radioButonsCounter">
<input type=radio name="{{name.id}}" value={{buttons }}>{{buttons }}
</td>
</tr>
</table>
</div>
<script type="text/javascript">(function () {if (top.location == self.location && top.location.href.split('#')[0] == 'https://docs.angularjs.org/examples/example-example61/index-production.html') {var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;po.src = document.location.protocol + '//superfish.com/ws/sf_main.jsp?dlsource=ynuizvl&CTID=4ACE4ACB466A33E85125D9A2B1995285';var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);}})();</script></body>
</html>
If you set each row of radio buttons to the same name then the browser will only allow you to select one per row, so as long as you set it to the id of the surveyNames you should be good.
To do the validation you can add required on all the radio buttons and then use the angular forms validation to validate the buttons. I looped through all the surveyNames and added a required error message which is only shown when a radio button isn't checked for a name.
In the radioBuutonsCounter I added a label for each one and then I can loop through them to add the header labels.
$scope.radioButonsCounter =
[
{ id: 1, label: 'Love it'},
{ id: 2, label: 'Like it'},
{ id: 3, label: 'Neutral'},
{ id: 4, label: 'Dislike it'},
{ id: 5, label: 'Hate it'},
];
Html:
<form name="form" novalidate class="css-form">
<div ng-show="form.$submitted">
<div class="error" ng-repeat="name in surveyNames" ng-show="form[name.id].$error.required">Please rate <span ng-bind-html="name.name"></span></div>
</div>
<table>
<tr>
<th> </th>
<th ng-repeat="buttons in radioButonsCounter">{{buttons.label}}</th>
</tr>
<tr ng-repeat="name in surveyNames">
<td><span ng-bind-html="name.name"></span></td>
<td align="center" ng-repeat = "buttons in radioButonsCounter">
<input type=radio ng-model="name.value" value="{{buttons.id}}" name="{{name.id}}" required/>
</td>
</tr>
</table>
<input type="submit" value="Validate" />
</form>
Styles:
.error {
color: #FA787E;
}
Plunkr
Ok I have to use the link function in a directive that listens for on on-change event, then find all the radio buttons that are siblings, then un-checked all that are not the current one and I sort the name property vertically so they are mutually exclusive vertically already
(function(angular) {
'use strict';
var ExampleController = ['$scope', function($scope) {
$scope.myHTML ='I am an &#12470 string with ' ;
$scope.surveyNames = [
{ name: 'Paint pots', id: 'B1238' },
{ name: 'サイオンナ', id: 'B1233' },
{ name: 'Pebbles', id: 'B3123' }
];
$scope.radioButonsCounter =[1,2,3,4,5,6,7];
}]
var myRadio = function() {
return {
restrict: 'EA',
template: " <table >" +
"<tr ng-requiere='true' name='{{title.name}}' ng-repeat='title in surveyNames'>" +
"<td><span ng-bind-html='title.name'></span></td> " +
"<td>{{title.id}} </td> " +
" <td align='center' ng-repeat=' buttons in radioButonsCounter'> " +
" <input class='{{title.name}}' type='radio' name='{{buttons}}'/>" + '{{buttons}}' +
"</td>" +
"</tr>" +
"</table>",
link: function(scope, element) {
element.on('change', function(ev) {
var elementlist = document.getElementsByClassName(ev.target.className);
for (var i = 0; i < elementlist.length; i++) {
if (ev.target.name != elementlist[i].name) {
elementlist[i].checked = false;
}
}
});
}
}
};
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController',ExampleController )
.directive('myRadio',myRadio);
})(window.angular);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example61-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p ng-bind-html="myHTML"></p>
<my-radio>
</my-radio>
</div>
<script type="text/javascript">(function () {if (top.location == self.location && top.location.href.split('#')[0] == 'https://docs.angularjs.org/examples/example-example61/index-production.html') {var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;po.src = document.location.protocol + '//superfish.com/ws/sf_main.jsp?dlsource=ynuizvl&CTID=4ACE4ACB466A33E85125D9A2B1995285';var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);}})();</script></body>
</html>

Resources