angular 2 cannot access array of objects from json - json

I am connecting my app with an API. whenever I do a post request, I will get a json back that looks like this
{
"output": {
"text": {
"values": [
"Sure ! What kind of mouse would you like"
],
"selection_policy": "sequential"
},
"cards": [
{
"Mouse_Type": "Wireless"
},
{
"Mouse_Type": "Optical"
}
]
}
}
in my html I have created this:
<ul class="cards-options">
<li *ngFor="let options of listOptions" >
<span class="card">
<p>{{options.Mouse_Type }}</p>
</span>
</li>
</ul>
my component is like this:
export class InputFieldComponent implements OnInit {
value = '';
listOptions = '';
public context :object;
constructor(private http : Http) { }
ngOnInit() {
}
onEnter(value: string) {
this.http.post('/APIconversation', {"input":value })
.map(response=>response.json())
.subscribe(
data => {
this.listOptions = data.output.cards;
console.log(this.listOptions);
}
)
}
Whenever I inspect the element of the *ngFor I find this:
bindings={ "ng-reflect-ng-for-of": "[object Object],[object
Object" }
How can I access the string within the object? I did the same exact thing with another API and it was working.
Thanks for your help.

Related

Angular 8 - Implementing a hierarchical list recursively with dynamic expansion and collapse

I am trying to build a hierarchical expand/collapse list to represent a parent child relationship. Upon Initial load, the parent nodes will be listed. If they have children, a carat icon is displayed otherwise, a bullet icon is displayed. Upon clicking the carat icon, an API call is made to retrieve data and the child information is displayed. Upon clicking the same carat icon again, no API call is made, but the immediate child list is hidden. This should happen recursively in case the children are parent nodes.
Desired behavior on Initial load:
Desired behavior after carat icon is clicked to expand:
JSON Data
{
"page": {
"results": [{
"id": "1001",
"title": "American",
"children": {
"page": {
"results": [{
"id": "1003",
"title": "Chevy",
"children": {
"page": {
"results": [],
"start": 0,
"limit": 25,
"size": 0
}
}
}],
"start": 0,
"limit": 25,
"size": 1
}
}
}, {
"id": "1002",
"title": "German",
"children": {
"page": {
"results": [],
"start": 0,
"limit": 25,
"size": 0
}
}
}],
"start": 0,
"limit": 2,
"size": 2
}
}
Component Code
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import { WikiService } from '../wiki.service';
import { WikiTree } from '../../interfaces/WikiTree';
import { AppConfigService } from '../../services/app-config.service';
#Component({
selector: 'app-wiki-tree-nav',
templateUrl: './wiki-tree-nav.component.html',
styleUrls: ['./wiki-tree-nav.component.css']
})
export class WikiTreeNavComponent implements OnInit {
public wikiPageId: any = '1000';
wikiTree$: Observable<WikiTree>;
public resultsPage: any;
constructor(private wikiService: WikiService, private route: ActivatedRoute,
private appConfigService: AppConfigService) {
this.route.params.subscribe(res => this.wikiPageId = res.id);
}
ngOnInit() {
this.getWikiTree(0, 200, this.wikiPageId);
}
getWikiTree(start: number = 0, limit: number = 200, pageId: string = '') {
this.wikiTree$ = this.wikiService.GetWikiTree(start, limit, pageId);
this.wikiTree$.subscribe((data) => {
this.resultsPage = data;
},
(err) => {
}
);
}
getCSSClass(pageId: string) {
let cssClass = '';
if (this.wikiPageId === pageId) {
cssClass = 'boldTitle';
}
return cssClass;
}
}
HTML Code
<ul *ngIf="wikiTree$ | async as wikiTree">
<li *ngFor="let result of wikiTree.page.results; index as i" style="margin-bottom:10px;">
<div>
<span *ngIf="result.children.page.size==0" style="margin-left:4px;margin-right:4px;">
<clr-icon shape="circle" class="is-solid" size="6"></clr-icon>
</span>
<span *ngIf="result.children.page.size>0">
<a (click)="getWikiTree(0,200,result.id)">
<clr-icon shape="caret right" size="14"></clr-icon>
</a>
</span>
<span style="margin-left:5px;font-size:14px;" [ngClass]="getCSSClass(result?.id)">
{{result.title}}
</span>
</div>
</li>
</ul>
My current HTML code replaces the entire tree instead of appending to the parent nodes. How can I implement the Angular HTML code to perform the expansion and collapse recursively?
I go to create a structural directive, so my app.component.html becomes like
<div recursive [children]="data" [search]="dataService.getData"></div>
where I defined
constructor(public dataService:DataService){}
ngOnInit()
{
this.dataService.getData(0).subscribe(res=>this.data=res)
}
I need pass the "search" function to the recursive component that return an Observable of an array of objects, the objects has as properties 'id','label' and 'hasChildren' to indicate if has children or not, e.g. the json response becomes like
[{id:1,label:'label1',hasChildren:true},{id:2,label:'label2'}]
In a recursive component I like defined as #Input() level,children and parent. As you want to, in a click call to a function that get the "children", I add the search and in a "open" function, I change the property "isOpen" and call to the searchFunction. See that in the object that manage the component, at first there're no property 'children' nor 'isOpen', I add these "on-fly"
#Component({
selector: "[recursive]",
templateUrl: "./tree-view.component.html",
styleUrls: ["./tree-view.component.css"]
})
export class TreeViewComponent {
#Input() level: number;
#Input() children: any;
#Input() parent: any;
#Input() search: (any) => Observable<any>;
self = this;
open(item) {
item.isOpen = !item.isOpen;
if (!item.children) {
item.loading="...."
this.search(item.id).subscribe(res=>{
item.loading=null
item.children=res;
})
}
}
}
With this conditions, our tree-view.html is like
<ul class="tree" *ngIf="level==undefined">
<ng-container *ngTemplateOutlet="tree;context:{children:children,search:search}">
</ng-container>
</ul>
<ng-container *ngIf="level!=undefined">
<ng-container *ngTemplateOutlet="tree;context:{children:children,search:search}">
</ng-container>
</ng-container>
<ng-template #tree let-children="children" let-search="search">
<li *ngFor="let item of children">
<div (click)="item.hasChildren && open(item)">
<span [ngClass]="!item.hasChildren?'doc':item.isOpen?'open':'close'" ></span>
{{item.label}}{{item.loading}}
</div>
<ul recursive *ngIf="item.children && item.isOpen"
[children]="item.children"
[parent]="self"
[level]="level!=undefined?level+1:0"
[search]="search"
>
</ul>
</li>
</ng-template>
see stackblitz, I hope this help you
NOTE: I use three css class, .doc,.open and .close to indicate the states of the tree and a "fool" service with of and delay to simulate a call

Replace an Url from Json with a button that gives URL to another function

I’m trying to implement a function in my web application, that can discover URLs in a loaded Json via Regular Expressions using angular. Afterwards the URLs get replaced with buttons and when the button gets clicked the exact URL who got replaced gets handed in another function in another component which loads the given URL.
Until now I’m at the point that I can replace the URL of the loaded JSON with a button. I’m using a pipe for that named transform-url.pipe:
import {
Pipe,
PipeTransform,
Input,
Component
} from '#angular/core';
import {
DomSanitizer
} from "#angular/platform-browser";
#Pipe({
name: 'transformUrl',
pure: false
})
export class TransformUrlPipe implements PipeTransform {
constructor(protected sanitizer: DomSanitizer) {}
transform(value: any, ): any {
if (value.match === 0) {
return value;
}
return this.sanitizer.bypassSecurityTrustHtml(
value.replace(/"url:\/\/.*\/.*\/.*"/g, "<button type='button' >Run</button>")
);
}
}
Hmtl:
<h3>Unrecognized JSON data:</h3>
<pre [innerHTML] = "genericJson | transformUrl"></pre>
Sample of Json:
"documentVersion": "1.0",
"documentType": "Urls",
"name": {
"request": {
"version": "1.0",
"abc": [
{
"productUrl": "url://awrtwtgsfgshsfh/sfg/v1/document/jsfhljhl564356lhgljhsljh5895hj",
"attributes": [
{
"attributeSet": {
"attributes": {
"426824828246824828282468248": {
"value": "1"
},
"647474373748648248282824": {
"value": "true"
}
}
}
}
]
},
"productUrl": "url://adgagjfjfjfjhf/sfg/v1/document/adfah5ahfah5jahlkhaliohßjkjlaß73-3",
"attributes": [
{
"attributeSet": {
"attributes": {
"426824828246824828282468248": {
"value": "1"
},
"647474373748648248282824": {
"value": "true"
}
}
}
}
]
},
I found nothing on google on how to do this specific task. Is a pipe even the best solution for this? I tried to implement functions in the pipe but it didnt work.
Another thing that i cant figure out is how i can make every button unique so the application knows which excat URL it should take when the button is clicked? And how can I select the URL and give it in another function in another component?
First of all rather than using pipe, I have created solution in Component only.
Stringify JSON which needs to be get values from. genericJson
Remove first and last ", which is matched in regex.
Using *ngFor, create buttons and pass separate link to click function.
Demo (check console for button click)
EDIT: NEW DEMO.
import { Component } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
name = "Angular";
genericJson = { ... some json ... }
formatedOutput: (string | boolean)[] = [];
ngOnInit() {
// adding formatting to make regex search easier
const jsonFormattedString = JSON.stringify(this.genericJson, null, 4);
this.formatedOutput = jsonFormattedString.split('\n');
}
onClick(out: string) {
// do whatever operation on link
const link = out.match(/url:\/\/.*\/.*\/.*/)[0];
console.log(link);
}
urlFound(out: string): boolean {
const match = out.match(/"url:\/\/.*\/.*\/.*"/);
if (match !== undefined && match !== null && match.length > 0) {
return true;
}
return false;
}
}
Use matched links in HTML template,
<div>
<div *ngFor="let out of formatedOutput">
<ng-container *ngIf="urlFound(out); else simple_json"><pre>{{out}}<button (click)="onClick(out)">Link</button></pre></ng-container>
<ng-template #simple_json><pre>{{out}}</pre></ng-template>
</div>
</div>
I think, you should do it like this instead of using pipe:
.html
<button (click)="goTo(genericJson.url)">Run</button>
.ts
genericJson = {
url: "www.google.com"
};
goTo(url: string) {
if (url) {
if (!url.includes("https")) {
url = "https://" + url;
}
window.open(url, "_blank");
}
}

How can I pass json variable to array of objects

im currently working on this project and im facing this error when I try to pass a variable (this.arrayObj) into the composals array.
ngOnInit() {
this.apiService.getComposals().subscribe((res) => {
console.log(res.tpoCampos);
this.arrayObj = res.tpoCampos;
});
}
Im getting the Json content from a Web Service.
export class AppComponent {
title = 'Form';
arrayObj: any;
composals: Composal[] = [
this.arrayObj,
];
}
This is the content from the webservice
{
"m_iPatientID":2,
"tpoCampos":[
{
"m_cColor":"#000000",
"m_cType":"combo",
"m_cOptions":[
"Sim",
"N\u00e3o"
],
"m_cQuestion":"",
"m_cAnswer":"2",
"m_eIdComposant":4443
},
{
"m_cColor":"#000000",
"m_cType":"combo",
"m_cOptions":[
"Sim",
"N\u00e3o"
],
"m_cQuestion":"",
"m_cAnswer":"2",
"m_eIdComposant":4448
}
]
}
How can I pass the json to the composal object inside the variable this.arrayObj?
I think this.arrayObj is already an array of objects, so it could be written like this
composals: Composal[] = this.arrayObj;
export class AppComponent {
title = 'Form';
arrayObj: any;
composals: Composal[] = this.arrayObj;
}

Angular2 Getting very deep nested json value using pipe! *ngFor

Hi I am having trouble getting json value which is really deeply nested using pipe.
What am I doing wrong?
Pipe I'm using
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'keyValues'
})
export class KeysPipe implements PipeTransform {
transform(value, args: string[]): any {
let keys = [];
for (let key in value) {
keys.push({
key: key,
value: value[key]
});
}
return keys;
}
}
Json I'm getting from server.
data:
0: {
Profile: { ...
}
BasicInfo: { ...
}
introduceInfo: {
curriculum: { ...
}
experience: {
0: {
category: "Mentor"
year: "2011"
duration: "24"
}
1: {
category: "Student"
year: "2011"
duration: "14"
}
}
}
}
It's actually a huge json object but I've simplified to only show what I need to get.
I want to get the value of category (which is "Mentor"and "Student".
And to do so, I've tried in my html
<div *ngFor="let detail of teaInfo | keyValues">
<div *ngFor="let experience of detail.value['introduceInfo'] | keyValues">
<div *ngFor="let exp of experience.value['experience'] | keyValues">
<p class="fontstyle2">{{exp.value['category']}} {{exp.value['year']}}년 | {{ex.value['duration']}}개월</p>
</div>
</div>
</div>
And I'm getting my json object in my component like this.
teaInfo: any[];
getTeacherDetail(): void {
let params = new URLSearchParams();
params.set('gradeType', `${this.getVal2()}`)
params.set('subjectType', `${this.getVal3()}`)
params.set('district', `${this.getVal1()}`)
this.teaDetail.getTeachersDetail(params)
.subscribe(
teaInfo => this.teaInfo = teaInfo,
error => this.errorMessage = error
)
}
And the result is I am getting nothing
What am I doing wrong?
Trying to interpret how your JSON looks like, something like this:
{
"data":{
"0": {
"Profile":{
"prof":"prof"
},
"BasicInfo":{
"basic":"basic"
},
"introduceInfo":{
"curriculum": {
"curr":"curr"
},
"experience":{
"0":{
"category":"Mentor",
"year":"2011",
"duration":"24"
},
"1":{
"category":"Student",
"year":"2011",
"duration":"14"
}
}
}
}
}
}
In below example, I have extracted the values from data, so:
.map(res => res.json().data)
To reach values Mentor and Student, first change your pipe to this:
export class KeysPipe implements PipeTransform {
transform(value: any, args: any[] = null): any {
return Object.keys(value).map(key => value[key]);
}
}
and change your HTML to this:
<div *ngFor="let detail of teaInfo | keyValues">
<div *ngFor="let experience of detail['introduceInfo']['experience'] | keyValues">
{{experience.category}}
</div>
</div>
This should work nicely:
Demo

how to impliment Search particular record from .json file using angular2

What I need to do is when i enters text inside textbox , i need to filter result inside li
I need do same as here
but this is done using ajax and i need to do using angular2 with data loading from .json file
my index.html is
<div id="search-container">
<div class="search" id="search-btn"></div>
<input #searchBox id="search" type="text" name="search-input" (keyup)="search(searchBox.value)">
<ul class="data-ctrl">
<li ng-repeat="i in items >i[0].name</li>
</ul>
</div>
My app.component.ts is
constructor(public http: Http,public _dmcService: DmcService) {
this._dmcService.getData('prc').subscribe(
data => { this.listingval= "prc"; this.assignJson(data); }
);
}
assignJson function from app.component.ts:
here i assigned data to heroes which is loaded from json file
assignJson(data: any) {
displayContent= data.teacher[0].libraryItems;
for (var i = 0; i <displayContent.length; i++) {
this.heroes[i] = [
{ id: i, name: data.teacher[0].libraryItems[i].display_title.slice(30)}
];
}
}
dmc.service.ts:
getData(filename: string) {
return this.http.get('assets/data/' + filename + '.json').map((res: Response) => res.json());
}
prc.json:
{
"isbn": "9781328694829",
"teacher": [
{
"component": "Core Resources",
"libraryItems": [
{
"id": "9781328694829-00001",
"display_title": "Practice- Ungroup from the Left or from the Right"
},
{
"id": "9781328694829-00002",
"display_title": "Reteach- Ungroup from the Left or from the Right",
}
]
}
}
search function in app.component.ts:
search(term: string): void {
this.searchTerms.next(term);
}
ngOnInit(): void {
let heroes: Observable<Hero[]>;
this.heroes = this.searchTerms
.debounceTime(300) // wait for 300ms pause in events
.distinctUntilChanged() // ignore if next search term is same as previous
.switchMap(term => term // switch to new observable each time
// return the http search observable
? this.heroSearchService.search(term)
// or the observable of empty heroes if no search term
: Observable.of<Hero[]>([]))
.catch(error => {
// TODO: real error handling
console.log(error);
return Observable.of<Hero[]>([]);
});
}
my heroesSearchService is as
#Injectable()
export class HeroSearchService {
constructor(private http: Http) {}
search(term: string): Observable<Hero[]> {
console.log('term:: ',term);
var str = this.http.get(`app/heroes/?name=${term}`)
.map((r: Response) => r.json().data as Hero[]);
return str;
}
}
export class Hero {
id: number;
name: string;
}
my current code not working, can anyone please help me with this.