Angular 2, How console the object object or loop through object object - wordpress

I am pulling the api from http://ecommerce-ux.london/wp-json/wp-api-menus/v2/menus/2 and it seem to have everything setup correctly and now would like to display from the api object.items ( that is the menu children)
When I do {{ menus }} I get [object object].
Apparently you can not use *ngFor if it is an object, so I needed to creat a pipe but still can figure out how to display the elements as all I get Keys or values.
Could someone please tell me where I gone wrong.
main-menu.component.html
<ul>
<li *ngFor="let menu of menus | menu">
{{ menu.key }} {{ menu.value }}
<ul>
<li *ngFor="let items of menu | menu">
{{ items.key }}
</li>
</ul>
</li>
</ul>
menu.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Menu } from './menu';
#Injectable()
export class MenuService {
private _wpMenuBase = "http://ecommerce-ux.london/wp-json/wp-api-menus/v2/";
constructor(private http: Http) { }
getPosts(): Observable<Menu[]> {
return this.http
.get(this._wpMenuBase + 'menus/2')
.map((res: Response) => res.json());
}
}
main-menu.components.ts
import { Component, OnInit } from '#angular/core';
import { Menu } from '../menu';
import { MenuService } from '../menu.service';
import { Router, ActivatedRoute, Params } from '#angular/router';
#Component({
selector: 'app-main-menu',
templateUrl: './main-menu.component.html',
styleUrls: ['./main-menu.component.css'],
providers: [MenuService]
})
export class MainMenuComponent implements OnInit {
menus: Menu[];
constructor( private menuService: MenuService, private router: Router ) { }
getPosts(){
this.menuService
.getPosts()
.subscribe(res => {
this.menus = res;
});
}
ngOnInit() {
this.getPosts();
}
}
menu.ts
export class Menu {
}
JSON
{
"ID":2,
"name":"Main menu","slug":"main-menu",
"description":"","count":2,
"items":[
{
"id":4,"order":1,"parent":0,
"title":"Sample Page","url":"http:\/\/ecommerce-ux.london\/sample-page\/","attr":"","target":"","classes":"","xfn":"","description":"","object_id":2,"object":"page",
"object_slug":"sample-page",
"type":"post_type",
"type_label":"Page"},
{
"id":7,
"order":2,
"parent":0,"title":"other page","url":"http:\/\/ecommerce-ux.london\/other-page\/","attr":"",
"target":"","classes":"","xfn":"",
"description":"",
"object_id":5,"object":"page","object_slug":"other-page","type":"post_type","type_label":"Page"}
],
"meta":
{
"links":
{
"collection":"http:\/\/ecommerce-ux.london\/wp-json\/wp\/v2\/menus\/",
"self":"http:\/\/ecommerce-ux.london\/wp-json\/wp\/v2\/menus\/2"
}
}
}
menu.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'menu'
})
export class MenuPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push({key: key, value: value[key]});
}
return keys;
}
}

You do not need a pipe in this case. The answer provided by Robin had a slight error in the inner iteration: <li *ngFor="let menuItem of menu">, you need to specify that the items is a subarray of menu:
<li *ngFor="let menuItem of menu.items">
So your html should look like this:
<div>
<h2>{{menu.name}}</h2>
<div *ngFor="let menuItem of menu.items">
{{menuItem.url}}<br>
<strong> Name: </strong> {{menuItem.id}}<br>
<strong> Title: </strong> {{menuItem.title}}<br>
</div>
</div>
Here's a demo plunker :)

The issue is that you can't ngfor through object object. The way you do it is via filter. Now in the end I created my own pipe, or so call filter. Not sure if it is right, but it worked like a charm.
main-menu.component.html
<ul>
<li *ngFor="let menu of menus | menu">
<a href="{{ menu.menuitem.url }}">
<strong> Name :</strong> {{ menu.menuitem.id }} |
<strong> Title :</strong> {{ menu.menuitem.title }} <br />
</a>
</li>
</ul>
PIPE
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'menu'
})
export class MenuPipe implements PipeTransform {
transform(value, args:string[]) : any {
//return value.filter( item => value[item] != value[item]);
let keys = [];
for (let key in value) {
if(key == 'items'){
for (let a in value[key]) {
keys.push({menuitem: value[key][a]});
// keys.push({menuName: value[key][a].id, menuName: value[key][a].title});
// console.log(value[key][a].id);
}
//keys.push({key: key, value: value[key]});
}
}
console.log(keys);
return keys;
}
}

Related

Filtering WordPress API with Angular pipe?

Currently trying to use a custom pipe to filter my *ngFor list items to toggle posts with a comment status of opened or closed. Seem to have run into a snag on setting it up.
Code is as follows:
app.component.html
<select (change)="onChange($event.target.value)">
<option value="all" selected="selected">All</option>
<option value="open">Open</option>
<option value="closed">Closed</option>
</select>
<ul>
<li *ngFor="let post of posts | myPipe:commentStatus">
<h1>{{ post.title.rendered }}</h1>
comment status: {{ post.comment_status }}
</li>
</ul>
app.component.ts
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'rest-ang';
posts = [];
wpUrl = 'http://wprest.local/wp-json/wp/v2/posts';
filterByComments= '';
//postsTitle: any = {};
constructor(private http: HttpClient) {}
ngOnInit(){
return this.http.get(this.wpUrl)
.subscribe(data => {
for(let key in data){
if(data.hasOwnProperty(key)){
this.posts.push(data[key]);
}
}
console.log(data);
//console.log(this.postsTitle);
})
}
onChange(optionFromMenu:string) {
if(optionFromMenu === 'all'){
this.posts = this.posts;
}
if(optionFromMenu === 'closed') {
this.posts = this.posts.filter(data => {
return this.posts.includes('closed');
});
}
}
}
mypipe.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'mypipe'
})
export class MyPipe implements PipeTransform {
transform(posts: any[], comment_status: any): any {
return posts;
console.log(comment_status);
if(comment_status === 'all') {
}
}
}
Although all my change is happening via the component.ts at the moment, I'd like to set it up in the pipe.ts file, but simply getting that working has got me a little stumped. Any suggestions appreciated.
If it helps, App was setup using Angular 6 via Angular CLI.
You can use template driven form way, make use of [(ngModel)] on select field & you'll no longer require that (change) method logic. So, update view code to:
<select [(ngModel)]="commentStatus">
<option value="all" selected="selected">All</option>
<option value="open">Open</option>
<option value="closed">Closed</option>
</select>
<ul>
<li *ngFor="let post of posts | myPipe:commentStatus">
<h1>{{ post.title }}</h1>
comment status: {{ post.comment_status }}
</li>
</ul>
Then update your transform method from pipe class, so that it will take current value of commentStatus variable & then filters posts array. So, the pipe code can be like:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'myPipe'
})
export class MyPipePipe implements PipeTransform {
transform(posts: any[], commentStatus: any): any {
if(!commentStatus) {
return posts;
}
if(commentStatus === 'all') {
return posts;
} else if(commentStatus === 'open' || commentStatus === 'closed') {
let filterdPosts = posts.filter((i) => {
return i.comment_status == commentStatus;
});
return filterdPosts;
}
}
}
Stackblitz Example

Angular 5 HTTP load json file error

newsService.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { INews } from './inews';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class NewsserviceService {
public _url = 'http://localhost/admin/demo/api/get_posts/';
constructor( public http: HttpClient) { }
getNews(): Observable<INews[]> {
return this.http.get<INews[]>(this._url).pipe(map(res => res.json()));
}
}
newsComponent.ts
import { Component, OnInit } from '#angular/core';
import { NewsserviceService } from './newsservice.service';
#Component({
selector: 'app-news-ticker',
templateUrl: './news-ticker.component.html',
styleUrls: ['./news-ticker.component.scss']
})
export class NewsTickerComponent implements OnInit {
public news: any[];
constructor(private newsservice: NewsserviceService) { }
ngOnInit() {
this.loadNews();
}
loadNews(): void {
this.newsservice.getNews().subscribe(res => this.news = res);
}
}
newscomponent.html
<section class="news-ticker">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="ticker">
<strong class="pull-left">news flash</strong>
<ul>
<li *ngFor='let news of news'>
{{news.content}}
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
console error
ERROR TypeError: res.json is not a function
at MapSubscriber.project (newsservice.service.ts:14)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:75)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:93)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:81)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:93)
at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterSubscriber._next (filter.js:85)
at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:93)
at MergeMapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber.notifyNext (mergeMap.js:136)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/InnerSubscriber.js.InnerSubscriber._next (InnerSubscriber.js:20)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:93)
its better that you write some info about your problem however your problem refer to
getNews(): Observable<INews[]> {
return this.http.get<INews[]>(this._url).pipe(map(res => res.json()));
}
in your service change it to :
getNews():Observable {
return this.http.get(this._url)
}
since the method give us observable we should subscribe it in target component you can just do :
loadNews() {
this.newsservice.getNews().subscribe(res => this.news = res);
}

Having issues trying to get downloadURL for multiple images from firebase storage using angular fire2

I have created a collection that holds the news title, news img, news desc, etc.. I also created the upload method that works fine , uploads the file to storage and saves the path to the collection.
Issues when trying to retrieve the image using getDownloadUrl();
Here are the files
news.component.html
<div class="container-fluid">
<div *ngFor="let item of thenews | async">
<p>
{{ item.newsTitle }} {{ item.newsDesc }}
</p>
<div class="col-md-4">
<img [src]="getImgUrl(item.newsImg)" class="img-responsive">
</div>
</div>
</div>
<div class="container-fluid">
<button mat-raised-button (click)="onLogout()">Logout</button>
</div>
News.component.ts
import { tap } from 'rxjs/operators';
import { AngularFireStorage } from 'angularfire2/storage';
import { AngularFirestoreCollection, AngularFirestoreDocument, AngularFirestore } from 'angularfire2/firestore';
import { Component, OnInit, Injectable } from '#angular/core';
import { AuthService } from './../auth.service';
import { DataService } from '../services/data.service';
import { News } from './../models/newsModel';
import { Observable } from 'rxjs';
#Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.css']
})
#Injectable()
export class NewsComponent implements OnInit {
news = {} as News;
newsCol: AngularFirestoreCollection<News>;
thenews: Observable<News[]>;
imgUrl: Observable<any>;
theData: News[] = [];
constructor(public authservice: AuthService, public dataservice: DataService, public afs: AngularFirestore,
private storage: AngularFireStorage) { }
ngOnInit() {
this.newsCol = this.afs.collection<News>('news');
this.thenews = this.newsCol.valueChanges();
}
getImgUrl(img) {
return this.storage.ref(img).getDownloadURL();
}
addNews(news) {
this.dataservice.addNews(news);
}
onLogout() {
this.authservice.onLogout();
}
}
When this is served it runs into an infinite loop. and the site goes hung.
Any help?
getDownloadURL() is a observable method (async function), so you have to wait for the observable to return the value i.e. the url or null if the image is not available
for example
imageUrl: Observable<string | null>;
const ref = this.storage.ref('users/davideast.jpg');
this.imageUrl= ref.getDownloadURL();
in template
<img [src]="profileUrl | async" />
Please refer this link from github
Hope this helps

Querying data from AngularFire2 with combinelatest

I could achieve some filtering behaviour with my question querying subset from angularfire2. Now I want to display these values as a list in Angular using ngFor. In my ts file I have:
export class OtherAnimals{
public animalList: Observable<{}>;
constructor(public af: AngularFire) {
this.animalList = Observable.combineLatest(
this.af.database.object('/set1/'),
this.af.database.object('/set2/'),
// Use the operator's project function to emit an
// object containing the required values.
(set1, set2) => {
let result = {};
Object.keys(set1).forEach((key) => {
if (!set2[key]) {
result[key] = this.af.database.object('/allanimals/' + key);
}
});
return result;
}
);
}
}
and in my .html file I have:
<ul>
<li *ngFor="let item of animalList | async">{{item.name}}</li>
</ul>
Might be worth it to build out a sub component that takes an animalId which will then go fetch animal information for you, and then display it. That way you can reuse it in other places. Also you won't have to build out crazy switchMaps or some other complex Observable patterns to solve all in one go.
other-animals.component.html
<ul>
<li *ngFor="let animalId of animalList | async">
<animal-item [animalId]="animalId"></animal-item>
</li>
</ul>
other-animals.component.ts
export class OtherAnimalsComponent {
private animalKeys: Observable<any>;
constructor(public af: AngularFire) {
this.animalKeys = Observable.combineLatest(
this.af.database.object('/set1'),
this.af.database.object('/set2'),
(set1, set2) => {
let list = [];
Object.keys(set1).forEach((key) => {
if (!set2[key]) { list.push(key); }
});
return list;
}
);
}
animal-item.component.html
<span>{{ (animalInfo | async)?.name }}</span>
animal-item.component.ts
#Component({
selector: 'animal-item',
templateUrl: 'animal-item.component.html'
})
export class AnimalItemComponent implements OnInit {
#Input() animalId: string;
animalInfo: Observable<any>;
constructor (private af: AngularFire) {}
ngOnInit () {
this.animalInfo = this.af.database.object(`/allanimals/${animalId}`);
}
}

Angular2 communication between 2 components

i am using Angular2 rc5 and i try to build a component communication through a service.
For arrays it works like expected but if i change a string its not updating.
My main component looks like:
import {Component} from '#angular/core';
import {ShareServiceService} from "../share-service.service";
import {ChildTwoComponent} from "../child-two/child-two.component";
import {ChildOneComponent} from "../child-one/child-one.component";
#Component({
selector: 'parent',
template: `
<h1>Parent</h1>
<div>
<child-one></child-one>
<child-two></child-two>
</div>
`,
providers: [ShareServiceService],
directives: [ChildOneComponent, ChildTwoComponent]
})
export class ParentComponent {
constructor() {}
}
My first children component:
import {Component} from '#angular/core';
import {ShareServiceService} from "../share-service.service";
#Component({
selector: 'child-one',
template: `
<div style="float:right; width: 45%">
<pre>title: {{title}}</pre>
<pre>title: {{_sharedService.testTitle}}</pre>
<div>
<ul *ngFor="let dataElement of data">
<li>{{dataElement}}</li>
</ul>
</div>
</div>
`
})
export class ChildOneComponent{
data:string[] = [];
title:string;
constructor(public _sharedService:ShareServiceService) {
this.data = this._sharedService.dataArray;
this.title = this._sharedService.testTitle;
}
}
The second children component:
import {Component} from '#angular/core';
import {ShareServiceService} from "../share-service.service";
#Component({
selector: 'child-two',
template: `
<div style="float:left; width: 45%">
<pre>title: {{title}}</pre>
<pre>titleObj: {{titleObj.title}}</pre>
<div>
<ul *ngFor="let dataElement of data">
<li>{{dataElement}}</li>
</ul>
</div>
<input type="text" [(ngModel)]="dataInput"/>
<button (click)="addData()">addData</button>
<button (click)="updateTitle()">updateTitle</button>
</div>
`
})
export class ChildTwoComponent {
dataInput:string = 'Testing data';
data:string[] = [];
title:string;
constructor(public _sharedService:ShareServiceService) {
this.data = this._sharedService.dataArray;
this.title = this._sharedService.testTitle;
}
addData() {
this._sharedService.insertData(this.dataInput);
this.dataInput = '';
}
updateTitle() {
this._sharedService.updateTestTitle(this.dataInput);
this.dataInput = '';
}
}
My service:
import {Injectable} from '#angular/core';
#Injectable()
export class ShareServiceService {
dataArray:string[] = [];
testTitle:string = "should be updated";
insertData(data:string) {
this.dataArray.push(data);
}
updateTestTitle(newTitle:string) {
this.testTitle = {title: newTitle};
}
}
What i try to achieve is, that if i enter something in the input field with binding for "" and press the "updateTitle" that the Title in both components are updated.
But thats doesnt work currently.
if i add my input value to an array, by clicking the "adddata" Button, all works like excepted and my list with data elements shows all elements.
Does someone know why i cant update a string?
Thanks in advance!
If you copy an object or array you copy a reference. Both (source and destination) point to the same object. If one side modifies the object the other side sees the modification.
If you copy a primitive value (string, number, boolean), the the destination gets a copy of the value and source and destination aren't related in any way.
// copies a reference
this.data = this._sharedService.dataArray;
// copies the value
this.title = this._sharedService.testTitle;
What you probably want is an observable that emits events when properties in the shared service are modified.
For more details see https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

Resources