Parent / Child component communication angular 2 - html

I am failing to implement action button in child_1 component but the event handler is in sub child component child_2 as shown in the following code:
app.component.html (Parent Html)
<div style="text-align:center">
<h1>
Welcome to {{title}}!
</h1>
<app-navigation></app-navigation> <!-- Child1-->
</div>
app.component.html (Parent Component)
import { Component } from '#angular/core';
import { ProductService } from './productservice';
import {Product} from './product';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = 'MobileShirtShoeApp';
}
app.module.ts (Main Module)
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpModule } from '#angular/http';
import { Product } from './product';
import { ProductService } from './productservice';
import { AppComponent } from './app.component';
import { NavigationComponent } from './navigation/navigation.component';
import { DataTemplateComponent } from './data-template/data-template.component';
#NgModule({
declarations: [AppComponent,NavigationComponent,DataTemplateComponent],
imports: [BrowserModule,HttpModule],
providers: [ProductService],
bootstrap: [AppComponent]
})
export class AppModule { }
navigation.component.html (Child 1 HTML)
<fieldset>
<legend>Navigate</legend>
<div>
<button (click)="loadMobiles()">Mobiles</button> <!--Child_1 Action-->
</div>
<app-data-template></app-data-template>
</fieldset>
navigation.component.ts (Child 1 Component.ts)
import { Component, OnInit } from '#angular/core';
import { ProductService } from '../productservice';
import {Product} from '../product';
import {DataTemplateComponent} from '../data-template/data-template.component';
#Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit {
error: string;
productArray: Product[];
constructor(private myService: ProductService){
this.myService = myService;
}
dataTemplateComponent: DataTemplateComponent = new DataTemplateComponent(this.myService);
ngOnInit() {
}
loadMobiles() {
return this.dataTemplateComponent.loadMobiles();
}
}
data-template.component.html (Child 2 HTML) (NOT DISPLAYING DATA)
<fieldset>
<legend>Requested Data</legend>
Welcome
<div>
<ul>
<li *ngFor="let product of productArray">
{{product.id}} {{product.name}} {{product.price}}
<img src="{{product.url}}">
</li>
</ul>
</div>
</fieldset>
data-template.component.ts (Child 2 Component) (Contains Product service calling code)
import { Component} from '#angular/core';
import {Product} from '../product';
import {ProductService} from '../productservice';
#Component({
selector: 'app-data-template',
templateUrl: './data-template.component.html',
styleUrls: ['./data-template.component.css']
})
export class DataTemplateComponent {
error: string;
productArray: Product[];
constructor(private productService: ProductService) {
this.productService = productService;
}
loadMobiles(){
let promise = this.productService.fetchMobiles();
promise.then(productArr => {
return this.productArray = productArr;
}).catch((err) => {
this.error = err;
});
}
}
ProductService.ts
import 'rxjs/add/operator/toPromise';
import {Http, HttpModule} from '#angular/http';
import {Injectable} from '#angular/core';
import {Product} from './product';
#Injectable()
export class ProductService{
http: Http;
constructor(http: Http){
this.http = http;
console.log(http);
}
fetchMobiles(): Promise<Product[]>{
let url = "https://raw.githubusercontent.com/xxxxx/Other/master/JsonData/MobileData.json";
return this.http.get(url).toPromise().then((response) => {
return response.json().mobiles as Product[];
}).catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
Sorry if the code bothers you. So basically i am failing to display service data in child_2.html when an action made in child_1.html.The service working fine and name is ProductService which uses Product.ts as an object to get the data in JSON format. Any kind of help is appreciated.

This doesn't work because the DataTemplateComponent you're instantiating in app-navigation isn't the same instance of DataTemplateComponent as the one on the page. It's a brand new one that you instantiated and that isn't bound to the page at all. What you're trying to achieve is component communication. Specifically, parent / child component communication. There are a number of ways to do this, the cleanest and most flexible / extensible way is with a shared service pattern. Basically, you declare a service with an observable in it that you inject into both services and one updates the observable while the other is subscribed to it, like this:
#Inject()
export class MyComponentCommunicationService {
private commSubject: Subject<any> = new Subject();
comm$: Observable<any> = this.commSubject.asObservable();
notify() {
this.commSubject.next();
}
}
Then provide this service, either at the app module or possibly at the parent component depending on needs then in app navigation:
constructor(private commService: MyComponentCommunicationService) {}
loadMobiles() {
this.commservice.notify();
}
and in data template:
constructor(private commService: MyComponentCommunicationService, private productService: ProductService) {}
ngOnInit() {
this.commSub = this.commService.comm$.subscribe(e => this.loadMobiles());
}
ngOnDestroy() { this.commSub.unsubscribe(); } // always clean subscriptions
This is probably a little unneccessary since you already have the product service there. You could probably just move the load mobiles logic into the product service and have that trigger an observable that the data template service is subscribed to, and have the nav component call the load mobile method on the product service, but this is just meant to illustrate the concept.
I'd probably do it like this:
#Inject()
export class ProductService {
private productSubject: Subject<Product[]> = new Subject<Product[]>();
products$: Observable<Product[]> = this.productSubject.asObservable();
loadMobiles() {
this.fetchMobiles().then(productArr => {
this.productSubject.next(productArr);
}).catch((err) => {
this.productSubject.error(err);
});
}
}
then nav component:
loadMobiles() {
this.myService.loadMobiles();
}
then data template:
ngOnInit() {
this.productSub = this.productService.products$.subscribe(
products => this.productArray = products,
err => this.error = err
);
}
ngOnDestroy() { this.productSub.unsubscribe(); } // always clean subscriptions

Related

Angular 6: use a service to get local json data

I have a movies.json that contain a list of movies and I want to create a MoviesServices to get the data where I want.
My MoviesServices:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { HttpErrorResponse } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class MoviesService {
movies: string[];
constructor(private httpService: HttpClient) {
this.getMovies();
}
getMovies() {
this.httpService.get('../../assets/movies.json').subscribe(
data => {
this.movies = data as string[];
console.log(this.movies); // My objects array
},
(err: HttpErrorResponse) => {
console.log(err.message);
}
);
console.log(this.movies); // Undefined
}
}
Firstly, I have no idea why the first console.log() works and the second not, can you tell me why ?
Here is my component where I need to get the data:
import { Component, OnInit } from '#angular/core';
import { MoviesService } from '../services/movies/movies.service';
#Component({
selector: 'app-movies',
templateUrl: './movies.component.html',
styleUrls: ['./movies.component.css']
})
export class MoviesComponent implements OnInit {
title = 'films-synopsys';
movies;
constructor(private myService: MoviesService) {}
ngOnInit() {
console.log(this.myService.movies); // Undefined
}
}
Of course this is not working. Can you tell me how must I do ? I'm newbie angular
So basically you need to return an Observable from your service and then subscribe to it from your Component. You can then assign your response to the Component property movies
Try this:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class MoviesService {
constructor(private httpService: HttpClient) { }
getMovies() {
return this.httpService.get('../../assets/movies.json');
}
}
And in your Component:
import { Component } from '#angular/core';
import { MoviesService } from './movies.service';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
title = 'films-synopsys';
movies;
constructor(private myService: MoviesService) {}
ngOnInit() {
this.myService.getMovies()
.subscribe(res => this.movies = res);
}
}
Here's a Sample StackBlitz for your ref.
Change your method to return an Observable which you can subscribe to:
import { Observable } from 'rxjs/Observable';
...
getMovies(): Observable<string []> {
this.httpService.get('../../assets/movies.json').subscribe(
data => {
this.movies = data as string[];
return this.movies;
},
(err: HttpErrorResponse) => {
console.log(err.message);
}
);
}
In your calling code:
import { Subscription } from 'rxjs/Subscription';
this.myService.getMovies().subscribe(movies => {
console.log(movies); // My objects array
}
The reason the first console log works is because you are doing it within an observable's subscription. Subscriptions have three states, Next, Error, Complete and so when you console log the first time, within the subscription next state you get the value that was pushed out from the event stream.
In your component the reason why it doesn't work is due to the fact that observables are lazy, and that you need to initialize the data by calling this.myService.getMovies() first to make the subscription happen.
A better way to do this would been to pass observables around and use async pipe in the html template.

TypeScript variable seems not to load the value from one file to another

I am following the tutorial here: https://coursetro.com/posts/code/154/Angular-6-Tutorial---Learn-Angular-6-in-this-Crash-Course
I currently have the code up to the section titled Fetching More Data from the API working. In short this is supposed to make a page with a bunch of users accessed through a toy API. The users are listed on a page with some personal information, and each one links to a very basic profile page displaying their details. Up to the section I mentioned above, I am successfully interfacing with the API, getting the users and displaying them and some personal information on the main page.
However, the code in this section is intended to collect an individual user's details and display them on a linked page. In trying to follow the example, my code does produce a page with the right outlines, but the value of the variable user$ and its fields like user$.name don't seem to be loaded by the details component. Where those values are supposed to show up, it's blank.
The app files:
app-routing.module.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { UsersComponent } from './users/users.component';
import { DetailsComponent } from './details/details.component';
import { PostsComponent } from './posts/posts.component';
const routes: Routes = [
{
path: '',
component: UsersComponent
},
{
path: 'details/:id',
component: DetailsComponent
},
{
path: 'posts',
component: PostsComponent
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
app.component.html
<div id="container">
<app-sidebar></app-sidebar>
<div id="content">
<router-outlet></router-outlet>
</div>
</div>
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SidebarComponent } from './sidebar/sidebar.component';
import { PostsComponent } from './posts/posts.component';
import { UsersComponent } from './users/users.component';
import { DetailsComponent } from './details/details.component';
import { HttpClientModule } from '#angular/common/http';
#NgModule({
declarations: [
AppComponent,
SidebarComponent,
PostsComponent,
UsersComponent,
DetailsComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
data.service.ts
import { Injectable } from '#angular/core';
import {HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class DataService {
constructor(private http: HttpClient) { }
getUsers() {
return this.http.get('https://jsonplaceholder.typicode.com/users')
}
getUser(userId) {
return this.http.get('https://jsonplaceholder.typicode.com/users'+userId)
}
getPosts() {
return this.http.get('https://jsonplaceholder.typicode.com/posts')
}
}
users files:
users.component.html
<h1>Users</h1>
<ul>
<li *ngFor="let user of users$">
{{user.name}}
<ul>
<li>{{user.email}}</li>
<li>{{user.website}}</li>
</ul>
</li>
</ul>
users.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.scss']
})
export class UsersComponent implements OnInit {
users$: Object;
constructor(private data: DataService) { }
ngOnInit() {
this.data.getUsers().subscribe(
data => this.users$ = data
);
}
}
details files:
details.component.html
<h1>{{ user$.name }}</h1>
<ul>
<li><strong>Username: </strong> {{user$.username}}</li>
<li><strong>Email: </strong> {{user$.email}}</li>
<li><strong>Phone: </strong> {{user$.phone}}</li>
</ul>
details.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
import { Observable } from 'rxjs';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-details',
templateUrl: './details.component.html',
styleUrls: ['./details.component.scss']
})
export class DetailsComponent implements OnInit {
user$: Object;
constructor(private data: DataService, private route: ActivatedRoute) {
this.route.params.subscribe( params => this.user$ = params.id );
console.log(this.route.params);
}
ngOnInit() {
this.data.getUser(this.user$).subscribe(
data => this.user$ = data
);
}
}
I tried logging values to the console, but I don't fully understand how TypeScript works so I don't fully know what I really should expect from these logs. In any case, in the log that I made in the details TypeScript file, it showed an object with no apparent loading errors.
this.route.params is a Observable. I am not sure what you can get by logging it.
I would suggest you add a log when subscribing to a observable and see what kind of data you really get. Like.
this.data.getUser(this.user$).subscribe(
data => {console.log(data);this.user$ = data}
);
#Adem, As Haijin comment, you must "work" a bit in subscribe parameter. The idea is that you subscribe to get the parameter, and, when you have the parameter, you ask about the user. But you need to make in the same step using switchMap
ngOnInit()
{
//You subscribe the param. When you have ask for the user
this.route.params.subscribe( params =>
{
this.user$ = params.id;
//In subscribe you ask for the user
this.data.getUser(this.user$).subscribe(
data => this.user$ = data
)
});
}
Well, really In Angular is better don't subscribe two times else using Rjxs and switchMap
ngOnInit()
{
//You subscribe the params, but you don't want the param
this.route.params.pipe(
switchMap(params =>
{ //you want return the user
return this.data.getUser(params.id)
})
).subscribe(data => this.user$ = data)
}

Firebase Cloud Firestore can not load data

When I open web data is show but I change component and come back this component data not show.It show no item. I check log but not run to ngOnIt. ngOnIt run on web strat or web reload.
this is stock.service.ts
import { Injectable } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
import { Observable } from 'rxjs/Observable';
import { Stock } from './stock.model';
#Injectable()
export class StockService {
stocksCollection: AngularFirestoreCollection<Stock>;
stocks: Observable<Stock[]>;
constructor(public afs: AngularFirestore) {
this.stocks = this.afs.collection('stocks').valueChanges();
}
getStock(){
return this.stocks;
}
}
stock.component.ts
import { Component, OnInit, Input } from '#angular/core';
import { ActivatedRoute,Params } from '#angular/router';
import { StockService } from '../shared/stock.service';
import { Stock } from '../shared/stock.model';
import { Brands } from '../shared/brand';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'app-stock',
templateUrl: './stock.component.html',
styleUrls: ['./stock.component.css']
})
export class StockComponent implements OnInit {
stocks: Stock[];
brand: Brands;
sub: Subscription;
brand_name: string;
constructor(
private stockService: StockService,
private router: ActivatedRoute
) { }
ngOnInit() {
this.stockService.getStock().subscribe(stocks => {
console.log(stocks);
this.stocks = stocks;
});
this.sub = this.router.params.subscribe(params =>{
this.brand_name = params['brand'];
});
console.log('Brand : '+this.brand_name);
}
}
and stock.component.html
<div *ngIf="stocks != null || stocks?.length > 0 ; else noStocks" >
<ul *ngFor="let item of stocks" class="collection">
<li class="collection-item " >{{item.name}} | {{item.brand}} | {{item.price}}</li>
</ul>
</div>
<ng-template #noStocks>
<p>no item</p>
</ng-template>
I don't know where code is mistake. Thank you for answer.
Don't load inside the constructor of your service, move it inside your method
constructor(public afs: AngularFirestore) {
}
getStock(){
this.stocks = this.afs.collection('stocks').valueChanges();
return this.stocks;
}

Console.log to html element - Angular 4

Simple question. I have the following response from web service and I am observing it on chrome console. How do I deploy this onto Html element in angular 4? I tried to convert into JSON, but I encountered with another problem so I just decided to go with what I received after parseString.
All I want to do is, to display those fields in html element using Angular. For now, I just have component.ts file and trying to do something in html but can't figure out.
import { HttpClient, HttpErrorResponse, HttpHeaders } from '#angular/common/http';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { Observable } from 'rxjs/Observable';
import { RequestOptions, Response } from '#angular/http';
import { Injectable } from '#angular/core';
import { parseString } from 'xml2js'
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
//import { IMovie } from './movie';
#Injectable()
export class AppService {
private urlNorth = 'service';
constructor(private http: HttpClient) { }
getMovies(): Observable<any[]> {
const headers = new HttpHeaders();
headers.set('Content-Type', 'text/sml');
headers.set('Accept', 'text/xml');
headers.set('Content-Type', 'text/xml');
return this.http.get<any[]>(this.urlNorth, { headers })
.map(res => {
var result = res.text().replace('<string xmlns="service">', '').replace('</string>', '').replace(/</g, '<').replace(/>/g, '>');
parseString(result, (err, resultN) => {
if (err) {
return console.dir('invalid XML');
}
else {
console.log(resultN);
}
})
})
.catch(this.handleError);
}
private handleError(err: HttpErrorResponse): ErrorObservable {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
const errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
console.error(errorMessage);
return Observable.throw(errorMessage);
}
}
Log data
This code:
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
Does not belong in your service file. This is a component decorator and it should be on your component. Like this:
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private _appService: AppService) { }
getProduction() {
this._appService.getProduction()
}
}
Then your index.html file should use the tag to display the HTML.
In looking at your code more closely, there are other issues as well. For example, you are calling getProduction two times. You should not be calling it from the service constructor.
Also, the subscribe should be in the component, not the service.
And you should be using Http OR HttpClient, not both.
And TestBed is only for use in tests ... not in services.
I have a more complete example of a working component/service here: https://github.com/DeborahK/Angular-GettingStarted in the APM-Final folder. Consider looking through that code (or starting with that code) and making adjustments as needed for your application.
Here is a working service. (Without a plunker I can't successfully show this with your code. So you will need to make the appropriate replacements for your example.)
Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import { IMovie } from './movie';
#Injectable()
export class MovieService {
private moviesUrl = './api/movies/movies.json';
constructor(private http: HttpClient) { }
getMovies(): Observable<IMovie[]> {
return this.http.get<IMovie[]>(this.moviesUrl)
.do(data => console.log(JSON.stringify(data)))
.catch(this.handleError);
}
private handleError(err: HttpErrorResponse): ErrorObservable {
// in a real world app, we may send the server to some remote logging infrastructure
// instead of just logging it to the console
const errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
console.error(errorMessage);
return Observable.throw(errorMessage);
}
}
Component:
import { Component, OnInit } from '#angular/core';
import { IMovie } from './movie';
import { MovieService } from './movie.service';
#Component({
templateUrl: './movie-list.component.html',
styleUrls: ['./movie-list.component.css']
})
export class MovieListComponent implements OnInit {
movies: IMovie[];
errorMessage: string;
constructor(private movieService: MovieService) { }
ngOnInit(): void { this.getMovies(); }
getMovies(): void {
this.movieService.getMovies()
.subscribe(
(movies: IMovie[]) => this.movies = movies,
(error: any) => this.errorMessage = <any>error);
}
}

Angularjs 4 HTTP Get request to json file

I have the folowing problem, i cant load the data from json.
What I'm trying to do is access the given file address and spell the data but something does not load I tried and without async
driver-list.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
#Injectable()
export class DriversListService {
private baseUrl: string = 'http://ergast.com/api/f1/2016/driverStandings.json';
constructor(private http : Http){}
data
getDriver() {
return this.http.get(this.baseUrl)
.map(res => this.data = res.json())
}
}
drivers-list-page.component.ts
import { Component, OnInit } from '#angular/core';
import { DriversListService } from '../drivers-list-page/drivers-list.service'
#Component({
selector: 'app-drivers-list-page',
templateUrl: './drivers-list-page.component.html',
styleUrls: ['./drivers-list-page.component.sass']
})
export class DriversListPageComponent implements OnInit {
drivers = []
constructor(private driverListServices: DriversListService) {}
ngOnInit(){
this.driverListServices.getDriver().subscribe(resDriverData=>this.drivers=resDriverData)
}
}
drivers-list-page.component.html
WORK
<ul class="items">
<li *ngFor="let driver of drivers | async">
<span>{{driver}}</span>
</li>
</ul>
enter image description here
Check the console log, to know more about the error...
then we can help you exactly
or try ...
import { Component, OnInit, Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { environment } from '../../environments/environment';
import 'rxjs/add/operator/map';
#Component({
selector: 'app-searcher',
templateUrl: './searcher.component.html',
styleUrls: ['./searcher.component.css']
})
#Injectable()
export class SearcherComponent implements OnInit {
// Pixabay API Key
private key:string = environment.PIXABAY_API_Key;
// API Url
url:string;
// Array of result
images:any[];
// Result per page
per_page:number;
// User query
query:string;
constructor(private result: Http) {
}
ngOnInit() {
}
// Get http result
letSearch(query){
this.url = "https://pixabay.com/api/?key=" + this.key + "&q=" + query;
return this.result.get(this.url).map(res => res.json()).subscribe(
data => console.log(data),
error => console.log(error),
() => console.log("Fine !")
);
}
}