Saving and displaying "nested" JSON data from API - html

I have the following format of JSON data fetched from an API and stored in IntentList
{
"id": 22,
"name": "IntentName",
"fk_app": 3,
"fk_intent": null,
"nlu_models": [],
"sentences": [
{
"text": "text1",
"id": 2308
},
{
"text": "text2",
"id": 2309
},......
So there are these levels : the first having "name" and "sentences", and the second which is inside sentences, having "text".
My goal is to be able to search the API data By text and to display on each row the name and text found inside the sentences related to that text.
Therefore if I search "text" this would appear => IntentName text1 IntentName text2
if I search text1 this would appear => IntentName text1
-----What I have done/tried so far-----
I searched for corresponding text and stored its intent object and was only able to display its intent name
Typescript:
fetchIntentsTxt(event: any) {
if (event.target.value === '') {
return this.searchResultINTxt = [];
}
this.searchResultINTxt = this.IntentList.filter(
i => i.sentences.some(
s => s.text.toLowerCase().includes(event.target.value.toLowerCase())));
}
Html:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (keyup)="fetchIntentsTxt($event)"
class="form-control" id="search0" placeholder="Search for Intents by Text">
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt" >{{intentTxt.sentences.text(this doesn't work obviously)}}<span
class="float-right">{{intentTxt.name}}</span> <br></a>
Any recommendation is appreciated, even if it meant changing the search function. Thanks in advance.

Example: https://stackblitz.com/edit/angular-ivy-pyaq7a
I would just create a new array in the fetchIntentsTxt method and add the data I want to display by the user input/search. After "filtering" all the data I need I set it to the variable thats iterated over in the View/Template.
public fetchIntentsTxt(searchValue: string): void {
const searchResults = [];
for (const entry of this.intentList) {
for (const sentence of entry.sentences) {
if (sentence.text.toLowerCase().includes(searchValue)) {
searchResults.push({
name: entry.name,
text: sentence.text,
});
}
}
}
this.searchResultINTxt = searchResults;
}
View:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (ngModelChange)="fetchIntentsTxt($event)" class="form-control" id="search0" name="search0" placeholder="Search for Intents by Text"/><br />
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt">
{{ intentTxt.text }}<span class="float-right">{{ intentTxt.name }}</span>
<br/>
</a>
Also note here, I used the (ngModelChange) instead of the (keyup) on the search input so I do not need to hassle around with the events and just get the value I need for filtering.

Related

XPath separate key and value of group

I'm trying to fetch data from a dd group that is not really well structured. The 'group' does have a DD wrapper but inside it's only p/div without a grouped wrapper around it:
[DD]
[P] Key
[DIV]
[P] Value
[P] Key
[DIV]
[P] Value
Is it possible to collect the data the proper way?
The html code I'm processing:
<dd class="product-specifications-v2__items">
<p class="product-specifications-v2__key">
EAN/UPC - product
</p>
<div class="product-specifications-v2__value">
<p class="product-specifications-v2__value-item">
7912372
</p>
</div>
<p class="product-specifications-v2__key">
Weight
</p>
<div class="product-specifications-v2__value">
<p class="product-specifications-v2__value-item">
2,170
<span>kg</span>
</p>
</div>
</dd>
I currently get the following result as a array:
{
"key": [
"EAN\/UPC - product",
"Weight"
],
"value": [
"7912372",
"2,170 kg",
]
}
And I need to get (without arrays):
{
"key": "EAN/UPC - product",
"value": "7912372"
},
{
"key": "Weight",
"value": "2,170 kg"
}
I'm fetching the data via an API with the following request:
{
"name":"attributes",
"selector":"div.product-specifications-v2__wrapper dl dd",
"targets":[
{
"name":"key",
"selector":"p.product-specifications-v2__key",
"dataType":"title"
},
{
"name":"value",
"selector":"div.product-specifications-v2__value p.product-specifications-v2__value-item",
"dataType":"title"
}
]
}
Using XPath 3.1 (for instance, inside the browser with Saxon-JS (https://www.saxonica.com/saxon-js/documentation2/index.html), also with Node) you can use a path expression that creates an XPath 3.1 XDM map with the key and value:
//dd[#class = 'product-specifications-v2__items']/p[#class = 'product-specifications-v2__key']!map { 'key' : normalize-space(), 'value' : following-sibling::div[#class = 'product-specifications-v2__value'][1]!normalize-space() }
const html = `<dd class="product-specifications-v2__items">
<p class="product-specifications-v2__key">
EAN/UPC - product
</p>
<div class="product-specifications-v2__value">
<p class="product-specifications-v2__value-item">
7912372
</p>
</div>
<p class="product-specifications-v2__key">
Weight
</p>
<div class="product-specifications-v2__value">
<p class="product-specifications-v2__value-item">
2,170
<span>kg</span>
</p>
</div>
</dd>`;
var htmlDoc = new DOMParser().parseFromString(html, 'text/html');
const results = SaxonJS.XPath.evaluate(`//dd[#class = 'product-specifications-v2__items']/p[#class = 'product-specifications-v2__key']!map { 'key' : normalize-space(), 'value' : following-sibling::div[#class = 'product-specifications-v2__value'][1]!normalize-space() }`, htmlDoc, { 'xpathDefaultNamespace' : 'http://www.w3.org/1999/xhtml' });
console.log(results);
<script src="https://www.saxonica.com/saxon-js/documentation2/SaxonJS/SaxonJS2.rt.js"></script>
The JavaScript API of Saxon-JS returns the sequence of XDM maps as an array of JSON objects to JavaScript.

How to display complex object attributes in form fields in angular

I have a JSON object with the structure below which I want to display in a form for edit when I clicked on edit button on my table row.
json
{
"id": 123,
"name": "Paul",
"cars": [
{
"type": "toyota",
"year": "2013"
},
{
"type": "audi",
"year": "2010"
}
]
}
I need help on how to display the type of each cars object in my table file separated by comma(,). I got the object id and name displayed but not attributes in the cars array. How do I complete my .ts code so that the type of each car could show up in my table.
.ts
showRowDetail( data: any ) {
this.formData.controls.id.setValue( data.id );
this.formData.controls.name.setValue( data.name );
//how do I update here to set the cars: types
}
.html
<div class="form-group">
<label>ID:</label> <input type="text" class="form-control"
formControlName="id">
</div>
<div class="form-group">
<label>Name:</label><input type="text" class="form-control"
formControlName="name">
</div>
<div class="form-group">
<label>Cars:</label> <input type="text" class="form-control"
formControlName="cars">
</div>
I omitted the *ngFor table code, the showRowDetail function is used on the edit button, which when clicked it opens a modal that present the form where the object data are shown. data.id and data.name fields are working, just the cars type I needed help with.
how to display the "type" of each "cars" object in my table file
seperated by comma(,)
Generate comma separated type values from carsarray using map() and join().
showRowDetail( data: any ) {
this.formData.controls.id.setValue( data.id );
this.formData.controls.name.setValue( data.name );
let types = data.cars.map(car => car.type).join(", ");
this.formData.controls.cars.setValue(types);
}

Is there a method to display data from api where the id key is taken from another key?

JSON Data:
"abcd":[
{
"id":"1",
"cityId":"2",
},
{
"id":"2",
"cityId":"3",
}
],
"city":[
{
"id":"2",
"cityName":"california"
},
{
"id":"3",
"cityName":"vicecity"
}
]
Angular:
<div *ngFor="let clg of abcd$">
<p>
{{clg.cityId}}
<!-- Here i need to print the cityname from city by using the cityId we have got from abcd -->
</p>
</div>
app.component.ts:
ngOnInit() {
this.data.getColleges().subscribe(
data => this.abcd$ = data
)
}
fetching data from "abcd" is perfectly working....and no problem in fetching the datas from "city" too. But is it possible to fetch the cityName from "city" by using the cityId key from the "abcd" section.
You can use a method to get city by ID:
app.component.ts:
getCityByID = (cityID) => {
return this.city$.find(item => item.id === cityID);
}
Template:
<div *ngFor="let clg of abcd$">
<p>
{{ clg.cityId }}
{{ getCityByID(clg.cityId).cityName }}
</p>
</div>
Update
As far as I understand, you are fetching colleges and cities with 2 separate observables. Because of this, when you are trying to get city by ID, it may (or may not) throw an error if second observable has not been resolved yet. So, you need to combine/join these two streams together. I prefer combineLatest to do this but forkJoin will work as well.
In app.component.ts:
import { combineLatest } from 'rxjs';
......
ngOnInit() {
combineLatest(
this.data.getColleges(),
this.data.getCity()
).subscribe(([colleges, cities]) => {
this.abcd$ = colleges;
this.city$ = cities;
});
}
This way, you make sure that both abcd$ and city$ are inited. Hope this helps.
For further reading:
combineLatest: https://www.learnrxjs.io/operators/combination/combinelatest.html
forkJoin: https://www.learnrxjs.io/operators/combination/forkjoin.html

How to delete and update data in json format using read and write property of Node.js in angular2 application

I have a json file in assets/json/abc.json
I have a requirement that I need to read /abc.json File from assets folder and write some data or delete some data from that /abc.json file according to an input value from a form in html.
I have tried but its not working.
Here, read/write/delete .json file is according to an input from user through a click event.
abc.json
[
{
"imgPath": "fa-users",
"dashboardName": "Command Center",
"urlToVisit": "dashboards/static/commandcenter"
},
{
"imgPath": "fa-tachometer",
"dashboardName": "HP Dashboard",
"urlToVisit": "dashboards/static/hpdash"
},
{
"imgPath": "fa-cube",
"dashboardName": "HP APJ",
"urlToVisit": "dashboards/static/hpapj"
}
]
abc.component.html
<form class="rmpm" (submit)="AddNewDashboardBox($event)">
<div class="form-group rmpm">
<div class='col-xs-12 rmpm'>
Enter Dashboard Name
<br>
<input type="text" class="form-control rmpm" placeholder="Dashboard Name" name="dashboardName"
required>
<br>
</div>
<div class='col-xs-12 rmpm'>
Enter Icon Name
<br>
<input type="text"class="form-control rmpm" placeholder="Icon Name" name="IconName"
required>
<br>
</div>
<div class='col-xs-12 rmpm'>
Enter Url Path to Visit
<br>
<input type="text" class="form-control rmpm" placeholder="Url Path" name="UrlPath"
required>
<br>
</div>
</div>
<button type="submit" class="btn btn-info">Add</button>
</form>
abc.component.ts
AddNewDashboardBox(e) {
e.preventDefault();
let dashboardNameInput = e.target.elements[0].value;
let IconNameInput = e.target.elements[1].value;
let UrlPathInput = e.target.elements[2].value;
var obj = {
table: []
};
var json = JSON.stringify(obj);
var fs = require('fs');
fs.readFile('assets/json/abc.json', 'utf8', function readFileCallback(err, data) {
if (err) {
console.log(err);
} else {
obj = JSON.parse(data); //now it an object
obj.table.push({ "imgPath": IconNameInput , "dashboardName": dashboardNameInput, "urlToVisit": UrlPathInput }); //add some data
json = JSON.stringify(obj); //convert it back to json
fs.writeFile('assets/json/abc.json', json, 'utf8');
}
});
}
The angular application is running on client browser and the file you want to change is residing on server. So this thing is not possible.
You will have to write an api to which angular will make rest call with new data and then that server will make the required changes in file on file (that is on server).
I recommend you to see the client server architecture for in depth details.

knockout template parsing of json data is not working

Please check this link is not working, i have no idea what is wrong in my code.
I am trying to create a blog application, which have title, description and comments, but i am not getting proper output.
<h4>Title</h4>
<label data-bind="value: title" />
<h4>Description</h4>
<label data-bind="value: description" />
<h4>Comments</h4>
<p data-bind="foreach: comments">
<label data-bind="value: commenter" /><br>
<label data-bind="value: comment" /><br>
</p>​
var data = {"title": "blog1",
"description": "Description1",
"comments": [{"commenter": "commenter1", "comment": "comment1"},
{"commenter": "commenter2", "comment": "comment2"},
{"commenter": "commenter3", "comment": "comment3"},
{"commenter": "commenter4", "comment": "comment4"}]};
function Comment(data) {
this.commenter = ko.observable(data.commenter);
this.comment = ko.observable(data.comment);
}
function BlogViewModel(data) {
var self = data;
self.title = data.title;
self.description = data.description;
self.comments = ko.observableArray(ko.utils.arrayMap(data.comments, function (com) {
return new Comment(com.commenter, com.comment);
}));
}
ko.applyBindings(new BlogViewModel(data));
​
You have multiple problems with your code some are related to KnockOut some of them are not:
Which are not related to KO:
In BlogViewModel the self variable should hold this not the data parameter: so it should be var self = this;
Your comment mapping is wrong: the new Comment(com.commenter, com.comment) should be new Comment(com)
Which are related to KO:
The value binding is used for input elements, you have labels so you need to use the text binding instead. E.g data-bind="text: title"
KO needs valid html. Because the self-closing label tag is not valid you need to add the closing tags to your labels e.g <label data-bind="text: description"></label>
Here is a working JSFiddle containg all the fixes.