Sending whole object from one component to another in angular 2 - javascript

I have a problem. I don't know how to send object from one component to another.
In first component cinema.component.html I have following function call:
<a title="Reserve" (click)="openReservationPage(repertoire)">Reserve</a>
In cinema.component.ts file, for that .html I have something like:
openReservationPage(repertoire: UpcomingRepertoire) {
this.router.navigate(['/reserve', {repertoire: JSON.stringify(repertoire)}]);
}
My app.routes.ts file contains appropriate routing:
{ path: 'reserve', component: ReserveFormComponent }
How can I use this repertoire object in another page reserve-form.component.ts and reserve-form.component.html ?

As an answer for the question in the title, i would said create a service to pass data between components.
Since its a router implementation you can pass the repertoire as a route parameter.
Follow these steps:
1)Modify the route in app.routes.ts to take a param
{ path: 'reserve/:repertoire', component: ReserveFormComponent }
2)In cinema.component.ts pass the repertoire as param
this.router.navigate(['/reserve',JSON.stringify(repertoire)]‌​);
3)Extract the param in reserve-form.component.ts
First of all you need to import
import {ActivatedRoute } from "#angular/router";
Technique 1
repertoire:any;
constructor(private activatedRoute: ActivatedRoute) {
this.repertoire = JSON.parse(activatedRoute.snapshot.params["repertoire"]);
}
Technique 2
import { Subscription } from "rxjs/Rx";
private subscription: Subscription;
repertoire:any;
constructor(private activatedRoute: ActivatedRoute) {
this.subscription = activatedRoute.params.subscribe(
(param: any) => this.repertoire = JSON.parse(param['repertoire'])
);
}
ngOnDestroy() { // here we unsubscribe to the observable
this.subscription.unsubscribe();
}
Further Explanation :
Technique 1 is adopted when you are sure that the param will be passed every time you navigate to the component.
Technique 2 is a subscription to the observable once there a param published but don't forget to unsubscribe in the ngOnDestroy() component's life cycle method to prevent memory leak.
It is more preferable because some times there a scenario that a param is passed to a component after it was created where the snapshot method wouldn't capture and it more flexible with different scenario than the basic one in technique 1.

The link below explains how you can do this. I've recently used this to create a messaging service. The example below, shows the code for a simple messaging service. It allows you to pass a number between components, just change the to I guess. You can also write out to local storage, but It seems services are more popular. Once you've got your head around them, they're easy to re-use.
Hope this helps
Sharing Data Between Angular Components - Four Methods
Message Service (PmMessageService)
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class PmMessageService
{
private pillMenuIndexBS = new BehaviorSubject <number> (null);
pillMenuIndex = this.pillMenuIndexBS.asObservable();
constructor() {}
setPillMenuIndex(index : number)
{
this.pillMenuIndexBS.next(index);
}
}
Component consuming message service, setting a value
import { Component, OnInit } from '#angular/core';
import { PmMessageService } from '../pm-message-service/pm-message.service'
import { BrowserModule } from '#angular/platform-browser';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
#Component({
selector: 'app-pm-configure',
templateUrl: './pm-configure.component.html',
styleUrls: ['./pm-configure.component.css']
})
export class PmConfigureComponent implements OnInit
{
constructor (private messageService : PmMessageService) {}
ngOnInit()
{
this.messageService.setPillMenuIndex(1);
}
}
Component consuming and subscribing.
import { Component, OnInit } from '#angular/core';
import { PmMessageService } from '../pm-message-service/pm-message.service'
import { BrowserModule } from '#angular/platform-browser';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
#Component({
selector: 'pm-bs-navbar',
templateUrl: './pm-bs-navbar.component.html',
styleUrls: ['./pm-bs-navbar.component.css']
})
export class PmBsNavbarComponent implements OnInit
{
tabActiveNumber;
constructor (private messageService : PmMessageService) {}
ngOnInit()
{
this.messageService.pillMenuIndex.subscribe(index => this.tabActiveNumber = index)
}
}

Related

how to pass data from file outside angular library to files inside of angular library

Below are the files of a library named posts-lib which makes http call inside posts.services.ts file and receives a list of posts and display them onto screen. It also consists a component named title.component.ts which is dependent on posts.services.ts and is responsible for displaying content on screen.
All of this works fine, but incase I want to move posts.service.ts folder out of the library and put it inside the app then how can I transfer the data from file which is outside of the library to the file title.component.ts which is dependent on it.
title.component.html
<h1>Testing titles api call</h1>
<ul>
<li *ngFor="let item of data">{{item.title}}</li>
</ul>
title.component.ts
import { Component, OnInit } from '#angular/core';
import { PostsService } from '../posts.service';
#Component({
selector: 'lib-tilte',
templateUrl: './tilte.component.html',
styleUrls: ['./tilte.component.css']
})
export class TilteComponent implements OnInit {
data: any;
constructor(private postData: PostsService) { }
ngOnInit() {
this.postData.getPosts().subscribe((result) => {
console.warn("reult",result);
this.data = result;
})
}
}
posts-lib.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'lib-posts-lib',
template: `
<p>
posts-lib works!
</p>
`,
styles: [
]
})
export class PostsLibComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
posts-lib.module.ts
import { NgModule } from '#angular/core';
import { PostsLibComponent } from './posts-lib.component';
import { TilteComponent } from './tilte/tilte.component';
import { HttpClientModule } from "#angular/common/http";
import { CommonModule } from '#angular/common'
#NgModule({
declarations: [
PostsLibComponent,
TilteComponent
],
imports: [
HttpClientModule,
CommonModule
],
exports: [
PostsLibComponent,
TilteComponent
]
})
export class PostsLibModule { }
posts.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from "#angular/common/http";
#Injectable({
providedIn: 'root'
})
export class PostsService {
url = "https://jsonplaceholder.typicode.com/posts";
constructor(private http: HttpClient) { }
getPosts() {
return this.http.get(this.url);
}
}
public-api.ts
export * from './lib/tilte/tilte.component';
export * from './lib/posts-lib.service';
export * from './lib/posts-lib.component';
export * from './lib/posts-lib.module';
export * from './lib/posts.service';
Ignoring all the issues the commenters are making - all valid - it sounds like you want to just remove the dependency on the service.
Or not, actually.
Yay, options!
Remove usage of the service
Just turn the component around from getting its own data, to being given its data. I.e. #Input.
Still with #Input, but instead, input the service itself rather than the values.
So either:
#Input() public data: any;
or
#Input() public set service(value: PostsService) {
this.postsService = value;
this.getData();
}
private getData(): void {
this.postsService.getPosts().subscribe(...);
}
Either way if you're moving the service out and no longer expecting the service and component to work as a functional pair within a system, you have to extract the component and feed it information instead with #Inputs.
Whether that's just feeding it the data from [a wrapper] service, or feeding it the service itself from wherever it now lives, you still need to give it to it.

Sending calculated data from one component to another without Services

I want to send the value from one component to another, they are not related so all solutions are saying that I must use shared service to do that. But these services are using templates (if I'm right). Is there a way to do this sharing without services?
I want to send the BMI value from homepage.component.ts to result.component.ts.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight
console.log(this.bmi); //this is the calculated value to send
}
}
result.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
constructor() { }
//I want to get the bmi here
ngOnInit(): void {
}
}
There are Two ways to communicate between unrelated components in angular:
1 - Through services, you have to understand where to inject it, in your case I think it should be injected in root, so try this with your service ( follow this tutorial to implement your service, just add my code instead of theirs )
#Injectable({
providedIn: 'root',
})
2 - Through a store ( a lot of boilerplate coding, to use if you have complexe states to keep synchronized through the whole app, by the way the store is basically a service )
If your components are not related then you can create a shared service between them. Then, you need to use dependency injection to communicate between these components. So, there is a great Angular tutorial which describes how to do it.
The service code would look like this:
#Injectable()
export class FooService {
constructor( ) { }
private yourData;
setData(data){
this.yourData = data;
}
getData(){
let temp = this.yourData;
this.clearData();
return temp;
}
}
and sender component:
import { Router } from '#angular/router';
import { FooService} from './services/foo.service';
export class SenderComponent implements OnInit {
constructor(
private fooService: FooService,
private router:Router) {}
somefunction(data){
this.fooService.setData(data);
this.router.navigateByUrl('/reciever');//as per router
}
}
and subscriber:
import { Router } from '#angular/router';
import { TransfereService } from './services/transfer.service';
export class RecieverComponent implements OnInit {
data;
constructor(
private fooService: FooService){
}
ngOnInit() {
data = this.transfereService.getData();
console.log(`data: `, data)
}
}
Solution: To pass the data from one component to another we can store it in a session storage or a local storage and then access it in other components from that storage. Here I have provided a sample code using local storage for your reference.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
data:string='';
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight;
this.data=localStorage.setItem('bmi',this.bmi);
console.log(this.bmi); //this is the calculated value to send
}
}
resultcomponent.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
data:any;
constructor() { this.data=localstorage.getItem('bmi')}
//Access the bmi using the data variable here
ngOnInit(): void {
}
}

Confusing behavior of a BehaviorSubject in my Angular App

I recently ran into a problem and can't really figure out what's wrong with my code at this point, hopefully someone of you can help me.
All I am trying to do is changing the value of my BehaviorSubject with a function but it isn't working out.
chat.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class ChatService {
chatId = new BehaviorSubject<number>(0);
constructor() {
this.chatId.next(1);
}
changeChatId(chatId: number) {
console.log(chatId);
this.chatId.next(chatId);
}
}
So the subscribers get the default as well as the changed chatId from the constructor. But as soon as I try to change it with the changeChatId function nothing happens at all. The right id's get passed into the function I already debugged that but the line this.chatId.next(chatId) doesn't seem to do anything.
ADD
These are the other components the service is currently used in.
chat-message-list
import { Component, OnInit, Input} from '#angular/core';
import { ChatService } from "../../../shared/services/chat.service";
#Component({
selector: 'app-chat-message-list',
templateUrl: './chat-message-list.component.html',
styleUrls: ['./chat-message-list.component.css'],
providers: [ChatService]
})
export class ChatMessageListComponent implements OnInit {
chatId: number;
constructor(private chat: ChatService) { }
ngOnInit() {
this.chat.chatId.subscribe(
chatId => this.updateMessageList(chatId)
);
}
}
chat-item
import { Component, OnInit, Input} from '#angular/core';
import { User } from '../../../shared/models/user.model';
import { ChatService } from '../../../shared/services/chat.service';
#Component({
selector: 'app-chat-list-item',
templateUrl: './chat-list-item.component.html',
styleUrls: ['./chat-list-item.component.css'],
providers: [ChatService]
})
export class ChatListItemComponent implements OnInit {
#Input()
user: User;
constructor(private chat: ChatService) { }
ngOnInit() {
}
onChatItemSelected(){
this.chat.changeChatId(this.user.id);
}
}
You need to make your ChatService a singleton (shared) service. Add it to the providers of your ngModule. This allows all the components that use the ChatService to share the same service instance.
#NgModule({
providers: [ChatService]
})
And remove it from your components providers. When you are adding it to your components providers, that component gets its own instance of ChatService which can not be used by other components.

Angular 4 pass data between 2 not related components

I have a questions about passing data in Angular.
First, I don't have a structure as <parent><child [data]=parent.data></child></parent>
My structure is
<container>
<navbar>
<summary></summary>
<child-summary><child-summary>
</navbar>
<content></content>
</container>
So, in <summary /> I have a select that do send value to <child-summary /> and <content />.
OnSelect method is well fired with (change) inside <summary /> component.
So, I tried with #Input, #Output and #EventEmitter directives, but I don't see how retrieve the event as #Input of the component, unless to go on parent/child pattern. All examples I've founded has a relation between component.
EDIT : Example with BehaviorSubject not working (all connected service to API works well, only observable is fired at start but not when select has value changed)
shared service = company.service.ts (used to retrieve company data)
import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class SrvCompany {
private accountsNumber = new BehaviorSubject<string[]>([]);
currentAccountsNumber = this.accountsNumber.asObservable();
changeMessage(accountsNumber: string[]) {
this.accountsNumber.next(accountsNumber);
}
private _companyUrl = 'api/tiers/';
constructor(private http: Http) { }
getSociete(): Promise<Response> {
let url = this._companyUrl;
return this.http.get(url).toPromise();
}
}
invoice.component.ts (the "child")
import { Component, OnInit, Input } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { SrvInvoice } from './invoice.service';
import { SrvCompany } from '../company/company.service';
#Component({
selector: 'invoice',
templateUrl: 'tsScripts/invoice/invoice.html',
providers: [SrvInvoice, SrvCompany]
})
export class InvoiceComponent implements OnInit {
invoice: any;
constructor(private srvInvoice: SrvInvoice, private srvCompany: SrvCompany)
{
}
ngOnInit(): void {
//this.getInvoice("F001");
// Invoice data is linked to accounts number from company.
this.srvCompany.currentAccountsNumber.subscribe(accountsNumber => {
console.log(accountsNumber);
if (accountsNumber.length > 0) {
this.srvInvoice.getInvoice(accountsNumber).then(data => this.invoice = data.json());
}
});
}
//getInvoice(id: any) {
// this.srvInvoice.getInvoice(id).then(data => this.invoice = data.json());
//}
}
company.component.ts (the trigerring "parent")
import { Component, Inject, OnInit, Input } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { SrvCompany } from './company.service';
#Component({
selector: 'company',
templateUrl: 'tsScripts/company/company.html',
providers: [SrvCompany]
})
export class CompanyComponent implements OnInit {
societes: any[];
soc: Response[]; // debug purpose
selectedSociete: any;
ville: any;
ref: any;
cp: any;
accountNumber: any[];
constructor(private srvSociete: SrvCompany)
{
}
ngOnInit(): void {
this.getSocietes();
}
getSocietes(): void {
this.srvSociete.getSociete()
.then(data => this.societes = data.json())
.then(data => this.selectItem(this.societes[0].Id));
}
selectItem(value: any) {
this.selectedSociete = this.societes.filter((item: any) => item.Id === value)[0];
this.cp = this.selectedSociete.CodePostal;
this.ville = this.selectedSociete.Ville;
this.ref = this.selectedSociete.Id;
this.accountNumber = this.selectedSociete.Accounts;
console.log(this.accountNumber);
this.srvSociete.changeMessage(this.accountNumber);
}
}
This is a case where you want to use a shared service, as your components are structured as siblings and grandchildren. Here's an example from a video I created a video about sharing data between components that solves this exact problem.
Start by creating a BehaviorSubject in the service
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
Then inject this service into each component and subscribe to the observable.
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-parent',
template: `
{{message}}
`,
styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
You can change the value from either component and the value will be updated, even if you don't have the parent/child relationship.
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-sibling',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Sibling")
}
}
if component are not related than you need use Service
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
There are two solutions for this.
This can be done through shared service by using observable's.
You can use ngrx/store for this. This is similar to Redux arch. You will be getting data from state.
Here is the simplest example of sharing data between two independent components, using event emitter and service
https://stackoverflow.com/a/44858648/8300620
When you mention non related components, I'm gonna assume that they don't have any parent component. If assumption isn't correct, feel free to read another of my answers where both cases are addressed.
So, as there's no common parent, we can use an injectable service. In this case, simply inject the service in the components and subscribe to its events.
(Just like the next image shows - taken from here - except that we'll inject the service in two Components)
The documentation explains it quite well how to Create and register an injectable service.

Angular2 - Make component wait until the service is done fetching data, then render it

I'm having trouble implementing a service that loads the data (gyms array) once, then allows all other components to use it, without making other HTTP requests.
My application works fine if the user started at the title page and loaded all the data, but when I go to a specific detail page (.../gym/1) and reload the page, the object isn't in the service array yet. How can I make the component that tries to access the service array wait until the data is loaded? More specifically, how can I delay the call of gymService.getGym(1) in the GymComponent until the getAllGymsFromBackEnd() function is done populating the array?
I've read about resolvers but my tinkering led me nowhere.
Any help would be appreciated.
This is the code I was working on:
Service:
import {Injectable} from "#angular/core";
import {Gym} from "../objects/gym";
import {BaseService} from "./base.service";
import {Http, Response} from "#angular/http";
import {HttpConstants} from "../utility/http.constants";
#Injectable()
export class GymService extends BaseService {
private gyms: Gym[] = [];
constructor(protected http: Http, protected httpConstants: HttpConstants) {
super(http, httpConstants);
this.getAllGymsFromBackEnd();
}
getAllGymsFromBackEnd() {
return super.get(this.httpConstants.gymsUrl).subscribe(
(data: Response) => {
for (let gymObject of data['gyms']) {
this.gyms.push(<Gym>gymObject);
}
}
);
}
getGyms() {
return this.gyms;
}
getGym(id: number) {
return this.gyms.find(
gym => gym.id === id
)
}
}
Component:
import {Component, OnDestroy, AfterViewInit, OnInit} from "#angular/core";
import {ActivatedRoute} from "#angular/router";
import {Subscription} from "rxjs";
import {Gym} from "../../objects/gym";
import {GymService} from "../../services/gym.service";
declare var $:any;
#Component({
selector: 'app-gym',
templateUrl: './gym.component.html',
styleUrls: ['./gym.component.css']
})
export class GymComponent implements OnInit, OnDestroy, AfterViewInit {
private subscription: Subscription;
private gym: Gym;
constructor(private activatedRoute: ActivatedRoute,
private gymService: GymService
) {}
ngOnInit(): void {
this.subscription = this.activatedRoute.params.subscribe(
(param: any) => {
this.gym = this.gymService.getGym(parseInt(param['id']));
}
);
}
ngAfterViewInit(): void {
$( document ).ready(function() {
$('.carousel').carousel();
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
You can use Resolver as well. Check it here https://angular.io/docs/ts/latest/api/router/index/Resolve-interface.html or use Observable. So the private gym: Gym; will become private gym$:Observable<Gym>;, and in your template, use async pipe to get the data.

Categories