AngularJS: Directive repetition, works as attribute but not as element - javascript

If I write twice the same directive as attribute, I get twice the result, but when I write it twice as element, I only get the result once, why?
I have a very simple directive:
.directive("ngMyText", function(){
return {
restrict: 'AE'
};
})
An $scope with a collection of items:
$scope.items = [
{ text:"AAA", show:true },
{ text:"BBB", show:true }
];
Therefore, when doing this:
<div ng-controller="myController">
<div class="container">
<div data-ng-my-text ng-repeat="item in items | filter:{show:true}" ng-bind="item.text"></div>
<div data-ng-my-text ng-repeat="item in items | filter:{show:true}" ng-bind="item.text"></div>
</div>
<div class="container">
<ng-my-text ng-repeat="item in items | filter:{show:true}" ng-bind="item.text" />
<ng-my-text ng-repeat="item in items | filter:{show:true}" ng-bind="item.text" />
</div>
</div>
I would expect to get the collection rendered twice in each container, but in the second container only happens once. Why does this happen?
I have created an runable example with the problem: http://jsfiddle.net/vtortola/mzAPk/
Cheers.

See Are (non-void) self-closing tags valid in HTML5? This
<ng-my-text ng-repeat="item in items | filter:{show:true}"
ng-bind="item.text" />
should be
<ng-my-text ng-repeat="item in items | filter:{show:true}"
ng-bind="item.text"></ng-my-text>

Related

keeping track of nested ng-repeat index's

So I have a nested ng-repeat like so:
<div ng-repeat="data in flow" ng-init="$flowIndex = $index">
Index: {{ $index }}
<div ng-click="flow.splice($index, 1)">Delete me</div>
<div ng-repeat="inside_data in flow[$flowIndex]">
Inside index: {{ $index }}
</div>
</div>
I want to be able to delete index in my $flowIndex. However if I have something like this:
0
1
2
3
And I delete index 2. If I go and delete index 3, it isn't found because ng-init variable still things its at index 3 but really its not at index 2.
Does anyone know a work around?
You can get rid of $flowIndex, it's not necessary, you can use $parent.$index instead, when you are using ngRepeat it creates a child scope and $index is part of that scope. Also consider moving your deleting logic into the controller.
Controller:
$scope.delete = function ($index) {
$scope.flow.splice($index, 1);
};
Html:
<div ng-repeat="data in flow">
Index: {{ $index }}
<div ng-click="delete($index)">Delete me</div>
<div ng-repeat="inside_data in flow[$index]">
Inside index: {{ $parent.$index }} -> {{ $index }}
</div>
</div>
I believe you can get the parent index like so:
$parent.$index
As mentioned in this answer: passing 2 $index values within nested ng-repeat
That way you don't have to worry about your variable being out of sync with your current state.
I just tested your similar code with some dummy data strings and the delete appears to work. I made some updates to your code to better analyze it.
// Code goes here
var myApp = angular.module('myApp',[]);
myApp.controller('MyController', ['$scope', function($scope) {
$scope.flow = [
["test01","test02","test03"],
["test11","test12","test13"],
["test21","test22","test23"],
["test31","test32","test33"],
["test41","test42","test43"]
]
;
}]);
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.5.8" data-semver="1.5.8" src="//opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<section ng-controller="MyController">
<div ng-repeat="data in flow">
Index: {{ $index }}
<div ng-click="flow.splice($index, 1)">Delete me [{{ $index }}]</div>
<div ng-repeat="inside_data in flow[$index]">
Inside index: {{ $index }} : {{ inside_data }}
</div>
<hr>
</div>
</section>
</body>
</html>

Can I pass args in the html tag of my custom angular element?

I am using angular 1, I have a directive being returned as an element called "store". So the store element will ng-repeat through each object in an array of json I am using to store all of my products.
<div class="list-group">
<div class="list-group-item" ng-repeat="product in store.products">
<h3>{{product.name}} <em class="pull-right">{{product.price | currency}} Spaces left: {{product.spots}}</em></h3>
</div>
</div>
This works fine on one of my pages where I want to angular repeat through all of the objects. On another page though I want to show the same information but I only want to show certain products. For example, if the key value for feature is set to true.
Is there a way in my tag to pass arguments to specify that I only want to display the first two elements in the array or only display it if it equals a certain key value?
You can use filters in your ng-repeat directive:
<div class="list-group">
<div class="list-group-item" ng-repeat="product in store.products | query:feature | limitTo:5">
<h3>{{product.name}} <em class="pull-right">{{product.price | currency}} Spaces left: {{product.spots}}</em></h3>
</div>
</div>
You can pass arguments to your custom directives as well. Where you define your directive, you can write:
return {
restrict: 'EA',
scope: {
max: '=',
},
...
template: '
<div class="list-group">
<div class="list-group-item" ng-repeat="product in store.products | query:feature | limitTo:max">
<h3>{{product.name}} <em class="pull-right">{{product.price | currency}} Spaces left: {{product.spots}}</em></h3>
</div>
</div>'
};
And now you can write:
<store max="5" />
This will iterate over the first 5 elements in the array and only display them if feature is true.

ng-Repeat in Angular.js Not iterating according to the items index in the json

I am trying to execute the below code where I'm trying use ng-repeat to iterate through a key:value pair.
But ng-repeat is not following the index instead of rendering the second key value its rendering the thrid index
HTML:
<div ng-app ng-controller="ItemsCtrl">
Type anything in the first box.
<div ng-repeat="(key, value) in item">
<input type="text" ng-model="item[key]" />
</div>
{{item | json}}
</div>
Javascript Controller:
function ItemsCtrl($scope) {
$scope.item = {
"1": "one",
"2": "two",
"10": "ten"};
}
Below is the jsfiddle link for the problem http://jsfiddle.net/nMjet/13/
Thanks #Blackhole.
Below the modified Code
HTML:
<div ng-app ng-controller="ItemsCtrl">
Type anything in the first box.
<div ng-repeat=" item in items">
<input type="text" ng-model="item[$indexxcdxdd]" />
</div>
{{item | json}}
</div>
Javascript Controller:
function ItemsCtrl($scope) {
$scope.items = ['one','two','ten'];
}

nested ng-repeat with outer ng-repeat item affecting the inner ng-repeat logic

My code
<div ng-repeat='n in [] | range:(my_works.items.length/3)+1'>
<div class="table-row" style="position:absolute;left:13px;{{row_gap_style(n)}}" class='ng-scope'>
<div class="book_container" ng-repeat='item in my_works.items.slice($index*3,($index*3+3))' style="position:absolute;top:0px;{{col_gap_style($index)}}">
<div id="" title="{{ item.Story.title}}" class="cover_container fetched {{check_published(item)}}" rel="<?php echo $rel; ?>">
What I want to do is
if n in the outer ng-repeat is 0, then the div.book_container will look like this instead:
<div class="book_container" ng-show="n == 0" ng-repeat='item in my_works.items.slice($index*2, ($index*2+2))' style="position:absolute;top:0px;{{col_gap_style($index+1)}}">
otherwise, the div should be as before.
How do I accomplish this?
You could create a directive for the same content and then use ng-if to set different attributes dependent on the value of n:
.directive('bookContainer', function() {
return {
scope: {
'items': '=',
'colstyle': '#'
},
restrict: 'E',
template: '<p ng-repeat="item in items">book: {{item}} {{colstyle}}</p>'
};
});
View:
<div ng-repeat='n in []'>
<book-container ng-if='n==0' items='my_works.slice($index,1)' colstyle='s1'></book-container>
<book-container ng-if='n!=0' items='my_works.slice($index,2)' colstyle='s2'></book-container>
</div>
Fiddle
The demo has been greatly simplified but shows the technique which you could hopefully use.

How can I give each ng-model in an ng-repeat a unique identifier?

I have a page with multiple AngularUI sliders, and a span displaying the value of each slider with an ng-bind. It looks something like this:
<div class="formitem" ng-repeat="food in foodlist" >
<div ui-slider="{range: 'min'}" min="0" max="{{ unit.max }}" ng-model="demoVals.slider" class="food-slider" >
<div class="begin">1</div>
<div class="end"> {{ unit.max }} {{ food.uid }} </div>
</div>
<span ng-bind="demoVals.slider"></span>
</div>
I want the ng-model to be unique for each food item, so something like demoVals.slider57, where 57 is the output of {{ food.uid }}. I can get {{ food.uid }} to print out in the template just fine, but if I try to add it to the ng-model or ng-bind, I just get this:
ng-model="demoVals.slider[food.uid]"
How can I add the food.uid to the ng-model of each food item?
It does not seem to be a good architectural choice to use individual elements instead of using an array. You should really try to switch to put your slider values into an array.
However, you can use some controller function to achieve what you want, although it is rather ugly. Within your ng-repeat you can bind to a function
<div ng-bind="test(food.uid)"></div>
with the function being
$scope.test = function test(v) {
var t = $scope.$eval("demoVals.slider" + v);
return t;
};
You want to add a food.uid property to demoVals for each iteration. This brings the need to execute js code on each iteration. That can be only be done (IMHO)in a directive.
So here is a working plunker
Here is how you should modify your html ( notice the repeat-directive ... directive :)
<div class="formitem" ng-repeat="food in foodlist" repeat-directive>
<div ui-slider="{range: 'min'}" min="0" max="{{ unit.max }}" ng-model="demoVals.slider" class="food-slider" >
<div class="begin">1</div>
<div class="end"> {{ unit.max }} {{ food.uid }} </div>
</div>
<span ng-bind="demoVals.slider"></span>
here is the code for the direective that will be executed on every iteration:
var app = angular.module('plunker', [])
.directive ('repeatDirective',function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
console.log('linking directive ',scope.demoVals)
if (scope.food){
// you have a value for food
// you can add it as an attribute to demoVals
scope.demoVals['prop'+scope.food.uid] = scope.food.name;
}}
}
});

Categories

Resources