Angular 6 - Sending an object between component using a Service - angular6

Actually I'm confronted to 2 issues with Angular.
My first consists on adding articles in a basket in different component. I send an object to a service and a second component retrieve it ...
My second issue is when i want to have a second product, i wish to display both of them and not overwritting it. I have been reading this : http://jasonwatmore.com/post/2018/06/25/angular-6-communicating-between-components-with-observable-subject in order to give me some skills ...
In my view i added a button :
<button class="btn btn-danger" (click)="onAddProduct()">Ajouter</button>
Here is my article component :
export class ProductComponent implements OnInit {
#Input() private product: Product;
private basketData: BasketdataService;
constructor(private basketData: BasketdataService) { }
ngOnInit() {
}
onAddProduct() {
console.log(this.product);
this.basketData.onAddBasket(this.product);
}
}
My service here retrieve the datas and share them with the basket component
export class BasketdataService {
private subject = new Subject<any>();
onAddBasket(product: Product) {
this.subject.next(product);
}
onGetBasket(): Observable<Product> {
return this.subject.asObservable();
}
removeArticleBasket() {
this.subject.next();
}
Here is my basketComponent
export class BasketmenuComponent implements OnInit, OnDestroy {
product : Product;
subscription : Subscription;
constructor(private basketdataService: BasketdataService) {
this.subscription = this.basketdataService.onGetBasket().subscribe(message => { this.product = product; });
}
ngOnInit() {
}
ngOnDestroy() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe();
}
}
I receive this error :
ERROR ReferenceError: product is not defined at SafeSubscriber._next

In basketComponent you've an error, try this:
export class BasketmenuComponent implements OnInit, OnDestroy {
product : Product;
subscription : Subscription;
constructor(private basketdataService: BasketdataService) {
this.subscription = this.basketdataService.onGetBasket().subscribe(product=> { this.product = product; });
}
ngOnInit() {
}
ngOnDestroy() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe();
}
}
It's not "message", but "product" in this.subscription

Related

how get item by id from JSON API with Angular 2+?

i have my service with 2 methods getAll() and getById(), from which i receive the data:
job.service.ts
export class JobService {
constructor(private http: HttpClient) {}
url: string =
'https://api.json-generator.com/templates/......../data?access_token=............';
getAll(): Observable<Jobs[]> {
return this.http.get<Jobs[]>(this.url)
}
getById(id: string): Observable<Jobs> {
const path = `${this.url}`;
return this.http.get<Jobs>(path)
}
}
with getAll() method i form a list of items, from where with routerLink and *queryParams * i need to go to the separate page of item, that i clicked
job-list-card.component.html
<a routerLink="/details" [queryParams]="{ id: jobs.id }">
<h2 class="card-info__title">
{{ jobs.title }}
</h2>
</a>
and here is my the separate page of item ts-file:
job-detail.component.ts
export class JobDetailComponent implements OnInit {
jobs: Jobs | undefined;
constructor(
private route: ActivatedRoute,
private location: Location,
private jobService: JobService
) {}
ngOnInit(): void {
this.getJob();
}
getJob(): void {
const id = String(this.route.snapshot.paramMap.get('id'));
this.jobService.getById(id)
.subscribe(jobs => this.jobs = jobs);
}
goBack(): void {
this.location.back();
}
}
but everything i get in my page is only template without any api data.
so, what's wrong with my service or job-detail?
how can i get one item from list of items, that i receive from remote JSON API?

How to use private properties in my html-file?

I am trying to show a list of Animals in my html page with their corresponding name and color.
My frontend gets the data from a spring backend that returns a list of Animals.
And I stumbled upon 2 questions that I have:
1)
I made the name and color properties private in the Animal class.
Code of the animal class:
interface AnimalJson {
name: string;
color: string;
}
export class Animal {
constructor(private name: string, private color: string) {}
static fromJSON(json: AnimalJson): Animal {
const a = new Animal(json.name, json.color);
return a;
}
}
code of my animal-component:
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs';
import { DataServiceService } from '../data-service.service';
import { Animal } from '../models/Animal';
#Component({
selector: 'app-animal',
templateUrl: './animal.component.html',
styleUrls: ['./animal.component.css'],
})
export class AnimalComponent implements OnInit {
public animals: Observable<Animal[]>;
constructor(private dataService: DataServiceService) {
this.animals = new Observable<Animal[]>();
}
ngOnInit(): void {
this.animals = this.dataService.getAnimals();
}
}
code of the service:
#Injectable({
providedIn: 'root',
})
export class DataServiceService {
constructor(private http: HttpClient) {}
getAnimals(): Observable<Animal[]> {
return this.http
.get<Animal[]>('http://localhost:8080/animals')
.pipe(map((animals: any[]): Animal[] => animals.map(Animal.fromJSON)));
}
}
code of the html-page:
<div *ngFor="let animal of animals | async">
<p>{{ animal.name }}</p>
</div>
Now when I try to get the animal.name, it gives an error that the name is private so I cant use it in my html page. How should I fix this? Should I just make it public? Or is there something I forget?
2)
Is this how you work with observables? Or am I using my observables in a wrong way?
Using the http get methode to get the observable and than call it in my animal-component and use async in my html-file to go over all the values in it?
If you use private then it should not be used in the html, am not sure why you are using a class for initializing the array. Just use a simple map statement.
If you are going to show it in the HTML then don't make the property private.
So the changes are.
interface Animal {
name: string;
color: string;
}
Service will be.
#Injectable({
providedIn: 'root',
})
export class DataServiceService {
constructor(private http: HttpClient) {}
getAnimals(): Observable<Animal[]> {
return this.http
.get<Animal[]>('http://localhost:8080/animals')
.pipe(map((animals: any[]): Animal[] => animals.map((item: Animal) => ({name: item.name, color: item.color}))));
}
}
Note: class can also be used as an interface, so when using animal you defined the properties as private, so you are unable to use in the HTML.

How to excute a function in x component from y component?

I have a component alphabatesComponent. It has a method postLetters(). I want to call wordsComponent listOfAlphabates() method whenever I call postLetters() in alphabatesComponent.
// Service
private _listners = new Subject<any>();
constructor(private httpClient: HttpClient) { }
listen(): Observable<any> {
debugger;
return this._listners.asObservable();
}
filter(filterBy: string) {
debugger;
this._listners.next(filterBy);
}
// alphabates component
export class alphabatesComponent {
postLetters(): void {
this.asynService.filter('Register click');
}
}
// this is my words component
constructor() {
this.asyncService.listen().subscribe((m: any) => {
this.listOfAlphabates();
}
}
listOfAlphabates() {}
}
for state managment use rxJs:
in alphabates component:
filter () {
this.asynService.onFilterChanged.next('Registerclick');
}
in asynService:
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
public onFilterChanged: BehaviorSubject<any> = new
BehaviorSubject('');
in word component:
import {Subscription} from 'rxjs/Subscription';
private onFilterChanged: Subscription;
constructor(public asynService: AsynService,) {
this.onFilterChanged = this.asynService.onFilterChanged
.subscribe((filterBy) => {
this.listOfAlphabates(filterBy);
});
}
public ngOnDestroy(): void {
// note: Do not forget to unsubscribe!
this.onFilterChanged.unsubscribe();
}
listOfAlphabates(text) {
console.log(text);
}

Angular (4): pass a class object from child to parent component

I can pass a class object like Person into a child component from parent component without any problems. But I would like to also manipulate that object in child component and pass it back to parent component.
This is the child component class:
export class ActionPanelComponent {
#Input('company') company: Company;
#Output() notify: EventEmitter = new EventEmitter();
constructor() {
}
public deleteCompany() {
console.log('display company');
console.log(this.company);
// FIXME: Implement Delete
this.company = new Company();
}
public onChange() {
this.notify.emit(this.company);
}
}
This is the html of this component (excerpt):
<div class="row" (change)="onChange()">
<div class="col-xs-2">
<button md-icon-button >
<md-icon>skip_previous</md-icon>
</button>
</div>
This is the parent component class (excerpt):
public onNotify(company: Company):void {
this.company = company;
}
And the parent component html (excerpt):
<action-panel [company]="company" (notify)="onNotify($event)"></action-panel>
I am doing something wrong because I cannot pass my company object inside the .emit and nothing works.
What is the correct way of achieving two way object binding between components?
Thanks in advance!
You were missing the type on the initialization of the EventEmitter.
You could use the Output binding to implement the two way object binding:
Child component (ts)
export class ActionPanelComponent {
#Input('company') company: Company;
#Output() companyChange: EventEmitter = new EventEmitter<Company>();
constructor() {
}
public deleteCompany() {
console.log('display company');
console.log(this.company);
// FIXME: Implement Delete
this.company = new Company();
}
public onChange() {
this.companyChange.emit(this.company);
}
}
Parent component (html)
<action-panel [(company)]="company"></action-panel>
So like this you don't need to declare an extra function onNotify. If you do need the onNotify function, use another name for the output binding:
export class ActionPanelComponent {
#Input('company') company: Company;
#Output() notify: EventEmitter = new EventEmitter<Company>();
constructor() {
}
public deleteCompany() {
console.log('display company');
console.log(this.company);
// FIXME: Implement Delete
this.company = new Company();
}
public onChange() {
this.notify.emit(this.company);
}
}
Change it like this to tell TS which Type the EventEmitter should emit:
export class ActionPanelComponent {
#Input('company') company: Company;
#Output() notify = new EventEmitter<Company>(); //<---- On this line!
constructor() {
}
public deleteCompany() {
console.log('display company');
console.log(this.company);
// FIXME: Implement Delete
this.company = new Company();
}
public onChange() {
this.notify.emit(this.company);
}
}
It is a workaround that worked for me, if it is helpful for anyone.
Your parent parent-component.ts would be like;
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'parent',
templateUrl:'./parent.component.html',
styleUrls: ['./parent.component.css']
})
export class Parent implements OnInit {
let parentInstance= this; //passing instance of the component to a variable
constructor() { }
parentMethod(var:<classtyepyourchoice>){
console.log(var);
}
ngOnInit() {
}
}
In you parent.component.html, you would have your child
<child [parent]="parentInstance" ></child>
This object will be available in the child component
Now, in your child component you will receive this like
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'child',
templateUrl:'./child.component.html',
styleUrls: ['./child.component.css']
})
export class Child implements OnInit {
#Input('parent') parent;
constructor() { }
childMethod(yourClassObject){
this.parent.parentMethod(yourClassObject);
}
ngOnInit() {
}
}
Thus, you can pass classobject from your child, like this, it worked for me.

Getting data from Web API in Angular 2

Thanks to tutorial on Angular 2 page called "Tour of Heroes", I managed to create a simple Angular 2 application. Then using Enitity Framework I decided to create a database. And fill the list of heroes from it (not from the file). I created Web Api Controller and added simple get method.
Then in hero.service.ts I call this method in order to get list of heroes. When I lunch my app it shows the list of heroes but without any values (name and id are blank). When I debug my application in the browser I can see this.heroes object in heroes.component.ts contains right data. So what is going on? Why aren't name and id showing?
hero.service.ts:
import {Injectable} from 'angular2/core';
import {HEROES} from './mock-heroes';
import {Hero} from './hero';
import {Http, Response} from 'angular2/http';
import 'rxjs/Rx';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class HeroService {
public values: any;
constructor(public _http: Http) { }
private _heroesUrl = 'http://localhost:61553/api/values'; // URL to web api
getHeroes() {
return this._http.get(this._heroesUrl)
.map(res => <Hero[]>res.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
heroes.component.ts:
import {Component, OnInit} from 'angular2/core';
import {Router} from 'angular2/router';
import {Hero} from './hero';
import {HeroDetailComponent} from './hero-detail.component';
import {HeroService} from './hero.service';
#Component({
selector: 'my-heroes',
templateUrl: 'templates/heroes.component.html',
styleUrls: ['styles/heroes-component.css'],
directives: [HeroDetailComponent]
})
export class HeroesComponent implements OnInit {
constructor(private _heroservice: HeroService, private _router: Router) { }
errorMessage: string;
public heroes: Hero[];
selectedHero: Hero;
ngOnInit() {
this.getHeroes();
}
onSelect(hero: Hero)
{
this.selectedHero = hero;
}
getHeroes() {
this._heroservice.getHeroes()
.subscribe(
value => this.heroes = value,
error => this.errorMessage = <any>error);
}
gotoDetail() {
this._router.navigate(['HeroDetail', { id: this.selectedHero.Id }]);
}
}
heroes.component.html:
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="#hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)">
<span class="badge">{{hero.Id}}</span> {{hero.Name}}
</li>
</ul>
<div *ngIf="selectedHero">
<h2>
{{selectedHero.Name | uppercase}} is my hero
</h2>
<button (click)="gotoDetail()">View Details</button>
</div>
hero.ts:
export class Hero {
Id: number;
Name: string;
}
Web API Controller:
using Microsoft.AspNet.Mvc;
using System.Collections.Generic;
using TestApplicationDataAccess;
using TestApplicationDataAccess.Entities;
namespace WebApplication2.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly TestAppDbContext _dbContext;
public ValuesController(TestAppDbContext dbContext)
{
_dbContext = dbContext;
}
// GET: api/values
[HttpGet]
public IEnumerable<Hero> Get()
{
return _dbContext.Heroes;
}
}
}
Hero Entity:
namespace TestApplicationDataAccess.Entities
{
public class Hero
{
public int Id { get; set; }
public string Name { get; set; }
}
}
JSON retrieved from WEB API Controller:
[{"Id":1,"Name":"Superman"}]
getHeroes() {
this._heroservice.getHeroes()
.subscribe(res=>{
this.heroes=res;
console.log(this.heroes); // make sure you get data here.
},
(err)=>console.log(err),
()=>console.log("Done")
);
}
Try this :public heroes: Hero[] = [];
In my case the issue was related to the visual studio 2015 bug. There was nothing wrong with the code itself. Sometimes changes made in vs were not refreshed in the browser. Updating vs to the latest version helped.