Angular - reusing observable within a component? - javascript

So, this is my use case: I get an observable from rest API, then by using async within the template I foreach the data. On click, I get the ID of particular user and then fill-in the form with the data of that user. To achieve the latter, I reuse the existing observable and filter the data, then subscribe to it. I was wondering if my approach is correct, since I believe that app is too slow when I click "edit" for the form to get filled, so I'm guessing here that I create too many subscriptions or whatnot?
Also, for the sake of argument, I created two observables (user and users) and one (user) gets a "reference" to another (users), then it is displayed in the template with async, while I also subscribe to it and set it to a "regular" variable. Is this an issue? To display an observable with async and also subscribing to it?
Here's the code, since my question might be a bit confusing:
//our root app component
import {Component, NgModule, VERSION, OnInit, OnDestroy} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
import { HttpClientModule } from '#angular/common/http';
import { ReactiveFormsModule } from '#angular/forms';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/find';
import 'rxjs/add/operator/takeUntil';
import { FormArray, FormBuilder, FormGroup, Validators } from '#angular/forms';
import { UserService } from './service';
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<div>
{{user | async | json}}
{{selected | json}}
</div>
<div *ngFor="let u of users | async">
{{u.first_name}} {{u.last_name}}
<br />
<img [src]="u.avatar" />
<br />
<br />
<a (click)="edit(u.id)">Edit</a>
<br />
<br />
</div>
<div>
<form [formGroup]="userForm" (ngSubmit)="onSubmit()" novalidate>
<input type="text" formControlName="first_name" />
<input type="text" formControlName="last_name" />
</form>
<p>userForm value: {{ userForm.value | json}}</p>
<p>validity: {{ userForm.valid }}</p>
</div>
</div>
`,
styles: ['a { cursor: pointer; }']
})
export class App implements OnInit, OnDestroy {
users: Observable<any>;
user: Observable<any>;
destroy$: Subject<boolean> = new Subject<boolean>();
name:string;
userForm: FormGroup;
constructor(private fb: FormBuilder, private service: UserService) {
this.name = `Angular! v${VERSION.full}`;
this.createForm();
}
createForm() {
this.userForm = this.fb.group({
first_name: ['', {updateOn: 'blur', validators: [Validators.required, Validators.minLength(2), Validators.maxLength(10)]}],
last_name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]]
})
}
edit(id) {
this.user = this.users.map(x => x.find(x => x.id === +id));
this.user.takeUntil(this.destroy$).subscribe(u => {
console.log(u);
this.selected = u;
this.userForm.patchValue({
first_name: u.first_name,
last_name: u.last_name
});
});
}
ngOnInit() {
console.clear();
console.log('hello world');
this.users = this.service.all();
}
ngOnDestroy() {
this.destroy$.next(true);
this.destroy$.unsubscribe();
}
}
#NgModule({
imports: [ BrowserModule, HttpClientModule, ReactiveFormsModule ],
providers: [UserService],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
and here's the link: http://plnkr.co/edit/F0dheIdgoNNw53hNVjZm?p=preview

Let's say you want to use observables for this task. Although there is more straightforward method in the answer from Leon but for sake of learning about Observables let's do it :-)
You are subscribing to user observable in the template using async pipe but also in edit method. The subscription in edit method is never unsubscribed so there is a memory leak.
Also you redefine this.user observable each time you click on user and letting async pipe in template to resubscribe. This results in another memory leak since async pipe has no clue about the overriden observable which should be unsubscribed.
This is not really how you should compose your observable streams. First you should define the observable data transformation.
When I look on your application you have two sources of data/events:
users from service
clicks on edit button
Users are just observable from a service. Clicks should be really Subject. Because on edit you should emit the userId which was clicked (observable) and react on this click by displaying user detail (observer behaviour).
editClick: Subject<number> = new Subject<number>;
So let's define how user observable should look like (all this code in ngOnInit):
this.users = this.service.all().shareReplay();
this.user = this.editClick
.switchMapTo(this.users, (userId, users) => ({ userId, users}))
.map(x => x.users.find(user => user.id === +x.userId))
.do((u) => {
console.log(u);
this.selected = u;
this.userForm.patchValue({
first_name: u.first_name,
last_name: u.last_name
});
});
Here I define user observable based on editClick - thats where I will get id from later. Using switchMapTo I combine users observable and id from editClick into object which I use later to filter out clicked user.
Now how to trigger editClick in onEdit method? Just emit userId on each click like this:
edit(id) {
this.editClick.next(id);
}
Notice how I define data stream in ngOnInit. Just once at beginning. No recreation or something. I have two observables to subscribe:
users to display users list
user to display detail - this one will emit each time I trigger editClick
Also there is only subscription in template therefore no memory leaks since async pipe will manage unsubscribe for you.

You can greatly simplify the edit method by passing the whole user instead of just the id.
<a (click)="edit(u)">Edit</a>
edit(user) {
this.selected = user;
this.userForm.patchValue({
first_name: user.first_name,
last_name: user.last_name
});
}
As for calling service.all() multiple times will cause multiple subscriptions and multiple calls to the backend.
If you want to call it multiple times use the .share() rxjs operator.
That will create an wrapper subject around the observable which will return the same value each time it is called.

Related

How do you query and display the authenticated user's order from the Firebase Realtime Database using AngularFire in an Angular app?

This is the function I am using to insert orders into the database, which works fine.
async createPackage(){
const itemsRef = this.afDatabase.database.ref(`delivery orders/${this.uid}`);
const userId =
itemsRef.push({packageName: this.packageName, packageSize: this.packageSize, packageDescription: this.packageDescription, packageFrom: this.packageFrom, packageTo: this.packageTo, deliveryDate: this.deliveryDate, receiverNumber: this.receiverNumber, paymentOption: this.paymentOption, UID: this.uid})
this.packageName = '';
this.packageDescription = '';
this.packageFrom = '';
this.packageTo = '';
this.deliveryDate = '';
this.paymentOption = '';
this.receiverNumber = '';
this.packageSize = '';
this.showAlert('Your package delivery order has been successfully taken.', 'You will be contacted by one of our Administrators soon.');
}
here is a screenshot of how it's been structured in the database, the parent node is the user uid for each user, while the child node is the id for each order by each user.
the problem I am having is how to query and display each user's order separately on their dashboard differently on the front end.
For context, the complete files for this answer can be found here: https://gist.github.com/nclarx/ef581b0e1a95a2d43531411fe91a9814
To Query the User's Orders
To query the user's data you need to use a function similar to the following:
getCurrentOrder(): Observable<Order[] | never> {
return this.afAuth.authState // authState is an observable
.pipe( // use pipe
switchMap((user) => { // switchMap gets authState and then lets you return a different observable
// The following returns an observable call to the real-time database:
return user ? this.afDatabase.list<Order>(`delivery-orders/${user.uid}`).valueChanges() // if the user is authenticated an observable with the Orders is returned
: EMPTY; // if the user is not authenticated an empty observable is returned
})
);
}
This isn't an ideal way to do authentication in a larger application. I suggest you look at this video on Authentication with AngularFire https://www.youtube.com/watch?v=qP5zw7fjQgo and https://fireship.io/lessons/angularfire-google-oauth/ to create an AuthService that can be used across your application.
Displaying the Orders
To display that information in an Angular component using Observables from AngularFire I suggest the following structure/pattern:
OrderService - contains methods for accessing the database (generate the service with the Angular CLI)
AppComponent - has OrderService injected and calls getCurrentUserOrder() when the component initialises ngOnInit()
Template: app.component.html - the template which uses the async pipe in a *ngFor directive to subscribe/unsubscribe to the observable automatically.
Handing an observable to the template and using the async pipe to subscribe to it is good practice because it means that you do not need to subscribe and unsubscribe from an observable manually.
The files can be found in their entirety here: https://gist.github.com/nclarx/ef581b0e1a95a2d43531411fe91a9814
See the comments in the code for important points about how this works.
The Service: OrderService.ts
import {Injectable} from '#angular/core';
import {EMPTY, Observable} from 'rxjs';
import {AngularFireAuth} from '#angular/fire/auth';
import {switchMap} from 'rxjs/operators';
import {AngularFireDatabase} from '#angular/fire/database';
export interface Order {
// write interfaces for all of your objects and use them
// when defining variables and function return types.
packageName: string;
packageSize: number;
packageDescription: string;
packageFrom: string;
packageTo: string;
deliveryDate: Date;
receiverNumber: number;
paymentOption: string;
UID: string;
}
export class OrderService {
constructor(private afAuth: AngularFireAuth, private afDatabase: AngularFireDatabase) {
}
getCurrentOrder(): Observable<Order[] | never> { // note the use of the interface: Order[], which means returning an array of Orders
return this.afAuth.authState // authState is an observable
.pipe( // use pipe
switchMap((user) => { // switchMap gets authState and then lets you return a different observable
// The following returns an observable call to the real-time database:
return user ? this.afDatabase.list<Order>(`delivery-orders/${user.uid}`).valueChanges() // if the user is authenticated an observable with the Orders is returned
: EMPTY; // if the user is not authenticated an empty observable is returned
// NOTE: this observable is not called until it is subscribed to in the template using the `async pipe`, see
// `app.component.html` where it has `*ngFor="let order of orders$ | async"` <== this is what kicks off the request to the database
})
);
}
}
The Component: app.component.ts
import {Component, OnInit} from '#angular/core';
import {Order, OrderService} from './order.service';
import {Observable} from 'rxjs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'ng-fire-so-qtn';
orders$: Observable<Order[]>; // property to hold the observable which will have your array of Orders
constructor(public orderService: OrderService) { // inject the OrderService into the component
}
ngOnInit() {
this.orders$ = this.orderService.getCurrentOrder();
// When this component is initialised it will set the `orders$` property to the `Observable<Order[]>` so it is available in the template
}
}
The Template: app.component.html
<section>
<ul *ngIf="orders$ | async"> <!-- The *ngIf will hide the whole list until the data has arrived-->
<li *ngFor="let order of orders$ | async"> <!-- The *ngFor will loop over and create list items for the orders once the data has arrived-->
{{order.packageName}}: {{order.packageDescription}}
</li>
</ul>
</section>
Thanks everyone, i finally was able to query each user's submission from the firebase realtime database with angular with this few lines of code.
getCurrentUserOrder() {
return this.afAuth.authState.subscribe(user => {
if(user) {
this.userId = user.uid;
console.log(user.uid)
console.log(user.email)
}
this.afDatabase.list(`delivery orders/${this.userId}`).valueChanges().subscribe(
data => {
console.log(data);
this.orders = data;
}
);
});
after importing the AngularFireDatabase and the AngularFireAuth inside the component.On the frontend,
<section *ngFor="let order of orders">
<ion-card>
<ion-item>
<ion-icon name="cube" slot="end"></ion-icon>
<ion-label>{{order.packageName}}</ion-label>
</ion-item>
<ion-card-content>
</ion-card-content>
</ion-card>
</section>
this solves the entire problems completely.

How to add custom unique validator for form values in angular reactive form?

I want to add a custom unique validator that will validate that all label fields values are unique.
(I) When I change the form values, the value of this.form changes after it is passed in CustomValidator.uniqueValidator(this.form). How to fix this?
(II) Is there any way of doing this without using any package?
Note: Forms have default values on load. Here is the screenshot.
this.form = this.fb.group({
fields: this.fb.array([])
});
private addFields(fieldControl?) {
return this.fb.group({
label: [
{value: fieldControl ? fieldControl.label : '', disabled: this.makeComponentReadOnly}, [
Validators.maxLength(30), CustomValidator.uniqueValidator(this.form)
]],
isRequired: [
{value: fieldControl ? fieldControl.isRequired : false, disabled: this.makeComponentReadOnly}],
type: [fieldControl ? fieldControl.type : 'text']
});
}
static uniqueValidator(form: any): ValidatorFn | null {
return (control: AbstractControl): ValidationErrors | null => {
console.log('control..: ', control);
const name = control.value;
if (form.value.fields.filter(v => v.label.toLowerCase() === control.value.toLowerCase()).length > 1) {
return {
notUnique: control.value
};
} else {
return null;
}
};
}
in real life, username or email properties are checked to be unique. This will be very long answer I hope you can follow along. I will show how to check uniqueness of username.
to check the database, you have to create a service to make a request. so this validator will be async validator and it will be written in class. this class will be communicate with the service via the dependency injection technique.
First thing you need to setup HttpClientModule. in app.module.ts
import { HttpClientModule } from '#angular/common/http';
#NgModule({
declarations: [AppComponent],
imports: [BrowserModule, YourOthersModule , HttpClientModule],
providers: [],
bootstrap: [AppComponent],
})
then create a service
ng g service Auth //named it Auth
in this auth.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private http: HttpClient) {}
userNameAvailable(username: string) {
// avoid type "any". check the response obj and put a clear type
return this.http.post<any>('https://api.angular.com/username', {
username:username,
});
}
}
now create a class ng g class UniqueUsername and in this class:
import { Injectable } from '#angular/core';
import { AsyncValidator, FormControl } from '#angular/forms';
import { map, catchError } from 'rxjs/operators';
import { of } from 'rxjs';
import { AuthService } from './auth.service';
// this class needs to use the dependency injection to reach the http client to make an api request
// we can only access to http client with dependecny injection system
// now we need to decorate this class with Injectable to access to AuthService
#Injectable({
providedIn: 'root',
})
export class UniqueUsername implements AsyncValidator {
constructor(private authService: AuthService) {}
//this will be used by the usernamae FormControl
//we use arrow function cause this function will be called by a
//different context, but we want it to have this class' context
//because this method needs to reach `this.authService`. in other context `this.authService` will be undefined.
// if this validator would be used by the FormGroup, you could use
"FormGroup" type.
//if you are not sure you can use type "control: AbstractControl"
//In this case you use it for a FormControl
validate = (control: FormControl) => {
const { value } = control;
return this.authService.userNameAvailable(value).pipe(
//errors skip the map(). if we return null, means we got 200 response code, our request will indicate that username is available
//catchError will catch the error
map(() => {
return null;
}),
catchError((err) => {
console.log(err);
//you have to console the error to see what the error object is. so u can
// set up your logic based on properties of the error object.
// i set as err.error.username as an example. your api server might return an error object with different properties.
if (err.error.username) {
//catchError has to return a new Observable and "of" is a shortcut
//if err.error.username exists, i will attach `{ nonUniqueUsername: true }` to the formControl's error object.
return of({ nonUniqueUsername: true });
}
return of({ noConnection: true });
})
);
};
}
So far we handled the service and async class validator, now we implement this on the form. I ll have only username field.
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { UniqueUsername } from '../validators/unique-username';
#Component({
selector: 'app-signup',
templateUrl: './signup.component.html',
styleUrls: ['./signup.component.css'],
})
export class SignupComponent implements OnInit {
authForm = new FormGroup(
{
// async validators are the third arg
username: new FormControl(
'',
[
Validators.required,
Validators.minLength(3),
Validators.maxLength(20),
Validators.pattern(/^[a-z0-9]+$/),
],
// async validators are gonna run after all sync validators
successfully completed running because async operations are
expensive.
this.uniqueUsername.validate
),
},
{ validators: [this.matchPassword.validate] }
);
constructor(
private uniqueUsername: UniqueUsername
) {}
//this is used inside the template file. you will see down below
showErrors() {
const { dirty, touched, errors } = this.control;
return dirty && touched && errors;
}
ngOnInit(): void {}
}
Final step is to show the error to the user: in the form component's template file:
<div class="field">
<input formControl="username" />
<!-- this is where you show the error to the client -->
<!-- showErrors() is a method inside the class -->
<div *ngIf="showErrors()" class="ui pointing red basic label">
<!-- authForm.get('username') you access to the "username" formControl -->
<p *ngIf="authForm.get('username').errors.required">Value is required</p>
<p *ngIf="authForm.get('username').errors.minlength">
Value must be longer
{{ authForm.get('username').errors.minlength.actualLength }} characters
</p>
<p *ngIf="authForm.get('username').errors.maxlength">
Value must be less than {{ authForm.get('username').errors.maxlength.requiredLength }}
</p>
<p *ngIf="authForm.get('username').errors.nonUniqueUsername">Username is taken</p>
<p *ngIf="authForm.get('username').errors.noConnection">Can't tell if username is taken</p>
</div>
</div>
You could create a validator directive that goes on the parent element (an ngModelGroup or the form itself):
import { Directive } from '#angular/core';
import { FormGroup, ValidationErrors, Validator, NG_VALIDATORS } from '#angular/forms';
#Directive({
selector: '[validate-uniqueness]',
providers: [{ provide: NG_VALIDATORS, useExisting: UniquenessValidator, multi: true }]
})
export class UniquenessValidator implements Validator {
validate(formGroup: FormGroup): ValidationErrors | null {
let firstControl = formGroup.controls['first']
let secondControl = formgroup.controls['second']
// If you need to reach outside current group use this syntax:
let thirdControl = (<FormGroup>formGroup.root).controls['third']
// Then validate whatever you want to validate
// To check if they are present and unique:
if ((firstControl && firstControl.value) &&
(secondControl && secondControl.value) &&
(thirdContreol && thirdControl.value) &&
(firstControl.value != secondControl.value) &&
(secondControl.value != thirdControl.value) &&
(thirdControl.value != firstControl.value)) {
return null;
}
return { validateUniqueness: false }
}
}
You can probably simplify that check, but I think you get the point.
I didn't test this code, but I recently did something similar with just 2 fields in this project if you want to take a look:
https://github.com/H3AR7B3A7/EarlyAngularProjects/blob/master/modelForms/src/app/advanced-form/validate-about-or-image.directive.ts
Needless to say, custom validators like this are fairly business specific and hard to make reusable in most cases. Change to the form might need change to the directive. There is other ways to do this, but this does work and it is a fairly simple option.

Angular 8. My view is not updated on model change

In my application I'm having a big trouble 'refreshing' the view after model for that view was updated. Mainly when API call is resolved and its response's data should be published on that view.
This my component management.component.ts ts file (I've removed code not important to this issue):
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
#Component({
selector: 'app-service',
templateUrl: './service.component.html',
styleUrls: ['./service.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ServiceComponent implements OnInit {
serviceForm: FormGroup;
notificationStatus: boolean;
constructor(
private http: HttpClient
) { }
ngOnInit() {
this.buildServiceForm();
// Example endpoint response: { active: false }
this.getNotificationInfo().subscribe((data: object) => {
this.notificationStatus = data['active'];
})
}
submit()
{
this.notificationStatus = !this.notificationStatus;
}
buildServiceForm()
{
this.serviceForm = this.formBuilder.group({
message_de: ['', { validators: [Validators.required] }],
message_en: ['', { validators: [Validators.required] }]
});
}
getNotificationInfo() {
return this.http.get(this.apiPath + 'service/notifications');
}
}
And this is the part in HTML that is responsible for displaying that model (button tag):
<form [formGroup]="serviceForm" (ngSubmit)="submit()">
<mat-form-field class="service__form__textarea">
<textarea matInput formControlName="message_de"></textarea>
</mat-form-field>
<mat-form-field class="service__form__textarea">
<textarea matInput formControlName="message_en"></textarea>
</mat-form-field>
<button>
<span *ngIf="notificationStatus == false"> Service Off </span>
<span *ngIf="notificationStatus == true"> Service On </span>
</button>
</form>
Whenever the button is clicked a form is submitted and the button's text should be updated (changed with use of ngIf) right away. But it only happens when I click randomly other objects on website.
I tried already with use of changeDetection: ChangeDetectionStrategy.OnPush but with no luck.
Here animation how it looks like in practice - after clicking the button its text changes only after clicking in textarea and not right after clicking the button:
gif animation of the behavior
Any one have any idea how can I refresh my view or what is the reason why my view behaves like that?
When using ChangeDetectionStrategy.OnPush the view will only update if object references change (see angular 2 change detection and ChangeDetectionStrategy.OnPush).
You may need to run the update to notificationStatus inside of zone using zone.run if that service is not already inside zone (the default Http client for example is already run inside zone automatically).
You also need to manually update the view using ChangeDetectorRef, or use the async pipe and observables which do this automatically.
You have two way to solve this:
Use CDR (quick & dirty)
Inject in your costructor ChangeDetectorRef
Call "cdr.markForCheck();" at the end of the submit method.
Transform your notificationStatus in a subject
In the class:
private notificationStatus$:Subject = new Subject(true);
In the submit method:
this.notificationStatus$:Subject.next(false);
In the html:
<span *ngIf="(notificationStatus$ | async ) == false"> Service Off </span>
PS: The $ in the variable name is a convention for obserable/subject

Listening to FormControl changes with ngOnChanges in the AppComponent?

I'm attempting to listen to changes on a reactive email form control like this:
import { Component, OnChanges } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnChanges {
form: FormGroup = new FormGroup({
email: new FormControl('',[ Validators.email ])
});
get emailInput() { return this.form.get('email'); }
ngOnChanges() {
this.form.get('email').valueChanges.subscribe(val => {
const formattedMessage = `Email is ${val}.`;
console.log(formattedMessage);
});
}
}
The form looks like this:
<form [formGroup]="form">
<input placeholder="Email" type="email" formControlName="email" >
</form>
When typing in the email field nothing gets logged. This is the Stackblitz. Thoughts?
This is the article the question implementation was based on.
Update
The accepted answer is to use the ngOnInitit lifecycle hook. I wanted if perhaps it should be ngAfterViewInit just to make sure the view is entirely initialized or will be form bindings always be complete in ngOnInit?
Didn't notice at first, but your ngOnChanges should not be where you are subscribing to the observable. ngOnChanges is for changes to input parameters to the current component (typically wrapped in []).
Setup your subscription to the observable in the ngOnInit like this and your code will work:
ngOnInit() {
this.emailSubscription = this.form.get('email').valueChanges.subscribe(val => {
const formattedMessage = `Email is ${val}.`;
console.log(formattedMessage);
});
}
Angular does not automatically unsubscribe so typically you'll want to save the value of the description, and then unsubscribe it in the ngOnDestroy:
ngOnDestroy() {
this.emailSubscription.unsubscribe();
}
Since you're writing this code in appComponent there's probably not an explicit need to do this outside it being generally good practice for every other component.
Edit: Updated stackblitz showing this working.
You're using onChanges wrong. OnChanges watches for changes performed on a child component so that the parent component can update information. You're doing this with a form, so nothing will send changes up to the component.
Since the input is an element on the component, you can do it with an (input) listener or a (keypress).

cannot display array sent from one component to other in angular2

SERVICE--
import {Injectable} from '#angular/core';
import {UserData} from '../user-data/user-data.component';
#Injectable()
export class UserDataService {
constructor(){}
userdata:UserData[];
getData(){
console.log('service',this.userdata);
return this.userdata;
}
setData(user:any){
this.userdata=user;
console.log(this.userdata);
}
}
USER-DATA-class ---
export class UserData {
firstname: string;
middlename: string;
lastname: string;
email: string;
contact: number;
}
Component1 --
import { Component,OnInit,OnDestroy } from '#angular/core';
import { UserData } from '../../user-data/user-data.component';
import { ViewEditUser } from '../../view-edit-user/view-edit-user.component';
import {UserDataService} from '../../user-data-service/user-data-service.service';
#Component({
selector: 'form-page',
templateUrl: `app/add-user-sidebar/user-form/form.component.html`,
providers:[UserDataService]
})
export class Form implements OnInit,OnDestroy {
userdetail:UserData;
constructor(private service:UserDataService){
}
addUser(first:string,middle:string,last:string,emailid:string,contactno:number){
this.userdetail=({firstname:first,middlename:middle,lastname:last,email:emailid,contact:contactno})
console.log(this.userdetail);
this.service.setData(this.userdetail);
}
ngOnInit(){
}
ngOnDestroy(){
}
}
Component2--
import { Component,Input, OnInit } from '#angular/core';
import { Form } from '../add-user-sidebar/user-form/form.component';
import {UserData} from '../user-data/user-data.component';
import { WelcomePage } from '../welcome-page/welcome-page.component';
import {UserDataService} from '../user-data-service/user-data-service.service';
#Component({
selector:'view-edit',
templateUrl: 'app/view-edit-user/view-edit-user.component.html',
providers: [UserDataService]
})
export class ViewEditUser implements OnInit {
arraydata:any;
constructor(private service:UserDataService){}
// arraydata:any;
printarray(){
console.log(this.arraydata);
}
ngOnInit()
{
this.arraydata=this.service.getData();
console.log("hhghdfghdf",this.arraydata);
}
}
I am new to angular2, I have two components in my module, one component is a form where user inputs data, that data is then sent to a service, when I console.log it then I can see the data in service. but when I try to access that array from the second component then I can't access the data what to do?
If you provide the service on each component, you can't use it for communication, because each component will get a different service instance.
If one component is a parent (ancestor) of the other component, only provide it on the parent component.
Otherwise provide it on a component that is a parent (anjestor) of both or provide it only in #NgModule() to make the service global.
You also need to be aware that it's possible that one component reads, before the other set the value, depending on where you set the value and in what order the components are created.
Using a BehaviorSubject usually avoids this pitfall, because this way it doesn't matter which component is created first or if one component tries to read, while the other hasn't set the value yet.
For shareing between to Angular instances see also How to share service between two modules - #NgModule in angular2?
You nee to use observables to pass data between components.
In your service create a Subject type variable and in the your first component do a .next to pass data to the service and in your 2nd component, subscribe to the service veriable and it will get you the data.
You are not getting the data because of the async behavior of JavaScript which will be handled by observables

Categories