Efficient way to bind Json to Html? - angularjs

I am trying to find out a better way to bind my json data to my html .
Json:
$scope.json = { 'one': { 'name': 'level1', 'two': { 'name': 'level2', 'three': { 'name': 'level3' } } } };
Html:
<div ng-controller="myController" >
<div ng-repeat="data in json"> -- (1)
<b>{{data.name}}</b>
<div ng-repeat="data1 in data">
<b>{{data1.name}}</b>
<div ng-repeat="data2 in data1 track by $index"><b>{{data2.name}}</b></div> -- (2)
</div>
</div>
</div>
Pointers : //marked in view
There is no array in my json data , so , is there a better way rather using ng-repeat (sort of traditional for) . Anything like with binding in knockout or templates (i'm not sure How) .
I see there are no duplicates in my json but still to compromise the error i have been using track by $index at the final inner div(if exclude final div i see no error) .

1: There is no array in my json data , so , is there a better way
rather using ng-repeat (sort of traditional for) . Anything like with
binding in knockout or templates (i'm not sure How) .
There is no way to use JSON object for ng-repeat but you can directly use JSON object using . operator for property.
Check below code
<div ng-controller="myController" >
<div>
<b>{{json.one.name}}</b>
<b>{{json.one.two.name}}</b>
<b>{{json.one.two.three.name}}</b>
</div>
</div>
2 : I see there are no duplicates in my json but still to compromise
the error i have been using track by $index at the final inner div(if
exclude final div i see no error).
The reason for error is because you are trying to get index of JSON object which doesn't has index.

Related

How can I change a value inside of ng-repeat after the repeat complete?

I have a JSON which provides me a user's working experiences info. But country and city's are provided in a code format (TR,DE etc.)
I am using ng-repeat to pass them into html like this
<div ng-repeat="e in experiences">
<span>{{e.Name}}</span>
<span ng-init="changeCodeToFullName(e.Country)">{{vm.CountryFullName[$index]}}</span>
</div>
I am using ng-init to convert Country Code to full name. changeCodeToFullName is an angular service written by me, Is this a correct method? If it is, I can't access the dom to change CountryFullName value. I tried to access them in JS file like vm.CountryFullName[0]="TEST" but it didn't worked. I need to use e.Country variable after, therefore I can't change the original .e.Country value.
How can I access a variable inside of ng-repeat after ng-repeat completed?
How about using a custom filter:
<div ng-repeat="e in experiences">
<span>{{e.Name}}</span>
<span>{{e.Country | changeCodeToFullName}}</span>
</div>
angular.module('App').filter('changeCodeToFullName', function(YourService) {
return function(country) {
return YourService.getFullCountryName(country)
}
})
Here's an example: http://codepen.io/rustydev/pen/YWyqJB
This is one way of doing it - but this ngInit value won't be reparsed if the list updates. Why not just format the data in the JSON request response - such as:
$http.get("json.json").success(function(data) {
$scope.exeriences = data.map(function(obj) {
//Format results;
if (obj.Country == "DE") {
obj.Country = "Germany"; //etc
}
return obj;
});
});

Creating dynamic html elements in angularjs

I am trying to create some dynamic html elements in angular based on values I have stored in a SQL database. This is a prize selection application for work where I have 5 prizes which have descriptions associated and each description has a type (span, p, div, h1, etc). So based on what our DB says the line should be i want the html to create itself. The way the data is laid out is I have a data object that has an array of pictures and each picture object has an array of description objects { Pictures[ Descriptions[] ] }
"Pictures":[{"ID":9,"IDName":"messengerBag","Descriptions":[{"ID":7,"Text":"Messenger bag is a perfect size for most 15” laptops. The 10th anniversary logo is beautifully displayed in full detail on the front pocket.","DescriptionType":"h3"},{"ID":8,"Text":"Zippered main compartment includes a padded laptop sleeve.","DescriptionType":"p"},{"ID":9,"Text":"Velcro front pocket with organization panel.","DescriptionType":"p"}, {"ID":10,"Text":"Pen loop and side mesh pocket.","DescriptionType":"p"},{"ID":11,"Text":"Adjustable shoulder strap and two carry handles.","DescriptionType":"ul"},...
I have tried using the values directly and it did not work:
<div ng-repeat="pic2 in vm.data.Pictures" ng-show="{{$index}} == vm.index">
<{{desc.DescriptionType}} ng-repeat="desc in pic2.Descriptions">{{desc.Text}}</{{desc.DescriptionType}}>
</div>
I then decided to try a directive. I could get it to return the text but never the element with the description type i was hoping for.
.directive("dynamicelement", ['$compile', function ($compile) {
return {
restrict: "E",
scope: { desc: '#' },
template: '<' + '{{header}}' + '>' + '{{header2}}' + '</' + '{{header}}' + '>',
controller: function ($scope) {
$scope.header = "p";
$scope.header2 = "hi";
}
}
};
I have read article where they talked about using $compile and needing a link function in my directive but i'm not really sure how to use those.
I also have an example of using ngSwitch from How can I use Angular to output dynamic form fields? but this didn't seem to lend itself to my dual ng-repeat organization I am currently using.
Is what I am trying to accomplish possible and if so anyone have pointers on what I should be looking at? Thanks for your time.
I was able to use ng-if's to solve this. It's not as clean as I had hoped but it is doing the trick.
<div data-ng-repeat="pic2 in vm.data.Pictures" class="picture{{$index}} pictures">
<div data-ng-repeat="desc in pic2.Descriptions">
<p data-ng-if="desc.DescriptionType=='p'">{{desc.Text}}</p>
<span data-ng-if="desc.DescriptionType=='span'">{{desc.Text}}</span>
<div data-ng-if="desc.DescriptionType=='div'">{{desc.Text}}</div>
<label data-ng-if="desc.DescriptionType=='label'">{{desc.Text}}</label>
<h1 data-ng-if="desc.DescriptionType=='h1'">{{desc.Text}}</h1>
<h2 data-ng-if="desc.DescriptionType=='h2'">{{desc.Text}}</h2>
<h3 data-ng-if="desc.DescriptionType=='h3'">{{desc.Text}}</h3>
<h4 data-ng-if="desc.DescriptionType=='h4'">{{desc.Text}}</h4>
<h5 data-ng-if="desc.DescriptionType=='h5'">{{desc.Text}}</h5>
<ul data-ng-if="desc.DescriptionType=='ul'"><li>{{desc.Text}}</li></ul>
<ol data-ng-if="desc.DescriptionType=='ol'"><li>{{desc.Text}}</li></ol>
</div>
</div>
The nested ng-repeat doesn't have any kind of HTML element or directive on it, so there's nothing to repeat.
Something like this should work:
<div ng-repeat="pic2 in vm.data.Pictures" ng-show="{{$index}} == vm.index">
<div ng-repeat="desc in pic2.Descriptions">
{{desc.DescriptionType}}
{{desc.Text}}
</div>
</div>

Object array value in ngRepeat not getting updated on UI

I have some data in the form of array, which I am converting into an object using _.groupBy(data, ""filter"). After grouping, it looks like this
$scope.data = {"Key1" : [{}, {}], "Key2" : [{}] }
I am looping this object using ngRepeat, and have a directive inside the ngRepeat like below.
<div ng-repeat = "(key, value) in data">
<my-directive id={{$index}} group = "data[key]"></my-directive>
</div>
The problem I am facing is, on refreshing and calling the service again, the updated array of keys is not displayed on the UI. However, if any new key is added in the data object, only then UI is updating.
Any suggestions please.

calling a function from ng-repeat with the object from the current scope

I'm trying to call a function (from a non event element) from a ng-repeat to feed an array of data to an autocomplete element (using https://github.com/JustGoscha/allmighty-autocomplete).
It's to generate a kind of logic system :
type(listbox) | comparator (eg:>=) (listbox) | value(autocomplete)
And several of those object can be listed on a webpage to get some complex logic
type=value && type2>value3 || ...
Depending on type and comparator, values are different.
The code so far (simplified):
<div class="comparator" ng-repeat="comp in container.comparators">
<select ng-model="comp.type"><option ng-repeat="i in type_options" value="{{i.value}}" ng-selected="{{i.value==comp.type}}">{{i.label}}</option></select>
<select ng-model="comp.comparator"><option ng-repeat="i in comp_options|filter:typeMatch(comp)" value="{{i.value}}" ng-selected="{{i.value==comp.comparator}}">{{i.label}}</option></select>
<autocomplete class="autocomplete" data="" attr-placeholder="Entrez votre valeur" click-activation="true" on-type="**updateValue**" ng-model="comp.value"></autocomplete>
</div>
updateValue is the function to call, but i need to know the current object (comp from the ng-repeat) on which i am to send the right array of value.
I tryed to send an existing array to avoid "digest loop"
$scope.updateValue = function(crit){
for(var i=0;i
I also tryed to do a function that return a function that return the array :DDDDD :
$scope.updateValue = function(crit){
return function(value/*not used*/){
for(var i=0;i<$scope.comp_options.length;i++) {
if($scope.comp_options[i].value===crit.comparator){
$scope.value_elements=$scope.comp_options[i].info;
break;
}
}
return $scope.value_elements;
};
};
Replacing the autocomplete object with :
if I console.log(comp), I see that I can get my object, but I get a digest loop ...
Is there a way to know the object of the "line" I was called from ?
Thx (i'm a total newbie in angular, but so far, i've been unable to find how to retrieve that information ... is that even possible :) ?).
Access it using $index ? Example below. You can then use the index to access it
<tr ng-repeat="user in uc.users track by $index">
<td>{{user.id}}</td>
<td>{{user.first_name}}</td>
<td>{{user.last_name}}</td>
<td>{{user.email}}</td>
<td>{{user.department}}</td>
<button ng-click="uc.open(user.id, $index);">Open</button>
</tr>

Generate new $index for array items per specific key:value in Angular

Please be patient if I don't understand something and need clarification as I'm still new to Angular, and please simplify explanations for an Angular newbie if you can, thanks!
I am working on a table display of data drawn from a database and using ng-repeat to generate it as something like "item in results.items". There are three "types" of item, let's just call them "title", "action" and "comment". Since results.items is the array, the $index goes by item placement in the whole array regardless of type.
What I want to do is be able to number only ONE type of item, item.type=='action'. Obviously if I just put
<td ng-show="item.type=='action'">{{$index}}</td>
it is going to go by the original array index, so instead of having my data with each 'action' numbered 1, 2, 3, etc. in sequence, I end up with them numbered as they fall in the array, something like: 5, 8, 9, 12, 15, 16, 22.
Can anyone clue me in on a straightforward way to set up the generation of a new $index (such as '$actionIndex') in my controller that will re-index only the items that are of type=='action'? Ideally I'd like to be able to designate a $scope function for this in my controller and then just call it with ng-model or something.
I know this inquiry is lacking in the code sampling department -- it's a little awkward in this case for me because I'm still pretty new to Angular and I can't just copy the actual code due to company NDA rules. Thanks for bearing with me and any assistance you can lend.
EDIT/UPDATE:
After reviewing the solution offered by pixelbits, I am wondering whether what I should do here instead is create a new associated id # for each item where "type==action" and just increment it with each successive item where "type==action"?
I don't want to filter items with "type!=action" out of the display entirely. Right now it is using an inclusive ng-repeat and sorting so that other types of "item", where applicable, appear in proper order under this type (item==action) to explain/enhance it. This means I have <tr> set up with the ng-repeat="item in items" and then <span> tags within the td cells per row to ng-show depending on type. I'm not seeing how to logically use a different ng-repeat based on type. I just want to reindex all type==action items and display that new index numeral instead of that of the larger default array "items" itself.
Still working on it ...
Here is a code snippet from the template to illustrate:
<tbody>
<tr ng-repeat="item in items" ng-class="{title: item.type=='title', action: item.type=='action', comment: item.type=='comment'}">
<td class="grid-2">
<span ng-show="item.type=='action'">{{$index}}</span>
<span ng-show="item.type!='action'"> </span>
</td>
<td class="action-text grid-8">
<span class="title regular-item" ng-show="item.type=='title'">
<h3>{{item.field}}</h3>
</span>
<span class="action regular-item" ng-show="item.type=='action'">{{item.field}}</span>
<span class="comment" ng-show="item.type=='comment'"><i>Comment by: {{item.author}}<br />{{item.field}}</i></span>
</td>
<td class="grid-2">
<span ng-show="item.type=='action'" class="timestamp" data-toggle="tooltip" data-original-title="{{item._created | date: 'hh:mm:ss a'}}">{{item._created | date: 'hh:mm:ss a'}}</span>
<span ng-show="item.type!='action'"> </span>
</td>
</tr>
</tbody>
So as you can see I have the general ng-repeat and then the template lays it out according to type. "Action" is a pretty "important" type as it has special displays the others do not.
You could implement a $watch handler:
JS
app.controller('ctrl', function($scope, $filter) {
$scope.result = ...;
$scope.actionList = [];
$scope.$watchCollection('result.items', function(newArr) {
$scope.actionList = $filter('filter')(newArr, { type: 'action' }, true);
});
});
HTML
<ul>
<li ng-repeat="item in actionList">{{ $index }}</li>
</ul>
After unsuccessfully trying what pixelbits suggested here, in the end I went back to the Service and plugged in a counter there which I could place in my template.
Relevant snippet:
}, function(response) {
var items = response.data;
var i = 1;
_.each(items, function(item) {
if (item.type == 'action') {
item.counter = i++;
}
});
callback(response.data);
});
And in the template:
<span ng-show="item.type=='action'">{{item.counter}}.</span>
One of my constant issues is "over-thinking" a problem (making it more complicated in my mind than it needs to be) and this was no exception! Hopefully this will help someone else in the future as well.

Resources