Global object in angular2 - javascript

How can I create a global object in angular2. I am collecting data from one page to another means page by page(may be for 4-5 pages) using navParams in ionic2. but want to store it in global object & finally submit it. I have tried it using global provider but haven't got most of it & not getting any clue also. Please any suggestion.

As with my question ionic community suggest to use global provider. i did use global provider to hold my 7 step form data and final submit on last step.
i used with ionic 3.
provider code:
import { Injectable } from '#angular/core';
#Injectable()
export class NcConnectionProvider {
public statecode:any;
public districtcode:any;
constructor() {
//console.log('Hello NcConnectionProvider Provider');
}
}
Page.ts
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { NcConnectionProvider } from '../../providers/nc-connection/nc-connection';
#IonicPage()
#Component({
selector: 'page-nc-connection',
templateUrl: 'nc-connection.html',
})
export class NcConnectionPage {
public distbform:FormGroup;
public statedata:any;
public districtdata:any;
constructor(public navCtrl: NavController,
public navParams: NavParams,
public ncdatashare: NcConnectionProvider) {
}
locateDistb(form: NgForm){
this.ncdatashare.statecode = this.distbform.value.state;
this.ncdatashare.districtcode = this.distbform.value.district;
}
}
you can inject provider to multiple pages as you want and access your global variable/object. like in example value set to this.ncdatashare.statecode = this.distbform.value.state; now you can access this.ncdatashare.statecode to other page but provider must inject there.

Make sure to provide some sample code to elaborate the problem better. Moreover, following is the way you can follow to collect the data in single object.
Create a service with a variable and its getter setter. Inject this service in your component and set the data wherever needed and later use its get method to collect it back again.
#Injectable()
export class DataService{
private _data: any;
get data() {
return this._data;
}
set data(data: any) {
this._data = data;
}
}
and in side your component
#Component({
// configurations
})
export class LoginComponent {
constructor(public dataService: DataService) {
this.dataService.data = { userAge: 37 }
}
}
and where you need to submit this data, just use the getter to collect it back.
let finalData = this.dataService.data;
Not sure about your use case. But I hope it might help you.

Related

BehaviourSubject.getValue() returning default values

I am fairly new to angular. I have two components namely header and profile component. The header component handles the login functionality and maintains two information- the user details which is json object and a isLoggedIn which is a boolean that saves current state of login. The general layout of the profile page is-
<header-component>
<profile-component>
Now since the header component handles the login. I want to avoid writing the logic for getting userDetails and the isLoggedIn status again for profile component. So i decided writing a shared service called profile service so that i can upload userDetails and isLogged from header and access that info in the profile component. The input in the loginlogout method comes from the header component.
SharedService code -
import { Injectable } from '#angular/core';
import { HttpService } from './https.service';
import { Observable, BehaviorSubject, of as observableOf } from 'rxjs';
import * as _ from 'lodash';
import { HttpHeaders, HttpParams } from '#angular/common/http';
import { BaseService } from './base.service';
#Injectable()
export class ProfileServices{
constructor(){};
userDetailsBS = new BehaviorSubject<any>('original value');
userDetails= this.userDetailsBS.asObservable();
isLoggedIn:boolean;
loginlogout(userDetails:any , isLoggedIn:boolean){
this.userDetails=userDetails;
this.userDetailsBS.next(this.userDetails);
console.log("Value of user details set in profile service",this.userDetails); //debug
console.log(".getValue() method:",this.userDetailsBS.getValue()); //debug
this.isLoggedIn=isLoggedIn;
}
getUserDetails(){
return this.userDetailsBS.getValue();
}
}
Post login from the header-component.ts i call the loginlogout method in the profile service to set the values. I also tried to access the value passed to the shared Service using the getUserDetails which shows that the userDetails object is passed correctly to the shared service.
The issue arises when i try to access the data from the profile component-
export class ProfileT1Component implements OnInit {
userDetails:any;
constructor(
public profileService: ProfileServices){
this.profileService.userDetails.subscribe((result)=>{
console.log(result);
this.userDetails=result;
console.log("received user details in profile component constructor: ", this.userDetails);
})
}
}
the result still shows "original value" and not the updated value. Is this wrong approach altogether or am i handling the observables incorrectly. Help would be much appreciated.
You need to make a couple of changes in your service to make it work. Add providedIn: root and remove all declarations from other modules. Secondly, you do not need this.userDetailsBS.asObservable() and you can use the subscribe directly on userDetailsBS. Your code will look something like the following.
Service:
#Injectable({
providedIn: 'root'
})
export class ProfileServices {
constructor() {}
userDetailsBS = new BehaviorSubject<any>('original value');
isLoggedIn: boolean;
loginlogout(userDetails: any, isLoggedIn: boolean) {
this.userDetailsBS.next(userDetails);
this.isLoggedIn = isLoggedIn;
}
getUserDetails() {
return this.userDetailsBS.getValue();
}
}
Component:
export class ProfileT1Component implements OnInit {
userDetails: any;
constructor(public profileService: ProfileServices) {
this.profileService.userDetailsBS.subscribe((result) => {
console.log(result);
this.userDetails = result;
console.log('received user details in profile component constructor: ', this.userDetails);
});
}
}
the implementation seems to be OK
(except you should make the BehaviorSubject private and expose only the observable)
probably you have multiple instance of the service.
try to add :
#Injectable({
providedIn: 'root',
})
and remove the service declaration from all the modules provider array
https://angular.io/guide/singleton-services

Where to put FirebaseAuth.onAuthStateChanged call in Angular app?

What is the appropriate place for adding a call to initialize a global listener in Angular app?
Here is the code:
export class AuthService {
constructor(
private store: Store<fromAuth.State>,
private afAuth: AngularFireAuth
) {
this.afAuth.auth.onAuthStateChanged(payload => {
if (payload) {
const user: UserBeta = {
uid: payload.uid,
displayName: payload.displayName,
email: payload.email,
emailVerified: payload.emailVerified
};
this.store.dispatch(AuthActions.authenticated({ user }));
} else {
this.store.dispatch(AuthActions.notAuthenticated());
}
});
}
As you could see I've added it to the constructor of the AuthService but it doesn't seem right for me.
What I'm also concerning about is that the following code has two dependencies: Ngrx and AngularFireAuth.
In this case, would it be correct to move somewhere to the FirebaseModule (i.e. firebase.module.ts) and if yes, how is the call will look like?
You can add it inside ngOnInit(), from the docs:
A callback method that is invoked immediately after the default change detector has checked the directive's data-bound properties for the first time, and before any of the view or content children have been checked. It is invoked only once when the directive is instantiated.
Check here for more info:
https://angular.io/api/core/OnInit
Thank all of you for replies.
I've finally decided to introduce a new initialize() method inside the AuthService and call it inside the ngOnInit() method of the AppComponent.
auth.service.ts:
#Injectable({ providedIn: 'root' })
export class AuthService {
constructor(
private http: HttpClient,
private store: Store<fromAuth.State>,
private afAuth: AngularFireAuth
) { }
initialize() {
this.afAuth.auth.onAuthStateChanged(payload => {
if (payload) {
const user: UserBeta = {
uid: payload.uid,
displayName: payload.displayName,
email: payload.email,
emailVerified: payload.emailVerified
};
this.store.dispatch(AuthActions.authenticated({ user }));
} else {
this.store.dispatch(AuthActions.notAuthenticated());
}
});
}
}
app.component.ts:
export class AppComponent implements OnInit {
constructor(private authService: AuthService) { }
ngOnInit(): void {
this.authService.initialize();
}
}
Update: At my project I'm using ngrx for state management. Since AngularFireAuth also manages user information I've faced difficulty in managing the same state in multiple places, which increased the complexity, so the final solution became quite complicated. In the end, I've decided to stop using the onAuthStateChanged listener and start persisting the ngrx state locally.

How can I create an instance of a service (injectable) with Angular inside a class

import { Store } from '#ngxs/store';
export class Service {
constructor(private _store: Store) {}
}
export abstract class A {
constructor( private _service: Service ) { }
}
export class B extends A {
constructor( private _service: Service ) {
super(_service);
}
}
I'd like to avoid B (and other 10 classes like B) to declare and pass that service to A.
I was reading about using ReflectiveInjector, but apparently it works only when all providers got #Injectable() decorator, which is not provided in Store.
Any idea if and how is doable?
You can make an instance of a class if you also insert the dependency:
public store: Store = new Store()
public service: Service = new Service(this.store);

Angular4 - let multiple unrelated components notify each other of the problem of updating data, and whether there is a cleaner coding method?

I have encountered a project in progress, let multiple unrelated components notify each other of the update data, is there a cleaner coding method?
There are 3 components (more likely later) and a common-data component. They have no parent-child relationship with each other and only show on the same screen.
The desired effect is to press the button of any component, update the contents of common-data, and notify yourself and other components to fetch new messages from common-data.
At present, my approach is to use Rx's Observable and Subscription, but they must be imported in the component.ts and service.ts files of each component, and a lot of duplicate code appears, it is very messy, I don't know what is better. practice?
Thanks!
My code :
The sample name is test-a-comp (a.b.c and so on, the code is the same)
test-a-comp.html
<p>
{{ownMessage}}
</p>
<button (click)="sendChange()">update</button>
test-a-comp.component
import { Component, OnInit } from '#angular/core';
import { Subscription } from 'rxjs/Subscription';
import { CommonData } from '../common-data/common-data';
import { TestACompService } from './test-a-comp.service';
import { TestBCompService } from '../test-b-comp/test-b-comp.service';
import { TestCCompService } from '../test-c-comp/test-c-comp.service';
#Component({
selector: 'app-test-a-comp',
templateUrl: './test-a-comp.component.html',
styleUrls: ['./test-a-comp.component.css']
})
export class TestACompComponent implements OnInit {
subscription: Subscription;
ownMessage;
constructor(
private testAService: TestACompService,
private testBService: TestBCompService,
private testCService: TestCCompService,
) {
this.subscription = this.testAService.getMessage()
.subscribe((test) => {
CommonData.message = test;
});
this.subscription = this.testBService.getMessage()
.subscribe(() => {
this.ownMessage = CommonData.message;
});
this.subscription = this.testCService.getMessage()
.subscribe(() => {
this.ownMessage = CommonData.message;
});
}
ngOnInit() {
}
sendChange() {
this.testAService.sendMessage();
}
}
test-a-comp.service:
import { Injectable } from '#angular/core';
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';
#Injectable()
export class TestACompService {
subscription: Subscription;
private subject = new Subject<any>();
constructor() {
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
sendMessage(): void {
this.subject.next('update message from A');
}
}
As far as i understand & you've mentioned in the above, there is a button in one of the component (test-a-component.html). If you update the button, you need to send message to other components which are subscribed.
The Components which have no Parent-Child relationship can communicate via a service:
Create a single service file (In your case: test-a-comp.service)
Create a Subject on what data you need to communicate via this service:
export class testMessageService {
constructor() {}
// Observable string sources
private message = new Subject<string>();
//Observable string streams
testMessage$ = this.message.asObservable();
constructor() {}
// Method to send message when a button is clicked
sendMessage(message: string) {
this.message.next(message);
}
/* You don't need "getMessage()" method as you've already subscribed to
the observables. There subscribed Observable string streams are
injected in your components (As below point 3) to display / do other
operation on the message. */
}
In your other Components, where you want to receive messages, do the following:
export class TestComponent 1 {
myMessage1: string;
constructor(private TestMessageService: testMessageService) {}
TestMessageService.testMessage$.subscribe(message => {
this.myMessage1 = message;
});
}
export class TestComponent 2 {
myMessage2: string;
constructor(private TestMessageService: testMessageService) {}
TestMessageService.testMessage$.subscribe(message => {
this.myMessage2 = message;
});
}
export class TestComponent 3 {
myMessage3: string;
constructor(private TestMessageService: testMessageService) {}
TestMessageService.testMessage$.subscribe(message => {
this.myMessage3 = message;
});
}
For more information/guidance refer Component interaction via a common
service: https://angular.io/guide/component-interaction
Hope this helps!

Angular2 component doesn't detect routing parameter updates (Router 3.0)

I've got a small Plunk I'm using for playing around with the new Router 3.0 alpha currently available in Angular 2. It works well in general, but the issue is that once I click on a link that routes to the 'detail' component with a particular ID, it never changes when I click on a different link with a different ID. The component is never being reinstantiated, so it only ever shows what it was passed the very first time it is loaded.
Here's the component in question:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ContactsService } from './contacts.service';
#Component({
selector: 'contacts-detail',
template: `
<h2>{{contact.name}}</h2>
`
})
export class ContactsDetailComponent implements OnInit {
constructor(private contactsService: ContactsService, private route: ActivatedRoute) {
}
ngOnInit() {
this.contact = this.contactsService.getContact(this.route.snapshot.params.id);
console.log('Fetching user', this.route.snapshot.params.id);
}
}
Here is the Plunk demonstrating the problem. Click on one author name and then another to see it not change.
In your ContactsDetailComponent, change the OnInit to this:
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
let id = +params['id'];
this.contact = this.contactsService.getContact(id);
});
}
Worked for me in your Plunk.
There appear to be multiple lifeCycle hooks that could possibly be used for this. I managed to get the desired behavior using the DoCheck interface and implementing the associated ngDoCheck() method in the component class, as seen below.
import { Component, DoCheck } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ContactsService } from './contacts.service';
#Component({
selector: 'contacts-detail',
template: `
<h2>{{contact.name}}</h2>
`
})
export class ContactsDetailComponent implements AfterViewChecked, DoCheck {
constructor(private contactsService: ContactsService, private route: ActivatedRoute) {
}
ngDoCheck() {
this.contact = this.contactsService.getContact(this.route.snapshot.params.id);
}
}
Here's a plunk with the updated code.
I'm not convinced this is the best/correct lifecycle hook to use, though. Perhaps there is some sort of hook available from the Router that would serve this better.
Another way to do this:
ngOnInit() {
this.route.params.forEach((params: Params) => {
let id = +params['id'];
this.contact = this.contactsService.getContact(id);
});
}
Here retrieve the route params from an Observable. The advantage of using an Observable over Snapshot is to reuse the component without instantiating it again. Looks like this is the recommended way of doing this as per Angular 2.0 final documentation.

Categories

Resources