Lets say using the following data I would like to create a treeview-like representation.
var demoData = [
{"name": "person1", "parent": ""},
{"name": "person2", "parent": "person1"},
{"name": "person3", "parent": "person1"},
{"name": "person4", "parent": "person2"},
{"name": "person5", "parent": "person3"}
];
The output should be something like this.
person1
|--person2
| |--person4
|--person3
|--person5
How would I do this with AngularJS. I know how to do this lets say in C# with a recursive function but cannot get my head around how to do this in AngularJS.
I have looked at other questions on SO but the data there was formatted in a different way so that the child entities were directly in the parent entity.
Generally (like in C#) you can achieve recursive call by using ng-include.
One node calls ng-include="'tree_item_renderer' for each child.
Something like:
<script type="text/ng-template" id="tree_item_renderer">
<span>
{{showNode(data)}}
</span>
<ul class="some" ng-show="data.show">
<li ng-repeat="data in data.nodes"
ng-include="'tree_item_renderer'" tree-node></li>
</ul>
</script>
And root call:
<ul>
<li ng-repeat="data in displayTree"
ng-include="'tree_item_renderer'"></li>
</ul>
The basic demo you can find here in Plunker
Related
I'm currently having an issue accessing nested objects that are referred to through numbers. I made a service call to retrieve a JSON object, and then mapped each field to another object. I'll be using this object to display each field in the HTML.
My problem is occurring when I reach the nested objects. Here's an example :
{
"name": {
"title": "mr",
"first": "something",
"last": "something"
},
"role": "something",
"projects": {
"0": {
"title": "something",
"account": "something",
"steps": {
"total": 30,
"completed": 28
},
"dueDate": "2021-07-19 09:00:00",
"status": "fine"
},
"1": {
"title": "something",
"account": "something",
"steps": {
"total": 10,
"completed": 5
},
"dueDate": "2021-07-20 09:00:00",
"status": "fine"
},
}
}
The projects field gets tricky when trying to display all projects in the HTML. At the moment, I've created a person variable initialized to an empty array, and I add all of the fields from t he subscription to it. To solve this problem, I figured I should create a separate variable such as projects: any = []; and then set it to a new field in the person variable. Then iterate through it using an *ngFor, and display every project. Something like this
<li *ngFor="let project of person.projects">
{{ project }}
</li>
However that approach still doesn't reach the nested fields. How do I access the numbered objects here, and generally iterate through all of the nested fields?
Any advice will be very helpful. Thank you in advance.
I think you are looking for the KeyValuePipe
You would use it like this:
<li *ngFor="let item of person.projects | keyvalue">
{{ item.value }}
</li>
But based on your comment on doing this for more nested values, it might be worth flattening the data a bit in the component to simplify the template logic.
It should be noted that I'm very new to Angular.
I have the following JSON I get from Django Rest Framework:
api/movie/1
{
"id": 1,
"name": "Mr & Mrs Smith",
"actors": [
1,
2,
]
}
api/actor/1
{
"id": 1,
"name": "Angelina Jolie",
}
api/actor/2
{
"id": 2,
"name": "Brad Pitt",
}
I made a movie detail page following the Angular tutorial, using $resource.
This is the movie-detail.component.js
angular.
module('movieDetail').
component('movieDetail', {
templateUrl: 'static/partials/movie-detail/movie-detail.template.html',
controller: [ '$routeParams', 'Movie',
function MovieDetailController($routeParams, Movie) {
var self = this;
self.movie= Movie.get({movieId: $routeParams.movieId})
}
]
});
In the movie HTML template, I have access to the "actors" id via "$ctrl.movie.actors". But I can't figure out if there is a way to use this id to ask the server for the 'actor' object. Something like :
Actor.get({id}) to incorporate them in the movie details template.
TL;DR
What I can do now :
Actors:
<ul>
<ling-repeat="actor in $ctrl.movie.actors"> {{actor}} </li>
</ul>
result:
Actors
1
2
And I want something along the lines of :
Actors:
<ul>
<ling-repeat="actor in $ctrl.movie.actors"> {{actor.name}} </li>
</ul>
result:
Actors
Angelina Jolie
Brad Pitt
How can this be achieved ?
Thank you for any replies!
First of All you are using Angularjs 2 and Angularjs 2 is way different then Angularjs 1.*.
In this case its more javascript object manipulation rather Angularjs tricks
you can try below ways to achieve this
option1:
if you are able to edit your rest call in Django, change your code to return the movie and actor details in one js like this
{
"id": 1,
"name": "Mr & Mrs Smith",
"actors": [
1: {"id": 1,"name": "Angelina Jolie"},
2: {"id": 1,"name": "Angelina Jolie"},
]
}
And try to ng-repeat for you actor object under movie. or you can separate both the calls as your requirement.
option2:
You need to do a lazy loading at the time of loading. once your movie object is loaded, you need to call actor rest url to get all the actor object required and append them in the existing object if you are unable to make a single rest call.then in the template you can show actor detail as above.
This follows from my question Recursion with ng-repeat in Angular, but is no way a duplicate. Now I know how to do that, but am asking why my current solution to the problem isn't working.
I'm busy adapting the huge Metronic Angular theme, and my first task is to dynamically load side menu items from a REST api. The container for the side meny, in the main index.html page (only page used: doing spa) looks like this:
<div data-ng-include="'tpl/sidebar.html'" data-ng-controller="SidebarController" class="page-sidebar-wrapper"></div>
Then sidebar.html looks like this:
<div class="page-sidebar navbar-collapse collapse">
<ul class="page-sidebar-menu" data-keep-expanded="false" data-auto-scroll="true" data-slide-speed="200"
ng-class="{'page-sidebar-menu-closed': settings.layout.pageSidebarClosed}"
rsng-menuItem="menuItems">
</ul>
</div>
You'll notice my custom directive, rsng-menuItem, used for recursing a menu tree:
angular.module("ReflexPortalApp").directive("rsngMenuItem", ["$parse", function ($parse) {
return {
restrict: "A",
scope: true,
templateUrl: "/tpl/sidebar/menu-item.html",
link: function (scope, element, attrs) {
scope.List = $parse(attrs.rsngMenuItem)(scope);
}
}
}]);
and it's template looks like this:
<li ng-repeat="item in List" ng-class="{\'nav-item\': !item.IsHeading, \'start\': item.IsStart}">
<a href="{{item.Href}}" ng-class="{\'nav-link nav-toggle\': item.subItems && item.subItems.length > 0}">
<i ng-if="{{item.Icon && item.Icon.length > 0}}" class="icon-{{item.Icon}}"></i>
<span class="title">{{item.Text}}</span>
<span ng-if="{{item.DecorClass || item.DecorText}}" class="{{item.DecorClass}}">{{item.DecorText}}</span>
</a>
<ul rsng-menuItem="item.subItems" class="sub-menu"></ul>
</li>
In SideBarController1, the menu items do load successfully from the REST API, and are assigned to themenuItems` scope property, so they should be available for binding.
Lastly, a menu item looks like this in JSON:
{
"IsDisabled": "0",
"seq": 2,
"Href": "javascript:;",
"Id": "2",
"IsStart": "0",
"IsNavItem": "1",
"DecorText": null,
"ApiCall": null,
"Index": 3,
"Text": "Accounting",
"Icon": "settings",
"ParentId": null,
"DecorClass": "arrow",
"subItems": [
{
"DecorClass": "arrow",
"ParentId": "2",
"Icon": "wrench",
"Text": "Statement",
"Index": 1,
"ApiCall": "statement&CustID=4446&statementdate=2016-05-01",
"DecorText": null,
"IsNavItem": "0",
"IsStart": "0",
"Id": "3",
"Href": "list",
"seq": 1,
"IsDisabled": "0"
}
]
}
When I started, and didn't have a clue how to do recursion in templates, I simply used a recursive code loop and jQuery to 'manually' construct and populate all the elements for each menu item, and that worked fine, but writing HTML with code literals stinks.
Now I simply get an empty side menu, with no errors in the console (gotta love that part of Angular), and was wondering if I'm doing anything obviously wrong.
The problem lies in your directive-name, angular simply doesn't recognize your directive because it doesn't deal well with uppercase letters in attribute-names. Rename rsng-menuItem to rsng-menu-item.
However, this won't work because you can't just recursively nest directives in angular, it will end in an infinite loop.
There are multiple solutions for this, take a look at Recursion in Angular directives and
https://github.com/marklagendijk/angular-recursion
Very simple user to user messaging piece that I'm struggling with the interface in an app to use ng-repeat on the items.
Here is the data:
{
"ID": 4118,
"CreatedDate": "2015-08-20T03:12:50.397",
"recipient": [
{
"ID": 13,
"FirstName": "Heather",
"LastName": "Martin",
"ProfileImage": "https://../profilepictures/13"
}
],
"sender": [
{
"ID": 1046,
"FirstName": "Brad",
"LastName": "Martin",
"ProfileImage": "https://../profilepictures/1046"
}
],
"messages": [
{
"ID": 4137,
"ConversationID": 4118,
"UserID": 1046,
"Body": "hey",
"CreatedDate": "2015-08-20T14:34:42.4716233+00:00"
}
]
}
In the controller I get the conversations out of LS, one conversation is one record in LocalStorage, the JSON above will represent one conversation.
$scope.convo = JSON.parse(localStorage.getItem("convo-" + $stateParams.chatId));
Here is the structure I am trying to achieve (again, very simple, nothing fancy).
<ul>
<li class="item-avatar-left" ng-repeat="c in convo track by $index">
<img ng-src="{{c.recipient[0].ProfileImage}}" />
<p class="bubble speech">
{{c.messages[0].Body}}
</p>
</li>
</ul>
I've tried multiple variations on the ng-repeat directive.
Essentially what I'd like to achieve is just showing one <li> per each message.
Current result:
Console output of a conversation from LS:
You can try normally by ng-repeat
In controller like:
$scope.convo = [
{
"ID": 4118,
"CreatedDate": '2015-08-20T03:12:50.397',
//...... as your info
}
];
In HTML:
<ul>
<li class="item-avatar-left" ng-repeat="c in convo">
<img ng-src="{{c.recipient[0].ProfileImage}}" />
<p class="bubble speech" ng-repeat="m in c.messages">
{{m.Body}}
</p>
</li>
</ul>
NB: your $scope.convo need to be an array
I have a little problem.. I've got this JSON data:
[
{
"students": {
"student_id": "2",
"student_school": "1",
"student_name": "Charles"
},
"parents": [
{
"parent_id": "2",
"parent_school": "1",
"parent_name": "Tim"
}
]
},
{
"students": {
"student_id": "3",
"student_school": "1",
"student_name": "Johnny"
},
"parents": [
{
"parent_id": "3",
"parent_school": "1",
"parent_name": "Kate"
}
]
}
]
The problem is that I try to call to my html page by angular:
{{student.student.student_name}}
Yeah it works but when I want to call the parents data it doesn´t...
{{student.parents.parent_name}}
Simply:
<div ng-repeat="student in data">
{{student.students.student_name}}
{{student.parents[0].parent_name}}
</div>
Or define function in scope called for example getParentDescription and than
<div ng-repeat="student in data">
{{student.students.student_name}}
{{getParentDescription(student)}}
</div>
Because parents is an array. You must specify the index (0 in your case). See the response here : How to get value from a nested JSON array in AngularJS template?
You can't access child scopes directly from parents. See the comment by Vittorio suggesting<ng-repeat="child in parent.children"/> also Binding to Primitives
I'm guessing student is from an ng-repeat where you go through each object in the array.
Take a closer look at your JSON. While "students": {} points to an object, "parents": [] points to an array. Fix your JSON and it'll be fine