Nested Children Json in Angular HTML - html

I have a JSON with nested children. RowElements can have n colElements and colElements can have n RowElements.
JSON
"container": {
"id": "37ec0632-3391-4ef5-a9cb-25db2216fa75",
"rowElements": [
{
"id": "80e1dae7-b497-48d3-80ad-b94d36ada642",
"colElements": [
{
"id": "c662efe9-615c-4de5-97d3-2b02a5bce831",
"rowElements": [
{
"id": "b31960fd-c082-4d3b-88fc-c05f0cf837aa",
"colElements": []
},
{
"id": "7552306a-ca61-4d80-bb4e-d6c8d15c2c78",
"colElements": []
},
{
"id": "f669896d-132b-468e-8dcf-f2b59d509a76",
"colElements": []
}
]
}
]
}
]
}
How can i iterate over the children dynamically in the template like:
<div class="row">
<div class="col">
<div class="row">
<div class="col">
</div>
<div class="col">
</div>
<div class="col">
</div>
...
</div>
...
</div>
...
</div>

You can use nested *ngFor to iterate the JSON and display it dynamically.
I also created a stackblitz for you to check my solution.
HTML - I'm display the id here just to check the JSON.
<div class="row" *ngFor="let firstRow of arr.container.rowElements">
{{ firstRow.id }}
<div class="col" *ngFor="let firstCol of firstRow.colElements">
{{ firstCol.id }}
<div class="row" *ngFor="let secondRow of firstCol.rowElements">
{{ secondRow.id }}
<div class="col" *ngFor="let secondCol of secondRow.colElements"></div>
</div>
</div>
</div>
AppComponent.ts - I added a variable to hold your sample JSON
export class AppComponent {
arr = {
container: {
id: '37ec0632-3391-4ef5-a9cb-25db2216fa75',
rowElements: [{
id: '80e1dae7-b497-48d3-80ad-b94d36ada642',
colElements: [{
id: 'c662efe9-615c-4de5-97d3-2b02a5bce831',
rowElements: [{
id: 'b31960fd-c082-4d3b-88fc-c05f0cf837aa',
colElements: []
},
{
id: '7552306a-ca61-4d80-bb4e-d6c8d15c2c78',
colElements: []
},
{
id: 'f669896d-132b-468e-8dcf-f2b59d509a76',
colElements: []
},
],
}, ],
}, ],
},
};
constructor() {
console.log(this.arr.container);
}
}

Related

AngularJS nested objects in the same bootstrap row

i have a JSON object
{
"products": [
{
"devices": [
{
"label": "P1D1"
},
{
"label": "P1D2"
}
]
},
{
"devices": [
{
"label": "P2D1"
},
{
"label": "P2D2"
}
]
}
]
}
and i want to have in HTML something like this
<div class="row">
<div class="col-3">
P1D1
</div>
<div class="col-3">
P1D2
</div>
<div class="col-3">
P2D1
</div>
<div class="col-3">
P2D2
</div>
</div>
AngularJS is the language i am using, but i don't seem the find the right syntax
<div class="row">
<div ng-repeat="product in obj.products">
<div class="col-3" ng-repeat="device in product.devices">
{{device.label}}
</div>
</div>
</div>
How can i achieve all the subitems to be displayed in a bootstrap column next to eachother?
I think you can't do it directly using only HTML. Alternatively you can use some manipulation in Javascript to flatten your data.
JAVASCRIPT
$scope.getItems = function(){
return $scope.obj.products.flatMap(function(element){
return element.devices;
})
}
HTML
<div class="col-3" ng-repeat="device in getItems()">
{{device.label}}
</div>
Check my example:
https://codepen.io/avgustint/pen/XWYyoyd?editors=1111

Running nested for loops with Puppeteer page.$$eval(...)

I have an html document with two levels of repeating html elements. The first level is a variable number of test questions (1 -> n), and then within each test question, there are a variable number of possible answers (2 -> n).
Using Puppeteer's page.$$eval(...) function, I need to iterate through these two levels and capture the data associated with each level.
I am able to capture the first level data (test questions), but am unable to figure out how to iterate through and capture the nested inner level (possible answers).
Here's what I have so far...
Sample HTML:
<html>
<body>
<div id="8888" class="course-wrapper">
<div class="question">
<div class="q-label">Question 1</div>
<div class="q-question">Is this question #2?</div>
<div class="q-choices">
<div class="choice">
<div class="num">1.</div>
<div class="answer">yes</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">2.</div>
<div class="answer">no</div>
<div class="answer-check">correct</div>
</div>
<div class="choice">
<div class="num">3.</div>
<div class="answer">perhaps</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">4.</div>
<div class="answer">all of the above</div>
<div class="answer-check">incorrect</div>
</div>
</div>
</div>
<div class="question">
<div class="q-label">Question 2</div>
<div class="q-question">How far is it to Tipperary?</div>
<div class="q-choices">
<div class="choice">
<div class="num">1.</div>
<div class="answer">a long way</div>
<div class="answer-check">correct</div>
</div>
<div class="choice">
<div class="num">2.</div>
<div class="answer">not so far</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">3.</div>
<div class="answer">a very long way</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">4.</div>
<div class="answer">right next door</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">5.</div>
<div class="answer">just over the hill</div>
<div class="answer-check">incorrect</div>
</div>
</div>
</div>
<div class="question">
<div class="q-label">Question 3</div>
<div class="q-question">Is this question #3?</div>
<div class="q-choices">
<div class="choice">
<div class="num">1.</div>
<div class="answer">yes</div>
<div class="answer-check">correct</div>
</div>
<div class="choice">
<div class="num">2.</div>
<div class="answer">no</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">3.</div>
<div class="answer">perhaps</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">4.</div>
<div class="answer">all of the above</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">5.</div>
<div class="answer">i don't know</div>
<div class="answer-check">incorrect</div>
</div>
<div class="choice">
<div class="num">6.</div>
<div class="answer">none of your business</div>
<div class="answer-check">incorrect</div>
</div>
</div>
</div>
</div>
</body>
</html>
Target JSON:
[
{
"courseUNID": "8888",
"qCount": 3,
"qArray": [
{
"testQLabel": "Question 1",
"testQ": "Is this question 2?",
"testQAnswerChoiceCount": 4,
"testQPossibleAnswers": [
{
"Num": "1.",
"Answer": "yes",
"AnswerCheck": "incorrect"
},
{
"Num": "2.",
"Answer": "no",
"AnswerCheck": "correct"
},
{
"Num": "3.",
"Answer": "perhaps",
"AnswerCheck": "incorrect"
},
{
"Num": "4.",
"Answer": "all of the above",
"AnswerCheck": "incorrect"
}
]
},
{
"testQLabel": "Question 2",
"testQ": "How far is it to Tipperary?",
"testQAnswerChoiceCount": 5,
"testQPossibleAnswers": [
{
"Num": "1.",
"Answer": "a long way",
"AnswerCheck": "correct"
},
{
"Num": "2.",
"Answer": "not so far",
"AnswerCheck": "incorrect"
},
{
"Num": "3.",
"Answer": "a very long way",
"AnswerCheck": "incorrect"
},
{
"Num": "4.",
"Answer": "right next door",
"AnswerCheck": "incorrect"
},
{
"Num": "5.",
"Answer": "just over the hill",
"AnswerCheck": "incorrect"
}
]
},
{
"testQLabel": "Question 3",
"testQ": "Is this question 2?",
"testQAnswerChoiceCount": 6,
"testQPossibleAnswers": [
{
"Num": "1.",
"Answer": "yes",
"AnswerCheck": "incorrect"
},
{
"Num": "2.",
"Answer": "no",
"AnswerCheck": "correct"
},
{
"Num": "3.",
"Answer": "perhaps",
"AnswerCheck": "incorrect"
},
{
"Num": "4.",
"Answer": "all of the above",
"AnswerCheck": "incorrect"
},
{
"Num": "5.",
"Answer": "i don't know",
"AnswerCheck": "incorrect"
},
{
"Num": "6.",
"Answer": "none of your business",
"AnswerCheck": "incorrect"
}
]
}
]
}
]
Code (need help with the nested page.$$eval(...) function):
const scraped_post_test = async (page) => {
const courseUNID = await page.$eval("#8888", element => element.getAttribute("id"));
const qCount = await page.$$eval("#8888 > div.question > div.q-label", elements => {
return elements.length
});
const qLabel = await page.$$eval("#8888 > div.question > div.q-label", elements => {
return elements.map(element => element.textContent)
});
const qQuestion = await page.$$eval("#8888 > div.question > div.q-question", elements => {
return elements.map(element => element.textContent)
});
const qPossibleAnswersArray = await page.$$eval("#8888 > div.question > div.q-choices", elements => {
/*** nested iteration here? ***/
const answersArray = [
{
"Num": "1.",
"Answer": "a long way",
"AnswerCheck": "correct"
},
{
"Num": "2.",
"Answer": "something else",
"AnswerCheck": "incorrect"
},
...
]
return answersArray
});
let qArray = [];
for (let i = 0; i < qCount; i++) {
let testQObj = {};
testQObj.testQLabel = qLabel[i];
testQObj.testQ = qQuestion[i];
testQObj.testQAnswerChoiceCount = qPossibleAnswersArray.length;
testQObj.testQPossibleAnswers = qPossibleAnswersArray;
await qArray.push(testQObj)
}
return {
courseUNID,
qCount,
qArray
}
}
module.exports = { scraped_post_test }
I can get you started:
page.$$eval('.course-wrapper', divs => divs.map(div => {
let questions = [...div.querySelectorAll('.question')]
return {
courseUNID: div.id,
qCount: questions.length,
qArray: questions.map(q => {
let [label, question, choices] = [...q.querySelectorAll('.q-label', '.q-question', '.q-choices')]
return {
testQLabel: label.innerText,
...
}
})
}
}))

Why chartjs is not drawing in interior of a div?

I have a problem with ChartJs, because I try to put in interior of a div with class="col-6", it's only working if that class="row" and don't understand why.
pie-chart.components.ts:
ngOnInit() {
this.labels = ["ONE", "TWO", "THREE"];
this.chartInit();
}
chartInit() {
let chartElement = this.elementRef.nativeElement.querySelector("#myChart");
chartElement.width = 750;
chartElement.height = 750;
this.pieChart = new Chart(chartElement, {
type: "outlabeledPie",
data: {
labels: this.labels,
datasets: [
{
backgroundColor: [
"#FF3784",
"#36A2EB",
"#4BC0C0",
"#F77825",
"#9966FF",
"#00A8C6",
"#379F7A",
"#CC2738",
"#8B628A",
"#8FBE00"
],
data: [1, 2, 3]
}
]
},
options: {
plugins: {
legend: false,
outlabels: {
text: "%l %p",
color: "white",
stretch: 20,
font: {
resizable: true,
minSize: 12,
maxSize: 25
}
}
},
responsive: true
}
});
}
pie-chart.components.html:
<canvas id="myChart" height="750" width="750"></canvas>
home.components.html:
<div class="row">
<div class="col-6 text-center">
Available books
<app-pie-chart></app-pie-chart>
</div>
<div class="col-6 text-center">
Lended books
<app-pie-chart></app-pie-chart>
</div>
</div>
The code that is working (but I don't want this version):
<div class="row">
<div class="row text-center">
Available books
<app-pie-chart></app-pie-chart>
</div>
<div class="row text-center">
Lended books
<app-pie-chart></app-pie-chart>
</div>
</div>
So how can I display that two pie charts in interior of that two divs? And the second question is: why is only working in a div with class="row", and in a div without class or with other class it doesn't display anything?
Thank you!

Collapse/Expand nested div in Angular 6

I am developing a schedule like structure using div. And my design looks like this.
What i want is when loading, only 1st level should be displayed (all venues) and when clicking on venue its immediate child should be displayed and so on. Is there any way to do so. my html is:
<div class="div-table">
<div class="div-table-row">
<div class="div-header-col" style="visibility: hidden;">A</div>
<div *ngFor="let date of dates" class="div-date-col">{{date | date:'d E'}}</div>
</div>
<!-- level1 -->
<div *ngFor="let venue of venues" class="level1" style="color: red">
<div class="div-table-row-level1" >
<div class="div-header-col">{{venue.name}}</div>
<div *ngFor="let x of dates" class="div-event-level1-col"></div>
</div>
<!-- level2 -->
<div *ngFor="let category of venue.categories" class="level2" style="color: blue">
<div class="div-table-row-level2">
<div class="div-header-col" style="padding-left: 10px">{{category.name}}</div>
<div *ngFor="let x of dates" class="div-event-level2-col"></div>
</div>
<!-- level3 -->
<div *ngFor="let asset of category.assets" class="level3" style="color: green">
<div class="div-table-row-level3">
<div class="div-header-col" style="padding-left: 20px">{{asset.name}}</div>
<div *ngFor="let x of dates" class="div-event-level3-col assest-hover" "></div>
</div>
</div>
</div>
</div>
</div>
My data for the table(div) is :
[
{
"id":1,
"name":"venue1",
"categories":[
{
"id":1,
"name":"cat1",
"assets":[
{
"id":1,
"name":"assest1"
},
{
"id":2,
"name":"assest2"
}
]
},
{
"id":2,
"name":"cat2",
"assets":[
{
"id":3,
"name":"assest3"
},
{
"id":4,
"name":"assest4"
}
]
}
]
},
{
"id":2,
"name":"venue2",
"categories":[
{
"id":3,
"name":"cat3",
"assets":[
{
"id":5,
"name":"assest5"
},
{
"id":6,
"name":"assest6"
}
]
},
{
"id":4,
"name":"cat4",
"assets":[
{
"id":7,
"name":"assest7"
},
{
"id":8,
"name":"assest8"
}
]
}
]
},{
"id":3,
"name":"venue3",
"categories":[
{
"id":5,
"name":"cat5",
"assets":[
{
"id":9,
"name":"assest9"
},
{
"id":10,
"name":"assest10"
}
]
},
{
"id":6,
"name":"cat6",
"assets":[
{
"id":11,
"name":"assest11"
},
{
"id":12,
"name":"assest12"
}
]
}
]
}
]
If you have entire data loaded together then you have good news that it can be implemented in a very simple way -
You can play around Element Reference. Nothing needs to be managed from ts file.
in html
<div *ngFor="let venue of venues" class="level1" style="color: red"
(click)="ele.class = ele.class == 'showChildren' ? '' : 'showChildren'"
[ngClass]="{ hideChildren : ele.class !== 'showChildren' }">
Repeat the same for all parent div
in CSS
.hideChildren>div{
display: none;
}
Here is the working copy- https://stackblitz.com/edit/angular-n43ihd
There are some more way to handle if the data is being fetched in Asynchronous fashion. Then these logic will move to ts file.
You can try to hide component's on-load, and set them to visible onClick
$scope.$on('$viewContentLoaded', function() {
// Hide all unwanted div's here
});

Multidimensional array with jquery templates

I have the following javascript object
var arr = [
[
{ "id": 1, "name": "one" },
{ "id": 2, "name": "two" },
{ "id": 3, "name": "three" }
],
[
{ "id": 4, "name": "four" },
{ "id": 5, "name": "five" },
{ "id": 6, "name": "six" }
],
]
I'm trying to use jquery templates to create the following HTML
<div class="row">
<div class="cell">
<span>1</span> : <span>one</span>
</div>
<div class="cell">
<span>2</span> : <span>two</span>
</div>
<div class="cell">
<span>3</span> : <span>three</span>
</div>
</div>
<div class="row">
<div class="cell">
<span>4</span> : <span>four</span>
</div>
<div class="cell">
<span>5</span> : <span>five</span>
</div>
<div class="cell">
<span>6</span> : <span>six</span>
</div>
</div>
I am using the following templates with no luck :(
<script id="rowTemplate" type="text/x-jQuery-tmpl">
<div class="row">
{{tmpl "#cellTemplate"}}
</div>
</script>
<script id="cellTemplate" type="text/x-jQuery-tmpl">
<div class="cell">
<span>${id}</span> : <span>${name}</span>
</div>
</script>
The line that calls the template is the following:
$("#rowTemplate").tmpl(arr).replaceAll("#somediv");
I am getting only one row with one cell with no data...
<div class="row">
<div class="cell">
<span></span> : <span></span>
</div>
</div>
What am I doing wrong?
I think the problem is with usage of replaceAll and the missing parameter to tmpl in the template.
Try this(used replaceWith for #someDiv and passed $data as tmpl parameter for child template):
<script type="text/javascript">
var arr =
[
[
{
"id": 1,
"name": "one"
},
{
"id": 2,
"name": "two"
},
{
"id": 3,
"name": "three"
}
],
[
{
"id": 4,
"name": "four"
},
{
"id": 5,
"name": "five"
},
{
"id": 6,
"name": "six"
}
]
];
$(function(){
$("#somediv").replaceWith($("#rowTemplate").tmpl(arr));
});
</script>
<script id="rowTemplate" type="text/x-jQuery-tmpl">
<div class="row">
{{tmpl($data) "#cellTemplate"}}
</div>
</script>
<script id="cellTemplate" type="text/x-jQuery-tmpl">
<div class = "cell"><span>${id}</span>:<span>${name}</span></div>
</script>
<div id="somediv"></div>