To access object properties based on the id present in an array - arrays

I have 2 api's called teachers and sessions.
teachers JSON file:
[
{
"teacherName": "Binky Alderwick",
"id": "01"
},
{
"teacherName": "Basilio Gregg",
"id": "02"
},
{
"teacherName": "Binky Alderwick",
"id": "03"
},
{
"teacherName": "Cole Moxom",
"id": "04"
}
]
sessions JSON file:
[
{
"id":"001",
"sessionName": "Chemistry",
"notes": "Chemistry is the study of matter, its properties",
"teacherIds": ["01","03"]<==========
},
{
"id":"002",
"sessionName": "Physics",
"notes": "Physics is the natural science that studies matter and its motion ",
"teacherIds": ["02","04"]
},
{
"id":"003",
"sessionName": "Maths",
"notes": "Mathematics includes the study of such topics as quantity",
"teacherIds": ["01","04"]<=========
},
{
"id":"004",
"sessionName": "Biology",
"notes": "Biology is the natural science that studies life and living organisms",
"teacherIds": ["02","04"]
}
]
Now i am displaying all the teachers in the template like this:
In the sessions JSON, I have mentioned the teacherIDs which is of array, I want to display the sessions of the particular teacher based upon the teachersID.
For ex the sessions (Chemistry & Maths) contains teacherID as (01),So i want to display these 2 sessions(Chemistry & Maths) under Teacher 1(i,e Binky Alderwick) Like this:
I should get all the properties of the session object based on the teacherId.
Stackblitz DEMO

This works in your stackblitz
<h4>Teachers</h4>
<div class="cust-detail" *ngFor="let teacher of teachers">
<tr>
<td>Name</td>
<td>{{ teacher.teacherName }}</td>
</tr>
<tr>
<td>Sessions</td>
<div *ngFor="let session of sessions">
<span *ngFor="let id of session.teacherId">
<span *ngIf="id == teacher.id">
<h2>{{ session.sessionName }}, {{ session.id }}</h2>
<p>{{session.notes}}</p>
</span>
</span>
</div>
</tr>
<hr>
</div>
Dont forget to remove the arrows from your JSON before putting this in.

You need to manipulate your JSON for the same like below -
getManipulatedData() {
this.teachers && this.teachers.forEach(res => {
res['subjects'] = [];
this.sessions.forEach(obj => {
if (obj['teacherId'].includes(res.id)){
res['subjects'].push({subject: obj.sessionName, notes: obj.notes})
}
});
});
}
<h4>Teachers</h4>
<div class="cust-detail" *ngFor="let teacher of teachers">
<tr>
<td>Name</td>
<td>{{teacher.teacherName }}</td>
</tr>
<tr>
<td>Sessions</td>
<td><br>
<div *ngFor='let subject of teacher?.subjects'>
<h2>{{subject?.subject}}</h2>
<p>{{subject?.notes}}</p>
</div>
</td>
</tr>
<hr>
</div>
Working Example
Update -
Also you need to call this method on API call end like this -
ngOnInit() {
this.myService.getContacts()
.subscribe(res => {
this.teachers = res;
this.getManipulatedData();
});
this.myService.getWorkers()
.subscribe(res => {
this.sessions = res;
this.getManipulatedData();
});
}

Related

angularjs 1.6.4 itrate over multi dimensional JSON and loop result in html

I'm stuck at logical end where I'm getting json from Request like this below:
"externalLinks": [
{
"extPageId": 10,
"groupName": "BLAVATNIK ARCHIVE FOUNDATION",
"title": "MISSION & HISTORY",
"url": "http://www.mission-history/",
"sortOrder": 1
},
{
"extPageId": 9,
"groupName": "BLAVATNIK ARCHIVE FOUNDATION",
"title": "LEADERSHIP & STAFF",
"url": "http://www.leadership-staff/",
"sortOrder": 4
},
{
"extPageId": 3,
"groupName": "PROGRAMS",
"title": "2016 PHOTOS",
"url": "http://www.2016-photos/",
"sortOrder": 1
},
{
"extPageId": 2,
"groupName": "PROGRAMS",
"title": "2015 PHOTOS",
"url": "http://www.2015-galleries/",
"sortOrder": 2
},
],
where my html on which i need this result to be displayed is like this:
<div class="overview-box">
<h4>BLAVATNIK ARCHIVE FOUNDATION</h4>
<p><a target="_blank" href="http://www.mission-history/"> MISSION & HISTORY<span class="stripe pdf-stripe"> </span></a>s</p>
<p><a target="_blank" href="http://www.collection-overview/"> COLLECTIONS OVERVIEW<span class="stripe pdf-stripe"> </span></a></p>
<h4>PROGRAMS</h4>
<p><a target="_blank" href="http://www.2016-photos/"> 2016 PHOTOS<span class="stripe pdf-stripe"> </span></a></p>
<p><a target="_blank" href="http://www.2015-galleries/"> 2015 PHOTOS<span class="stripe pdf-stripe"> </span></a></p>
</div>
so as you can see in html, there is group name at top and below it it's content like title, url etc. So how can iterate over this json result to display content in html. I've tried to create array and some other code which i got from google but didn't found it's right logic. I can do this is PHP but here in Angular I'm fairly new:
$http.get(BASE_URL + "aboutdata")
.then(function (response) {
$scope.pageTitle = response.data.pageTitle;
var externalLinks = [];
$scope.groupName = [];
angular.forEach(response.data.externalLinks, function (value, key) {
this.push(key + ': ' + value);
}, externalLinks);
console.log(externalLinks);
}).finally(function () {
// called no matter success or failure
$scope.loading = false;
});
You can easily create a dictionary out of the array, meaning each entry contain an array of the items that belong to each unique group :
var groupBy = function(array, key) {
var r = {};
array.forEach(function(item) {
if (r[item[key]]) {
r[item[key]].push(item)
} else {
r[item[key]] = [item]
}
})
return r
}
$scope.grouped_externalLinks = groupBy(externalLinks, 'groupName');
Now you have an array like object literal on the form
{
'BLAVATNIK ARCHIVE FOUNDATION': [ ... ],
'PROGRAMS': [ ... ]
}
ng-repeat it as usual, you can even take care of the sortOrder within each group :
<div ng-repeat="(groupName, items) in grouped_externalLinks">
<strong> {{ groupName }} </strong>
<div ng-repeat="item in items | orderBy:'sortOrder'">
<p><a target="_blank" href="{{ item.url }}"> {{ item.title }} </a></p>
</div>
</div>
What you are looking for is groupBy filter. If you use that you can group based on a property inside ng-repeat
<li ng-repeat="(key, value) in data | groupBy: 'groupName'">
<b>Group name: {{ key }}</b>
<ul>
<li ng-repeat="item in value">
<p><a target="_blank" href="{{item.url}}"> {{item.title}}</a></p>
</li>
</ul>
</li>
Working JSFiddle for your reference.
NOTE: groupBy filter is not part of angular.js You have to include angular.filter module.

Angular/MVC Duplicates In Repeater Are Not Allowed

First, here is all the code that leads me to the error I'm getting:
The JSON:
[
{
"Id": 0,
"UserName": "uniqueusername",
"Photo": "base64string",
"Email": "user#user.com",
"Office": "Location "
},
{
"Id": 1,
"UserName": "uniqueusername",
"Photo": "base64string",
"Email": "user#user.com",
"Office": "Location"
},
{
"Id": 2,
"UserName": "uniqueusername",
"Photo": "base64string",
"Email": "user#user.com",
"Office": "Location"
},
{
"Id": 3,
"UserName": "uniqueusername",
"Photo": "base64string",
"Email": "user#user.com",
"Office": "Location"
},
{
"Id": 4,
"UserName": "uniqueusername",
"Photo": "base64string",
"Email": "user#user.com",
"Office": "Location"
}
]
It is generated using this function in my controller:
List<string> Names = arepo.GetAllAvionteUsers();
List<UserPreviewViewModel> AllUsers = new List<UserPreviewViewModel>();
int count = 0;
foreach(string name in Names)
{
UserPreviewViewModel preview = new UserPreviewViewModel(name);
preview.Id = count;
AllUsers.Add(preview);
count++;
if (count == 10) break;
}
return Json(new { Users = JsonConvert.SerializeObject(AllUsers, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore}) }, JsonRequestBehavior.AllowGet);
The View Model:
public int Id { get; set; }
public string UserName { get; set; }
public string Photo { get; set; }
public string Email { get; set; }
public string Office { get; set; }
the angular controller:
angular.module('app.module')
.factory('Users', ['$http', function UsersFactory($http) {
return {
AllUsers: function () {
return $http({ method: 'GET', url: '/Controller/GetAllUsers' });
}
}
}]);
angular.module('app.module')
.controller('UserController', ['$scope', 'Users', function ($scope, Users) {
var vm = this;
Users.AllUsers().success(function (data) {
vm.users = JSON.stringify(data.Users);
});
}]);
And finally the view:
<table class="dataTable row-border hover" datatable="ng" dt-instance="vm.dtInstance"
dt-options="vm.dtOptions">
<thead>
<tr>
<th class="secondary-text">
<div class="table-header">
<span class="column-title">Id</span>
</div>
</th>
<th class="secondary-text">
<div class="table-header">
<span class="column-title">Name</span>
</div>
</th>
<th class="secondary-text">
<div class="table-header">
<span class="column-title">Photo</span>
</div>
</th>
<th class="secondary-text">
<div class="table-header">
<span class="column-title">Email</span>
</div>
</th>
<th class="secondary-text">
<div class="table-header">
<span class="column-title">Office</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in vm.users">
<td>{{user.Id}}</td>
<td>{{user.UserName}}</td>
<td><img class="product-image" ng-src="data:img/jpg;base64,{{user.Photo}}"></td>
<td>{{user.EmailAddress}}</td>
<td>{{user.Office}}</td>
</tr>
</tbody>
</table>
Every time that I try to run this code I get the following error from my JSON:
angular.js:13920 Error: [ngRepeat:dupes] Duplicates in a repeater are
not allowed. Use 'track by' expression to specify unique keys.
Repeater: user in vm.users, Duplicate key: string:\, Duplicate value:
\
I have tried to use angular's suggested fix, which is track by $index and all that does is cause my page to freeze.
I have tried to take out the Formatting.Indented when I return the JSON string and all that does is give me the same error as well as taking out the ReferenceLoopHandling part and also getting the same error.
In the angular controller I tried to do JSON.parse(data) and I get the following error:
angular.js:13920 SyntaxError: Unexpected token o in JSON at position 1
When I try to do let users = data.Users and then do let count = users.length it gives me the number 85941 which seems like it is counting every single character in the string.
When I do a console.log(data) it gives me the JSON that I pasted above (I did change usernames, emails, and locations to keep my user's info safe).
At this point I have absolutely no clue what is wrong here or how to fix it, any help would be greatly appreciated.
Thanks!
Although I am not entirely sure as to why this error occurred in first place, the fix for it was not to return JSON from the controller. I modified my return statement like so:
return Json(JsonConvert.SerializeObject(AllUsers, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore}), JsonRequestBehavior.AllowGet);
Then in the angular controller i did:
vm.users = JSON.parse(data);
That gave me the proper JSON array and now it works perfectly.

How to filter in ngTables

I am using ng-table to generate my table.
but my data has two column, the first one is an object.
My function in controller :
$scope.allServers = function() {
$http.get("/volazi/getServers").success(function(data) {
$scope.serversDTO = data;
$scope.tableParams = new NgTableParams({}, {
dataset: data
});
});
}
So my data will be like:
[{
server {
name: "ser1",
date: "..",
group: {
name: "G1",
created: ".."
}
},
status
}, ...]
how i can use filter in html
<tr ng-repeat="sr in $data">
<td title="'Name'" filter="{server.name: 'text'}" sortable="'server.name'">
{{ sr.server.name }}
</td>
</tr>
Its not working like that
You should apply the filter to the loop:
<tr ng-repeat="sr in $data | filter: { server.name: 'text' }">
I solved th proble by adding ''
i replace
filter="{server.name: 'text'}"
by
filter="{'server.name': 'text'}"
This will be really very helpful :LINK

Angularjs checkbox multiple filter

I have factory, that send request to get some data. After responce, i will receive it in controller and create scope list. Than i must to filter this list by checking checkboxes. I'v receive results, but they not visible. Help me pls...
$scope.checkRooms = [];
$scope.filterRooms = function(app) {
return function(p) {
for (var i in $scope.checkRooms) {
if (p.rooms == $scope.uniqueRooms[i] && $scope.checkRooms[i]) {
return true;
}
}
};
};
UPDATE 2
Here is working fiddle . Now how to sort by ASC rooms numbers? "orderBy" function sort correct but rooms indexes sort wrong
Ok here's a slightly different approach whereby the filtering is done in the controller rather than using the filter:expression in your ng-repeat.
Not the only way to do it but I think you should definitely think about removing any watch functions from your controllers they make it really difficult to test your controllers.
Fiddle
HTML
<div class="filter-wrap" ng-controller="mainController">
<div class="main-filter">
<div class="form-group">
<span class="gr-head">
Rooms count
</span>
<div class="check-control" ng-repeat="room in uniqueRooms | orderBy: room">
<input
type="checkbox"
name="room_cnt"
ng-model="checkboxes[room]"
ng-change='onChecked(filterRoom)'
/>
<label>{{room}}</label>
</div>
</div>
</div>
<table>
<thead>
<tr>
<th>
<span>Rooms</span>
</th>
<th>
<span>Size</span>
</th>
<th>
<span>Price</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="app in filteredApps">
<td>{{app.rooms}}</td>
<td>{{app.size}}</td>
<td>{{app.price}}</td>
</tr>
</tbody>
</table>
<div class="result">
<h2>SCOPE size</h2>
{{filteredRooms}}
</div>
</div>
JS
var sortApp = angular.module('sortApp',[]);
sortApp.controller('mainController', function($scope, $timeout) {
$scope.apps = [
{
"rooms": 2,
"size": 55.50,
"price": 55500.00,
},
{
"rooms": 1,
"size": 25.50,
"price": 45000.00,
},
{
"rooms": 8,
"size": 28,
"price": 15500.00,
},
{
"rooms": 1,
"size": 28,
"price": 15500.00,
},
{
"rooms": 8,
"size": 28,
"price": 15500.00,
},
{
"rooms": 3,
"size": 120.55,
"price": 88990.00,
},
{
"rooms": 3,
"size": 120.55,
"price": 88990.00,
}
];
$scope.filteredApps = $scope.apps;
$scope.uniqueRooms = uniqueItems($scope.apps, 'rooms');
$scope.onChecked = filterRooms;
$scope.checkboxes = createCheckboxes($scope.uniqueRooms);
function filterRooms(checkboxes){
$scope.filteredApps = [];
angular.forEach($scope.apps, function(app){
if($scope.checkboxes[app.rooms]){
$scope.filteredApps.push(app);
}
});
}
function createCheckboxes(labels){
var checkboxes = {};
angular.forEach(labels, function(label){
checkboxes[label] = true;
});
return checkboxes;
}
function uniqueItems(data, key) {
var result = [];
for (var i = 0; i < data.length; i++) {
var value = data[i][key];
if (result.indexOf(value) == -1) {
result.push(value);
}
}
return result;
};
});

Another Infinite $digest Loop

Doing a loop within a loop in a view:
<tr ng-repeat="result in results">
<td>
<span ng-repeat="device in helpers.getIosDevices(result.ios_device)">
{{ device.code }}
</span>
</td>
</tr>
The controller:
$scope.helpers = CRM.helpers;
The helper:
var CRM = CRM || {};
CRM.helpers = {
// Handle "111" value format
getIosDevices: function (devices) {
var obj = [];
if (devices !== null && devices !== undefined) {
if (devices.charAt(0) === '1') {
obj.push({
code: 'ipod',
name: 'iPod',
});
}
if (devices.charAt(1) === '1') {
obj.push({
code: 'ipad',
name: 'iPad',
});
}
if (devices.charAt(2) === '1') {
obj.push({
code: 'iphone',
name: 'iPhone',
});
}
}
return obj;
}
};
Got this error: https://docs.angularjs.org/error/$rootScope/infdig?p0=10&p1=%5B%5B%22fn:%E2%80%A620%20%20%7D;%20newVal:%20undefined;%20oldVal:%20undefined%22%5D%5D
as I understand but I don't know how can I solve it in my case. What workaround should I use?
The reason of this error that you try to change source list in ng-repeat directive during digest cycle.
<span ng-repeat="device in helpers.getIosDevices(result.ios_device)">
^^^^^^^^
and obj.push(/* ... */) in getIosDevices
First we need ask our self when digest cycle will stop looping: It will stop when Angular detect that on several iterations the list didn't change. In your case each time when ng-repeat calls getIosDevices method - the list gets different items and therefore it looping again till you get limit and Angular drops this exception.
So what is a solution:
In Angular its not good practice to call method getList() in ngRepeat. Because developpers make bugs.
Its clear that in your case getIosDevices() list depends on results therefore I would create different fixed object with some watcher on results and write HTML part like:
<tr ng-repeat="result in results">
<td>
<span ng-repeat="device in devices[result.ios_device]">
{{ device.code }}
</span>
</td>
</tr>
where devices represents Map.
This is some demo that might help you:
$scope.results = [{
ios_device: "100"
}, {
ios_device: "010"
}, {
ios_device: "001"
}];
$scope.devices = {
"100": [{
code: 'ipod',
name: 'iPod1',
},
{
code: 'ipod',
name: 'iPod2',
}],
"010": [{
code: 'ipod',
name: 'iPod1',
},
{
code: 'ipad',
name: 'iPad2',
}],
"001": [{
code: 'ipod',
name: 'iPod1',
},
{
code: 'iphone',
name: 'iphone2',
}],
}
HTML
<table>
<tr ng-repeat="result in results">
<td><span ng-repeat="device in devices[result.ios_device]">
{{ device.code }}
</span>
</td>
</tr>
</table>
Demo in Fiddle

Resources