I am making a very simple custom directive, which will show, one column of product details, that is present in scope, as 2D array. The custom directive, will have an attribute, that will pass, which column of array has to be shown . Please see following plnkr--
https://plnkr.co/edit/zVIRZ8ADdQB4X8dSFOaJ
From UI I am using this--
<show-products type="name"></show-products>
Currently all data of the array is showing. but i need to show only 1 column of data and that column will be mentioned by directive attribute(for example -name as in plnkr)
In link function I am able to get the column name by using following code-
link: function postlink(scope, element, attr) {
console.log("...type..." + attr["type"]); // prints name
}
but how to pass that field name to template?
template: '<div ng-repeat="x in products">{{x}}</div>' // i need to print only name column here
You get those attributes for the template function as well, just create a function instead of a string and pass your attribute type into the product type variable.
The documentation for the function version of the template can be found in the angular v1 documentation: https://docs.angularjs.org/guide/directive
For more see the snippet below.
<html ng-app="app">
<head>
<title>Directives</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.js"></script>
<script>
angular.module("app", []);
angular.module("app")
.controller("ctrl", ctrl)
.directive("showProducts", showProducts);
function ctrl($scope) {
$scope.products = [{
name: "Apples",
category: "Fruit",
price: 1.20,
expiry: 10
}, {
name: "Bananas",
category: "Fruit",
price: 2.42,
expiry: 7
}, {
name: "Pears",
category: "Fruit",
price: 2.02,
expiry: 6
}];
};
function showProducts() {
return {
template: (child, attributes) => {
return `<div ng-repeat="x in products">{{x["${attributes["type"]}"]}}</div>`
}
};
};
</script>
</head>
<body>
<div class="panel panel-default">
<div class="panel-body" ng-controller="ctrl">
<show-products type="name"></show-products>
</div>
</div>
</body>
</html>
Related
I am defining a custom filter like so:
<div class="idea item" ng-repeat="item in items" isoatom>
<div class="section comment clearfix" ng-repeat="comment in item.comments | range:1:2">
....
</div>
</div>
As you can see the ng-repeat where the filter is being used is nested within another ng-repeat
The filter is defined like this:
myapp.filter('range', function() {
return function(input, min, max) {
min = parseInt(min); //Make string input int
max = parseInt(max);
for (var i=min; i<max; i++)
input.push(i);
return input;
};
});
I'm getting:
Error: Duplicates in a repeater are not allowed. Repeater: comment in item.comments | range:1:2 ngRepeatAction#https://ajax.googleapis.com/ajax/libs/angularjs/1.1.4/an
The solution is actually described here: http://www.anujgakhar.com/2013/06/15/duplicates-in-a-repeater-are-not-allowed-in-angularjs/
AngularJS does not allow duplicates in a ng-repeat directive. This means if you are trying to do the following, you will get an error.
// This code throws the error "Duplicates in a repeater are not allowed.
// Repeater: row in [1,1,1] key: number:1"
<div ng-repeat="row in [1,1,1]">
However, changing the above code slightly to define an index to determine uniqueness as below will get it working again.
// This will work
<div ng-repeat="row in [1,1,1] track by $index">
Official docs are here: https://docs.angularjs.org/error/ngRepeat/dupes
For those who expect JSON and still getting the same error, make sure that you parse your data:
$scope.customers = JSON.parse(data)
I was having an issue in my project where I was using ng-repeat track by $index but the products were not getting reflecting when data comes from database. My code is as below:
<div ng-repeat="product in productList.productList track by $index">
<product info="product"></product>
</div>
In the above code, product is a separate directive to display the product.But i came to know that $index causes issue when we pass data out from the scope. So the data losses and DOM can not be updated.
I found the solution by using product.id as a key in ng-repeat like below:
<div ng-repeat="product in productList.productList track by product.id">
<product info="product"></product>
</div>
But the above code again fails and throws the below error when more than one product comes with same id:
angular.js:11706 Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater
So finally i solved the problem by making dynamic unique key of ng-repeat like below:
<div ng-repeat="product in productList.productList track by (product.id + $index)">
<product info="product"></product>
</div>
This solved my problem and hope this will help you in future.
What do you intend your "range" filter to do?
Here's a working sample of what I think you're trying to do: http://jsfiddle.net/evictor/hz4Ep/
HTML:
<div ng-app="manyminds" ng-controller="MainCtrl">
<div class="idea item" ng-repeat="item in items" isoatom>
Item {{$index}}
<div class="section comment clearfix" ng-repeat="comment in item.comments | range:1:2">
Comment {{$index}}
{{comment}}
</div>
</div>
</div>
JS:
angular.module('manyminds', [], function() {}).filter('range', function() {
return function(input, min, max) {
var range = [];
min = parseInt(min); //Make string input int
max = parseInt(max);
for (var i=min; i<=max; i++)
input[i] && range.push(input[i]);
return range;
};
});
function MainCtrl($scope)
{
$scope.items = [
{
comments: [
'comment 0 in item 0',
'comment 1 in item 0'
]
},
{
comments: [
'comment 0 in item 1',
'comment 1 in item 1',
'comment 2 in item 1',
'comment 3 in item 1'
]
}
];
}
If by chance this error happens when working with SharePoint 2010: Rename your .json file extensions and be sure to update your restService path. No additional "track by $index" was required.
Luckily I was forwarded this link to this rationale:
.json becomes an important file type in SP2010. SP2010 includes certains webservice endpoints. The location of these files is 14hive\isapi folder. The extension of these files are .json. That is the reason it gives such a error.
"cares only that the contents of a json file is json - not its file extension"
Once the file extensions are changed, should be all set.
Just in case this happens to someone else, I'm documenting this here, I was getting this error because I mistakenly set the ng-model the same as the ng-repeat array:
<select ng-model="list_views">
<option ng-selected="{{view == config.list_view}}"
ng-repeat="view in list_views"
value="{{view}}">
{{view}}
</option>
</select>
Instead of:
<select ng-model="config.list_view">
<option ng-selected="{{view == config.list_view}}"
ng-repeat="view in list_views"
value="{{view}}">
{{view}}
</option>
</select>
I checked the array and didn't have any duplicates, just double check your variables.
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys.
Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}
Example
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="angular.js"></script>
</head>
<body>
<div ng-app="myApp" ng-controller="personController">
<table>
<tr> <th>First Name</th> <th>Last Name</th> </tr>
<tr ng-repeat="person in people track by $index">
<td>{{person.firstName}}</td>
<td>{{person.lastName}}</td>
<td><input type="button" value="Select" ng-click="showDetails($index)" /></td>
</tr>
</table> <hr />
<table>
<tr ng-repeat="person1 in items track by $index">
<td>{{person1.firstName}}</td>
<td>{{person1.lastName}}</td>
</tr>
</table>
<span> {{sayHello()}}</span>
</div>
<script> var myApp = angular.module("myApp", []);
myApp.controller("personController", ['$scope', function ($scope)
{
$scope.people = [{ firstName: "F1", lastName: "L1" },
{ firstName: "F2", lastName: "L2" },
{ firstName: "F3", lastName: "L3" },
{ firstName: "F4", lastName: "L4" },
{ firstName: "F5", lastName: "L5" }]
$scope.items = [];
$scope.selectedPerson = $scope.people[0];
$scope.showDetails = function (ind)
{
$scope.selectedPerson = $scope.people[ind];
$scope.items.push($scope.selectedPerson);
}
$scope.sayHello = function ()
{
return $scope.items.firstName;
}
}]) </script>
</body>
</html>
If you call a ng-repeat within a < ul> tag, you may be able to allow duplicates. See this link for reference.
See Todo2.html
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: sdetail in mydt, Duplicate key: string: , Duplicate value:
I faced this error because i had written wrong database name in my php api part......
So this error may also occurs when you are fetching the data from database base, whose name is written incorrect by you.
My JSON response was like this:
{
"items": [
{
"index": 1, "name": "Samantha", "rarity": "Scarborough","email": "maureen#sykes.mk"
},{
"index": 2, "name": "Amanda", "rarity": "Vick", "email": "jessica#livingston.mv"
}
]
}
So, I used ng-repeat = "item in variables.items" to display it.
There's this template that I call multiple times on the same page:
<div ng-controller="templateController">
<div class="source">
<div ng-repeat="item in info">
<div class="content" data-value="{{item.ID}}">{{item.name}}</div>
</div>
</div>
<br style="clear:both" />
<div class="receiver"></div>
</div>
and then I want to select all the elements with class="content" within each template scope in order to manipulate them.
How can I achieve this using JS?
EDIT :
Plunker
In this planker the console.log should print "1" twice and its printing "1" and then "2" when the template loads the second time
After more explanation here is a working example:
https://plnkr.co/edit/n5GOd6MDLyvG4ZAsuLvf?p=preview
The main idea is creating 2 lists and iterating over both and just moving data around between them on click.
angular.module("demo", []);
angular
.module("demo")
.controller("demoController", ["$scope", function($scope) {
}]);
angular
.module("demo")
.controller("templateController", ["$scope", "$timeout", function($scope, $timeout) {
$scope.sourceList = [
{
name: "john",
ID: 1
},
{
name: "Edward",
ID: 0
},
{
name: "Carl",
ID: 2
}
];
$scope.receiverList = [
{
name: "Bob",
ID: 1
}
];
$scope.moveToReceiver = function(item){
$scope.receiverList.push(item);
$.each($scope.sourceList, function(i){
if($scope.sourceList[i].name == item.name){
$scope.sourceList.splice(i, 1);
return false;
}
});
}
}]);
Most of the time you do not want to do DOM manipulation in Angularjs and instead hook into events with your controller. If you have to do DOM manipulation in AngularJS you would use directives
Docs on Creating a Directive that Manipulates the DOM
You could then use your link function to grab the children of your directive's element
function link(scope, element, attrs) {
var content = angular.element(element[0].querySelector('.content'));
}
https://stackoverflow.com/a/17329630/2033671
I am learning inherited/isolated scopes in angular directives and struck with some basic concepts. please see the plunker below.
Example.
Scenario1:
I have 2 directives (book and details). I am displaying two "Book Details" containers and toggling book name by sending custom attributes like this.
<book bookdetails="book" collapsed="yes" list="list"></book>
<book bookdetails="book1" collapsed="no" list="list"></book>
Question: Is this the right way to handle displaying things in 2 different containers?
Scenario 2:
I want to hide the author details section in container 1 but show in container2 on load. How to accomplish that?
When I use this line below it will hide and show both author details section but I want to keep it separate.
<details collapsed="yes"></details>
I know I am lacking basic skills using inherited/isolated scopes. Can someone educate me?
It's OK to use nested directives like you've used so you can do everything related to the details pane in the details controller like removing items from the books list.
If you wouldn't do any logic in details controller and just include some html I would just use ng-include.
Some points I've detected during improving your code:
Template markups are partial html files, so no need to add header, body etc. Just add your markup that you need in your directive.
I've created one model array books that you can iterate with ng-repeat and not two separate scope variables. That's easier to add more books.
I wouldn't pass the collapsed state to directive isolated scope. I would add it to the book model then you can have independent states of the details panes.
You could also create a collapsed array scope variable separate from your model and use it like ng-hide='collapsed[$index]' if you don't like to add it to your model.
Don't compare to the string yes. It makes things more complicated. It's better to use true or false.
The list you're passing is OK if you'd like to use one list for every details pane. But I think you need them independent from each other so add it to your book model.
For toggeling a value you can use the js shorthand: collapsed = !collapsed;. It takes the value of collapsed and inverts it and re-asigns it to collapsed.
Details directive: You don't need to pass attributes to a directive that doesn't use isolated scope. Instead you can directly use the inherited scope of the parent.
Note: I think you should have a look at angular-ui-bootstrap and use an accordion instead of your manually created panes later. But for learning directives your code is OK.
Please have a look at your updated code below or in this plunker.
If something is not clear, feel free to add a comment and I'll try to help.
angular.module('plunker', [])
.controller('MainCtrl', function($scope) {
$scope.books = [{
id: 0,
name: 'Building modern ASP.NET 5',
author: {
name: 'name1',
age: 31,
country: 'USA'
},
collapsed: false,
list: [{
id: 0,
name: 'book1'
}, {
id: 1,
name: 'book2'
}, {
id: 2,
name: 'book3'
}]
}, {
id: 1,
name: 'AngularJS',
author: {
name: 'name2',
age: 27,
country: 'USA'
},
collapsed: true,
list: [{
id: 0,
name: 'book1'
}, {
id: 1,
name: 'book2'
}, {
id: 2,
name: 'book3'
}]
}];
//$scope.list = ["book1", "book2", "book3"];
}).directive('book', function() {
return {
restrict: 'E',
templateUrl: 'book.html',
scope: {
bkdet: "=bookdetails"
//list: "="
//collapsed: "#"
},
controller: function($scope) {
$scope.toggleDetails = function() {
$scope.bkdet.collapsed = !$scope.bkdet.collapsed;
updateCaption();
};
function updateCaption() {
$scope.hypshowhide = $scope.bkdet.collapsed ? 'show details' : 'hide details';
}
// first run
updateCaption();
/*if ($scope.collapsed == 'yes')
{
$scope.dethide = true;
}
else {
$scope.dethide = false;
} */
//$scope.hypshowhide = 'show details';
}
}
})
.directive('details', function() {
return {
restrict: 'E',
templateUrl: 'details.html',
controller: function($scope) {
/*console.log($scope.bkdet.collapsed);
if (!$scope.bkdet.collapsed) { //== 'yes') {
$scope.dethide = true;
}
else {
$scope.dethide = false;
}*/
$scope.removeItem = function(index) {
$scope.bkdet.list.splice(index, 1);
}
}
}
})
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="plunker">
<div ng-controller="MainCtrl">
<div class="container">
<book bookdetails="book" ng-repeat="book in books"></book>
</div>
</div>
<script type="text/ng-template" id="book.html">
<div class="row">
<div class="panel panel-default">
<div class="panel-heading">
<h1>Book Details</h1>
</div>
<div class="panel-body">
<a class="pull-right" href="#" ng-click="toggleDetails(collapsed)">{{hypshowhide}}</a>
<div>
<!--ng-hide="dethide">-->
{{bkdet.name}}
</div>
<!--<details collapsed="no"></details>-->
<details></details>
</div>
</div>
</div>
</script>
<script type="text/ng-template" id="details.html">
<div class="container" ng-hide="bkdet.collapsed">
<div class="row">
<ul class="list-group list-unstyled">
<!--<li>
<h1>Author:</h1>
</li>
<li>
<ul>-->
<li>
<strong>Author</strong>
{{bkdet.author.name}}
</li>
<li>
<strong>Age</strong>
{{bkdet.author.age}}
</li>
<li>
<strong>Country</strong>
{{bkdet.author.country}}
</li>
<li>
<div ng-if="bkdet.list.length == 0">
<p>No books here!</p>
</div>
<div ng-repeat="c in bkdet.list">
<p>
{{c.name}}
<button class="btn btn-danger" ng-click="removeItem($index)">X</button>
</p>
</div>
</li>
<!--</ul>
</li>-->
</ul>
</div>
</div>
</script>
</div>
I have a list of options pulled from the database via json based on product selection in angular.js
Here's a sample code:
<ion-checkbox ng-repeat="extra in extras" ng-model="order.extras" checklist-value="{{ extra.id }}"><strong>{{ extra.name }}</strong></ion-checkbox>
I want a user to be able to select multiple extras but can't seem to be able to bind these selections.
I think it could work with ng-model="order.extras[extra.id]" then you can track the checked extras in order.extras.
Please have a look at the demo below or at this jsfiddle.
angular.module('demoApp', ['ionic'])
.controller('mainController', mainController);
function mainController($scope) {
$scope.order = {};
$scope.extras = [
{
id: 0,
name: 'first'
},
{
id: 1,
name: 'second'
},
{
id: 2,
name: 'third'
}
]
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/ionic/1.2.4/css/ionic.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ionic/1.2.4/js/ionic-angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ionic/1.2.4/js/ionic.bundle.js"></script>
<div ng-app="demoApp" ng-controller="mainController">
<ion-content>
<ion-checkbox ng-repeat="extra in extras" ng-model="order.extras[extra.id]" checklist-value="{{ extra.id }}"><strong>{{ extra.name }}</strong></ion-checkbox>
current order: {{order}}
</ion-content>
</div>
I'm using a Moltin Cart which I want to loop through, so I can read it in Angular JS and then
I load the cart into a variable, which I'd like to <div ng-repeat-start="item in cart.contents"> and loop through the items in the cart.
The issue I'm having is really a pretty basic JavaScript issue, that if I console.log the cart.contents I get this:
Object{
33bb9f991617377b9b8333a79ca9ce2c: Object { ... cart contents... },
1ba14b804d0d81e49397b004e5f1c6d1: Object { ... cart contents... },
33bb9f991617377b9b8333a79ca9ce2c: Object { ... cart contents... },
}
Is there a way that I can fix this either in:
Laravel
Angular JS
or Classic JavaScript
I think what you're trying to do is this:
angular.module('myapp', [])
.controller('MyController', function($scope) {
$scope.objectOfObjects = {
"ASDF": { name: 'First', price: 100 },
"QWER": { name: 'Second', price: 200 },
"FGHJ": { name: 'Third', price: 500 }
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myapp">
<div ng-controller="MyController">
When iterating over an object of objects, there is no guarantee about the order:
<div ng-repeat-start="item in objectOfObjects">{{item.name}}</div>
<div ng-repeat-end>{{item.price}}<br/><br/></div>
</div>
</body>