Angular recursive ng-include while keeping track of recursion depth - javascript

I have a situation that is pretty similar to this answer to the question here:
AngularJS ng-include with nested hierarchy
I have some data in the format
$scope.data = {
text: "blah",
comments:
[
{
text: ["blahL11", "blahL12", "blahL13"],
comments: [
{
text: ["blahL111", "blahL112", "blahL113"]
},
{
text: ["blahR111", "blahR112", "blahR113"]
}
]
},
{
text: ["blahR11", "blahR12", "blahR13"]
}
]
};
And I am display it with a recursive ng-include like this:
<ul>
<li>{{data.text}}</li>
<li ng-repeat="item in data.comments" ng-include="'tree'"></li>
</ul>
<script type="text/ng-template" id="tree">
<ul>
<li ng-repeat="text in item.text">{{text}}</li>
<li ng-repeat="item in item.comments" ng-include="'tree'"></li>
</ul>
</script>
http://plnkr.co/edit/8swLos2V6QRz6ct6GDGb?p=info
However, I would like to somehow keep track of the depth of the recursion as well. So that instead of simply displaying:
-blah
-blahL11
-blahL12
-blahL13
-blahL111
It could display
-1. blah
-2. blahL11
-2. blahL12
-2. blahL13
-3. blahL111
Without me modifying the data structure (as the depth is only really for display?). Can I insert a variable into the ng-include, is there some sort of recursive $index I can use?

You can do this using ng-init. This will assign a value to the new scope when it's created, which you can do by refering to a value in the old scope and incrementing it.
plnkr demo
<li ng-repeat="item in item.comments" ng-include="'tree'" ng-init="depth = depth + 1"></li>

Related

ng-repeat on object angularjs

I have an object like this
Obj = {
"elements":[
{"name":"something","id":"v1-234"},
{"name":"somethingElse","id":"v1-239"}
],
"paging":{
"next" : "100",
"total": "2000"
},
"linked"={
"partners":[
{"id":"82","name":"A"},
{"id":"83","name":"B"}
],
"instructors":
[
{"id":"11232","name":"alex"},
{"id":"11432","name":"boob"}
]
}
}
I have equal items inside the elements array and the partner and instructors array. I have only placed 2 items just for display there could be many.
The Obj.elements[0] is related to Obj.linked.partners[0] and Obj.linked.instructors[0]. Similarly for the first,second,third ..... items as well.
How can I ng-repeat on this object such that I can show
Obj.elements[i] Obj.linked.partners[i] Obj.linked.instructors[i]
at a time in html template ?
If your elements, partners, and instructors arrays will always be the same length and you wanted to for example join together the names you could try something like this.
<div ng-repeat="element in Obj.elements">
{{element.name}} {{Obj.linked.partners[$index].name}} {{Obj.linked.instructors[$index].name}}
</div>
$index is used by Angular in a ng-repeat to keep track of its position when iterating through an object. Here is the documentation: ng-repeat
ng-repeat has the $index scoped variable:
<ANY ng-repeat="element in model.elements">
<span ng-bind="element.name"></span>
<span ng-bind="model.linked.partners[$index].name"></span>
<span ng-bind="model.linked.instructors[$index].name"></span>
</ANY>

UI-Sortable with Angular filter for ng-repeat

It seems that the UI-sortable (https://github.com/angular-ui/ui-sortable) plug in does not play well if there is a filter in the list that is being ng-repeated by angular. The drag and drops do not work as intended.
This is shown by these two fiddles:
First: Has filter - the drag and drop will not stay in place.
http://jsfiddle.net/Lvc0u55v/2886/
HTML:
<div ng-controller="Controller">
<ul ui-sortable="sortableOptions" id="test" ng-model="items">
<li class="line" ng-repeat="item in items | filter: myFilter">
{{item.name}}
</li>
</ul>
</div>
JS:
var myApp = angular.module('myApp',['ui.sortable']);
myApp.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Superhero';
$scope.items = [
{name: 'TEST'},
{name: 'TEST2'},
{name: 'TEST3'},
{name: 'TEST4'},
{name: 'TEST5'},
{name: 'TEST6'},
{name: 'TEST7'},
{name: 'TEST8'}
]
$scope.myFilter = function (item) {
return item.name !== 'TEST';
};
$scope.sortableOptions = {
opacity: '0.8',
axis: 'y',
tolerance: 'pointer',
}
}]);
Second: Has no filter - works as intended.
JS SAME,
HTML:
<div ng-controller="Controller">
<ul ui-sortable="sortableOptions" id="test" ng-model="items">
<li class="line" ng-repeat="item in items">
{{item.name}}
</li>
</ul>
</div>
http://jsfiddle.net/Lvc0u55v/2887/
I will create an issue on their github repo as well, but if anyone has a work around that would be great!
In the docs you can see
Filters that manipulate the model (like filter, orderBy, limitTo,...) should be applied in the controller instead of the ng-repeat (refer to the provided examples).
This is the preferred way since it:
- is performance wise better
- reduces the chance of code duplication
- is suggested by the angularJS team
- it does not break the matching of the generated DOM elements and the ng-model's items
So yeah, don't do filtering in the view, it is not intended to work with ui-sortable.
For example:
JS:
$scope.filtered= $filter('filter')($scope.items, $scope.myFilter);
HTML:
<li class="line" ng-repeat="item in filtered">
{{item.name}}
</li>
you are passing an array in ng-model.
ngModel directory works like just another attribute like id which is accessible by angular.
and ngModel is mostly used in the tags which produces data, like input, textarea, select and other custom directives.
simply instead of accessing the data from id by using the general DOM identification like
$scope.variable = document.getElementById('someElement').value
angular uses ngModel directive to bind the data in both ways.

Javascript - How to access the array object in Angular js expression? (ng-repeat)

Javascript
<script>
var car=[
{
name:'honda',
color:'red',
comments:[
{
rating:3,
bidprice:"25,000"
},
{
rating:4,
bidprice="21,000"
]
}
];
</script>
What am i trying to do is getting the bid price of car from each comments by using ng-repeat.
I did try like this
<li ng-repeat="car in carList">
{{car.comments.bidprice}}
</li>
Unfortunately, I getting nothing respond from this code.
What should I do?
Thanks you.
<li ng-repeat="comment in car.comments">
{{comment.bidprice}}
</li>
Should work. I am not sure where you took carList from.
comments is an array. or you do like this car.comments[0].bidprice or you do another ng-repat="comment in car.comments".

Using splice on dynamic array in Angular repeater

I have an issue when trying to delete an object from an array using splice. I have an array that is dynamically created through a UI and stored in a scope variable called $scope.productAttributes.Products. This is an example of what it looks like...
[
{
"ProductLabel":"Net",
"Code":"ela",
"Site":"SITE1"
},
{
"ProductLabel":"Link",
"Code":"eli",
"Site":"SITE1"
},
{
"ProductLabel":"24-port managed PoE switch",
"Code":"24p",
"Site":"SITE2"
},
{
"ProductLabel":"Dedicated Firewall",
"Code":"ded",
"Site":"SITE2"
},
{
"ProductLabel":"Link",
"Code":"eli",
"Site":"SITE3"
},
{
"ProductLabel":"IPv4 Addresses",
"Code":"ip4",
"Site":"SITE3"
}
]
I then display that array in an angular repeater and group it by 'site' (which might be part of the problem)...
<div ng-repeat="(key, value) in productAttributes.Products | groupBy: 'Site'">
<strong>{{key}}</strong>
<div ng-repeat="site in value">
<h4>{{site.ProductLabel}}</h4>
<sapn href="" ng-click="deleteItem($index)" class="text-danger">Remove {{site.ProductLabel}}</span>
</div>
</div>
</div>
On the delete button I pass in the index of the object and use the splice function...
$scope.deleteItem = function (index) {
$scope.productAttributes.Products.splice(index, 1);
};
So the issue is that the $index is always zero (I noticed this from a console.log) as I mentioned that I think it might be down to the groupBy but I am not sure. anyone know whats going wrong? Thanks
UPDATE:
It would seem the problem is with the $index in the nested repeater. So if the json above the structure would be...
SITE1:
Product: Net - $index: 0
Product: Link - $index: 1
SITE2:
Product: 24-port - $index: 0
Product: Dedicated - $index: 1
SITE3:
Product: Link - $index: 0
Product: IPV4 - $index: 1
So if I try to delete the IPV4 product in SITE3, it removes the LINK product in Site1 as it has the same $index. any ideas how I can fix that?
We can not rely on $index as it does not contain the updated value after you remove an item from array.
Pass the object dynamically from UI and delete it from model using below code:
In Html:
<div ng-repeat="(key, value) in productAttributes.Products | groupBy: 'Site'">
<strong>{{key}}</strong>
<div ng-repeat="site in value">
<h4>{{site.ProductLabel}}</h4>
<sapn href="" ng-click="deleteItem(site)" class="text-danger">Remove {{site.ProductLabel}}</span>
</div>
</div>
</div>
In JavaScript:
$scope.productAttributes.Products.splice
($scope.productAttributes.Products.indexOf(site), 1);
This causes model to update with updates values in repeater and re-renders it on UI.
OK - I ended up doing it this way and it seems to work
$scope.deleteItem = function (item) {
var index = $scope.productAttributes.Products.indexOf(item);
$scope.productAttributes.Products.splice(index, 1);
};
So passing in the whole object seems to have worked. I'm not sure why.

Mustache (or Handlebars) iterating over two lists

I have two arrays:
var content = {
"girls": ["Maria", "Angela", "Bianca"],
"digits": ["21.143.191.2", "123.456.78.90", "971.6.17.18.1"]
};
and a template:
<script id="template" type="text/template">
<ul>
<li>{{girls}}</li>
</ul>
</script>
I'd like the end result to be:
<ul>
<li>Maria</li>
<li>Angela</li>
<li>Bianca</li>
</ul>
I've tried block mustaches like {{#girls}} {{.}} {{/girls}} and {{#digits}} {{.}} {{/digits}} but no matter which way I nest them I seem to get repeats instead of interlacing.
Any Ideas?
PS: Obviously in the future we'll be asking for IP addresses, not phone numbers.
PPS: None of those are intended to be real IPs, please don't try to contact those girls!
You need to rearrange your content so that you can iterate over just one thing. If you combine those two arrays with something like this:
var data = { girls: [ ] };
for(var i = 0; i < content.girls.length; ++i)
data.girls.push({
name: content.girls[i],
number: content.digits[i]
});
Then a template like this:
<script id="template" type="text/template">
<ul>
{{#girls}}
<li>{{name}}</li>
{{/girls}}
</ul>
</script>
should work.
With "mu is too short"'s advice. And a few crazy ideas I came up with an interesting approach to complex templating. *It almost works!
So let's say I have this content (or data or view) which I want to put into a template:
var content = {
title: "Black Book",
girls: ["blonde", "brunette", "redhead"],
digits: ['123', '456', '789'],
calc: function () {
return 2 + 4;
}
};
And I have a template like this:
<script type="text/template" id="template">
<h1>{{title}}</h1>
<h3>{{calc}}</h3>
<ul>
<li>{{hair}}</li>
</ul>
</script>
And the end result I want is this:
<h1>Black Book</h1>
<h3>6</h3>
<ul>
<li>blonde</li>
<li>brunette</li>
<li>redhead</li>
</ul>
The problem we'll encounter is that we have arrays (or lists) nested in our original content, and if we tried to Mustache.render(html, content) we'd end up with only one li item or a whole array within one href="" attribute. So sad...
So he's the approach, pass through the template multiple times. The first time, pass through and replace the top level items, and adjust the template for the next pass through. The second time, adjust one of the lists, and adjust the template for the third pass through, etc for how ever many layers of content you have. Here's the new starting template:
<script type="text/template" id="template">
<h1>{{title}}</h1>
<h3>{{calc}}</h3>
<ul>
{{#hair}}
{{#digits}}
<li>{{hair}}</li>
{{/digits}}
{{/hair}}
</ul>
</script>
On the first pass through fill in the top level stuff, and change {{digits}} to {{.}}
$.each(content, function (index, value) {
template2 = template.replace(/{{title}}/ig, content.title)
.replace(/{{calc}}/ig, content.calc)
.replace(/{{digits}}/ig, '{{.}}');
});
Now you have this:
<h1>Black Book</h1>
<h3>6</h3>
<ul>
{{#hair}}
{{#digits}}
<li>{{hair}}</li>
{{/digits}}
{{/hair}}
</ul>
On the next pass through we'll just call Mustache.render(template2, content.digits); and that should give us:
<h1>Black Book</h1>
<h3>6</h3>
<ul>
{{#hair}}
<li>{{hair}}</li>
<li>{{hair}}</li>
<li>{{hair}}</li>
{{/hair}}
</ul>
And that's where my logic dies, haha. Any help thinking this through would rock! I'm thinking I could take out the {{hair}} block elements and just do a $.each pass through content.girls and stack .replace three times. Or I could try to start with lowest level of content, and work my way up. I dunno.
All this basically leaves me wondering if there's some "logic-less" way for this kind of nesting or multiple pass throughs to be put into mustache does handlebars do it?

Categories

Resources