How to iterate over nested JSON in Angular - angular7

I'm using Angular7 and ngx-translate.
i18n/en.json looks something like this:
{
"items": {
"a1": "first",
"a2": "second",
"a3": "third"
},
"nesteditems": {
"n1": {
"f1": "nested11",
"f2": "nested12",
"f3": "nested13"
},
"n2": {
"f1": "nested21",
"f2": "nested22",
"f3": "nested23"
},
"n3": {
"f1": "nested31",
"f2": "nested32",
"f3": "nested33",
"f4": "nested34"
}
}
}
Is there a way to iterate over "items" and "nesteditems" using ngFor or some other directive to display them?

You could use the keyvalue pipe in angular 7 twice if you really wanted to stick to iterating over it using ngFor.
<div *ngFor="let item of nesteditems | keyvalue">
<div *ngFor="let innerItem of item.value | keyvalue">
{{innerItem .key}}:{{innerItem .value}}
</div>
</div>
Now another recommendation would be to consider flattening the nestedItems into an array in some way, now I dont know the shapeof your data so maybe that wouldnt be reasonable though.

Related

Making a button visible based on number of records in datatable

I am using jquery datatable in asp.net mvc and i want to show a submit button which will be saving the data to the database only if there is atleast one row in the datatable.
I am trying this code, however its not working
<tr id="trbtnSubmit">
<td colspan="9" align="center">
<input type="submit" name="btnSubmit" value="Save"
class="btn btn-edit btn-text" />
</td>
</tr>
<script>
var PopUp, dataTable;
$(document).ready(function () {
dataTable = $("#tblCustomerList").DataTable({
"ajax": {
"url": "/Customer/GetCustomers",
"type": "GET",
"data": "json"
},
"lengthChange": false,
"pageLength": 10,
"columns": [
{ "data": "Number" },
{ "data": "Name" },
{ "data": "fileName" },
{ "data": "mD5Hash" },
{ "data": "dataSizeInGB" },
{
"data": "Id",
"render": function () {
return "<a href='#'><i class='fa fa-eye'></a></i><a href='#' style='margin-left:5px'><i class='fa fa-pencil'></i></a><a href='#' style='margin-left:5px'><i class='fa fa-trash'></a></i>";
},
"orderable": false,
"width": "40px"
},
],
"language": {
"emptyTable": "No Customers , click on <b>New Customer</b> to add Customers"
}
});
var table = $('#tblCustomerList').DataTable();
if (!table.data().any()) {
$('#trbtnSubmit').hide();
} else {
$('#trbtnSubmit').show();
}
});
</script>
Since you didn't specify the version of datatables, I assume it's v1.10.
And there are 2 side notes I want to make before going into your problem:
Difference between .datatable() and .DataTable()
Enable server-side processing
Difference Between .datatable() and .DataTable()
I saw you declared another variable, var table, at the bottom of your sample code to get another instance of DataTables and check if there is any data? You actually don't need to.
.DataTable() returns a DataTables API instance, while .datatable() returns a jQuery object.
So if you intent to make usages on the DataTables APIs after you initialize the table, you can just use the varirable you declared from the beginning, var dataTable since you used .DataTable() way.
Enable Server-side Processing
Server-side processing is enabled by turning on the serverSide option, and configuring the ajax option. You're missing the first one, whose default is false.
So you might need to add serverSide option in your code:
dataTable = $("#tblCustomerList").DataTable({
serverSide: true,
ajax: {
...
},
...
});
Enough said. Now looking at your problem ...
DataTables Callbacks
There are many ways to achieve what you want to do, and I like to use callbacks so that you can configure your DataTables in one place.
There are lots of callbacks you can use, and the one I would use is drawCallback:
dataTable = $("#tblCustomerList").DataTable({
serverSide: true,
...,
language: {
emptyTable: "No Customers , click on <b>New Customer</b> to add Customers"
},
drawCallback: function(settings) {
$('#trbtnSubmit').toggle(settings.aoData.length > 0);
}
});
Hopefully my code is readable enough without any additional explanations :)

Angular2 Cannot bind to child array

I have a JSON object like the following:
{
"sessions": [
{
"title": Session Title,
"room": "Ballroom A"
},
{
"title": Session Title #2,
"room": "Ballroom B"
}
],
"speakers": [
{
"name": John Doe,
"twitter": "jdoe"
},
{
"name": John Smith,
"twitter": "jsmith"
}
]
}
I am trying to bind to the sessions child of the object. The full object is in a variable named conferenceData and the code I am using to display the titles is:
<div *ngFor="#session of conferenceData.sessions">{{session.title}}</div>
When I do this, I get the error: TypeError:
Cannot read property 'sessions' of undefined in [conferenceData.sessions in ProductListComponent#65:17]
If I assign the child to a variable:
this.sessionData = this.conferenceData.sessions;
Then bind to the sessionData variable instead, it works as expected. This is using TypeScript and Angular 2. I suppose this could be a type issue with TypeScript, but I thought by this time it was all JavaScript. Any help would be appreciated.
Thanks!
This is probably, because this.conferenceData is not assigned yet, when Angular is rendering your view the first time.
You can easily fix this with the elvis operator:
<div *ngFor="#session of conferenceData?.sessions">{{session.title}}</div>
The problem is the time to fetch the json data, the component doesn't have the data when the angular renders the view. The solution is uses 'elvis' (?) operator to ensure the data is ready:
-
<div *ngFor="#session of conferenceData?.sessions">
{{session.title}}
</div>
The second solution is hide the ngFor until the conferenceData is ready:
<div *ngIf="conferenceData">
<div *ngFor="#session of conferenceData.sessions">
{{session.title}}
</div>
</div>

How to work with nested data angularjs and rails?

I am working on an rails application to manage and present images to friends and family.
In that application you can have
Events -> Subevents -> EventImages
routes.rb
resources :events do
resources :subevents do
resources :event_images
end
end
The angularjs part/page is starting when a user selects an specific event.
On the event edit page i want to present the subevents and images like that:
Subevent1 -> Upload Images Button
Image1 Image2 Image3 ...
Subevent2 -> Upload Images Button
Image1 Image2 Image3 ...
...
New Subevent Button
So basically i want to present all available subevents and the images inside each subevent on one page (there could be several subevents and several 100 images). The user should be able to add new subevents and to upload or delete images to each subevent and moving images between subevents via drag/drop. But adding/deleting/uploading/mowing is not my problem right now i just mentioned it because it might influence the solution to my problem.
Im using a nested ng-repeat to display all the information on the page
<div class="subevent" ng-repeat="subevent in event.subevents">
<li class="img" ng-repeat="image in subevent.event_images">
</li>
</div>
Im new to the angular world and im having problems now on how to retrieve the data i need for the page mentioned above.
I came up with two ideas so far:
Retrieve all the informations via an api controller in an nested form:
show.json.jbuilder
json.id #event.id
json.subevents #event.subevents do |subevent|
json.id subevent.id
json.name subevent.name
json.event_images subevent.event_images do |event_image|
json.id event_image.id
json.thumb event_image.image({ resize: "150x150" }).url
end
end
Which will give me this:
{
"id": "54d75dd9686f6c2594020000",
"subevents": [
{
"id": "54d75de1686f6c2594030000",
"name": "Test",
"event_images": [
{
"id": "54df3904686f6c41cf0c0000",
"thumb": "/uploads/event_image/54df3904686f6c41cf0c0000/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
},
{
"id": "54df56c5686f6c41cf850100",
"thumb": "/uploads/event_image/54df56c5686f6c41cf850100/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
}
]
}
]
}
And im using restangular to retrieve this information
$scope.event = Restangular.one('api/events','<%= #event.id %>').get().$object;
But this solution doesnt feel right to me. When i start to manipulate the page (uploading new images/deleting images/moving images from one subevent to another) i see myself refreshing the complete page because i see no other way here on how to just delete one image for example without reloading the complete $scope.event to get the updated page.
The other solution which came to my mind but i did not get working so far was to use the nested features of restangular to retrieve all the needed information for my page without having to create a seperate api controller.
$scope.event = Restangular.one("events",'<%= #event.id %>').all("subevents").all("event_images");
I could not find a working solution which would let me iterate over the $scope.event with ng-repeat like i did above and im not sure if this would solve my overall problem.
So the questions i would like to have answered are:
Should i stick to the API controller appreaoch or is there a way to make my second idea working to get rid of the api controller ?
How do i manipulate the images/subevents without having to reload everything ? ( an example of deleting an image on the page would be great )
This answer your second question using your current API
Plunker
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.event = {
"id": "54d75dd9686f6c2594020000",
"subevents": [
{
"id": "54d75de1686f6c2594030000",
"name": "Test",
"event_images": [
{
"id": "54df3904686f6c41cf0c0000",
"thumb": "/uploads/event_image/54df3904686f6c41cf0c0000/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
},
{
"id": "54df56c5686f6c41cf850100",
"thumb": "/uploads/event_image/54df56c5686f6c41cf850100/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
}
]
}
]
};
});
app.directive('myEvent', function(){
return {
scope: { myEvent: '=' },
template: "<div>{{myEvent.id}}<div my-subevent='subevent' ng-repeat='subevent in myEvent.subevents'></div></div>",
controller: function($scope){
this.removeSubevent = function(e){
$scope.myEvent.subevents.splice($scope.myEvent.subevents.indexOf(e), 1);
};
}
};
});
app.directive('mySubevent', function(){
return {
scope: {mySubevent: '='},
template: "<div>{{ mySubevent.name }} <a href='' ng-click='remove()'>remove subevent</a><div my-subevent-img='img' ng-repeat='img in mySubevent.event_images'></div></div>",
require: '^myEvent',
link: function(scope, ele, attrs, myEventCtrl){
scope.remove = function(){
myEventCtrl.removeSubevent(scope.subevent);
};
},
controller: function($scope){
this.removeImg = function(img){
$scope.mySubevent.event_images.splice($scope.mySubevent.event_images.indexOf(img), 1);
};
}
};
});
app.directive('mySubeventImg', function(){
return {
scope: { mySubeventImg: '=' },
template: "<div><img ng-src='mySubeventImg.thumb'><a href ng-click='remove()'>remove img</a></div>",
require: '^mySubevent',
link: function(scope, ele, attrs, mySubeventCtrl){
scope.remove = function(){
mySubeventCtrl.removeImg(scope.mySubeventImg);
};
}
};
});
index.html
<div my-event="event"></div>

Accessing values in ViewModel in a dxList - PhoneJS

So in my PhoneJS web app, I have a dxList widget, with checkboxes on each item. I want to be able to select multiple items, and then do something with them. I'm trying to bind the 'checked' binding to an observable, but I get an 'undefined' error.
Here's the code for the dxTemplate for the list
<div data-options="dxTemplate:{name:'item'}">
<span data-bind="text: $data.information"></span>
<div data-bind="dxCheckBox: { checked: check_boxes }"></div>
</div>
The problem is that check_boxes is in the viewModel, not the item array. I need to access values in the viewModel. I've tried viewModel.check_boxes, but with no success.
Here's the js code:
AppNamespace.otherView = function (params) {
var viewModel = {
my_list: [
{
key: 'Group 1',
items: [
{ information: 'Test 1' },
{ information: 'Test 2'},
{ information: 'Test 3' }
]
}
],
check_boxes: ko.observable(false),
//...etc
Has anyone had any experience with this, and is there a solution?
Thanks!
Knockout provides special properties to access parent binding contexts. In your case both $parent and $root should work.
More on this topic in Knockout docs: Binding context.

angular using ui-select2 to assign an object as a model property

Hypothetical example to illustrate a problem I am having using angular-UI select2. Let's say I have a screen where I want to edit a "game" model. A game, among other things has players. I want to be able to set the players via a select2 drop down menu. Here's some example code:
app.js
$scope.getGamePromise().then(function(results) {
$scope.game = results;
console.log(game.players); //prints [{name:'Joe',age: 15},{name:'Sally',age:16}]
});
$scope.players = [
{
name: 'Joe',
age: 15
},
{
name: 'Fred',
age: 14
},
{
name: 'Sally',
age: 16
},
{
name: 'Lucy',
age: 13
}
]
view.html
<select ngModel="game.players" ui-select2 multiple>
<option ng-repeat="player in players" value="player">{{ player.name }}</option>
</select>
When I want to save this 'game' object, I send the game object up to the server. The server is expecting game.players to be an array of objects. However, what is being sent up is a string. I am moderately familiar with angular, and completely new to select2. How can I get my select2 to set game.players as an array of objects instead of strings?
I guess you find another solution or you don't have the problem anymore. Anyway I post it here:
Use
<input>
instead of
<select>
Example:
<input type="hidden" ui-select2="playersCfg" ng-model="players"/>
And following configuration:
$scope.playersCfg = {
multiple: true,
allowClear: true,
data: { results: Player.query(), text: 'name' },
formatResult: function(item) {
return "<div class='select2-user-result'>" + item.name + "</div>";
},
formatSelection: function(item) {
return item.name;
}
};
Player.query()
is a resource which returns a list of player containing a name (name) and an identifier (id)
Hope it would help somebody!

Resources