Angular await service between components, Behavior Subject - javascript

I have an issue in my Angular web store when i refresh the window, i create a service that takes the user data from the server and then inject to the 'products' section with BehaviorSubject, my goal is to make just one request to the server:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable({
providedIn: 'root'
})
export class DataService {
private userId = new BehaviorSubject<any>('');
currentUserId = this.userId.asObservable();
constructor() { }
sendUserId(message: string){
this.userId.next(message)
}
}
This works fine but the problem is when i refresh the window in products section, in console i can see that the service takes the user data but when i getProducts() it throws an error, it seems like getProducts() makes the request before the service had the response, i need the user Id to make the products request. My question: Is there a way to await the response of BehaviorSubject and then make the getProducts() request?. This is the code in the products section:
ngOnInit(): void {
this._dataService.currentUserId.subscribe(userId => this.userId = userId);
if(this.userId.length === 0){
this.userService.getUserProfile().subscribe(
res => {
this.userDetails = res['user'];
this.userId = this.userDetails._id;
this.getProducts();
},
err => {
console.log(err);
}
);
} else {
this.getProducts();
}
}
As you can see, i do a condition to check if userId exists, if not i have to make a new user request, this fix the bug but i think there's a better way to solve this. Thanks in advance.

How about placing all your logic within the observer's next function as below:
this._dataService.currentUserId.subscribe(userId => {
if (userId.length === 0)
{
this.userService.getUserProfile().subscribe(
res => {
this.userDetails = res['user'];
this.userId = this.userDetails._id;
this.getProducts();
},
err => {
console.log(err);
}
);
} else
{
this.getProducts();
}
});

Related

Angular - Is there any performance loss when sending repeated http requests in case the client has internet connection turned off?

import { Component, OnInit } from '#angular/core';
import {HttpClient, HttpErrorResponse} from '#angular/common/http';
import { Post } from './post.model';
import { PostService } from './post.service';
import {ConnectionService} from 'ng-connection-service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
loadedPosts: Post[] = [];
isFetching = false;
postCreationData: { name: string };
constructor(private http: HttpClient, private postService: PostService) {}
ngOnInit() {
this.onFetchPosts();
}
onFetchPosts() {
this.isFetching = true;
this.postService.fetchData().subscribe(
(data) => {
this.loadedPosts = data;
this.isFetching = false;
},
(err: HttpErrorResponse) => {
console.log(err.message);
this.onFetchPosts();
}
);
}
onClearPosts() {
// Send Http request
}
onCreatePost(postData: Post) {
this.isFetching = true;
this.postService.createPost(postData).subscribe(
(data) => {
this.postCreationData = data;
console.log(this.postCreationData);
this.isFetching = false;
},
(err: HttpErrorResponse) => {
console.log(err.message);
this.onFetchPosts();
}
);
}
}
Let me explain the code. The this.postService.fetchData() method simply sends an http get request to my firebase database. this.postService.createPost(postData: Post) sends a post request to the same url with some info on the body. The isFetching property allows the app to show a css loading indicator when fetching the data from the server.
Now, I want to show the loading indicator infinitely when the user doesn't have internet connection. Also, at the same time, repeatedly keep sending requests to the server until I get a successfull response in which case, the app simply resumes as it should.
Will there be any noticeable performance loss by doing this. By this, I mean sending infinite number of requests to the server? if so, is there any better alternative?
Try this, it gives you the interval to make sure it's not impacting performance
import { HttpClient } from "#angular/common/http";
import { first } from "rxjs/operators";
import { Subscription } from 'rxjs';
...
private source = interval(3000); // Allows you to determine how often
...
this.source.subscribe(() => {
this.httpclient.get('https://www.google.com', { observe: 'response' })
.pipe(first())
.subscribe(resp => {
if (resp.status === 200 ) {
//all is good
} else {
// some other code like 302,304 etc.
}
},
err => { //this is where to process the HTTPErrorResonse });
});
Keep in mind that TCP/IP has auto-retry built-in, up to 10 retries before failure.

Object is exist with value, but when access the property returned undefined

i find weird things. I have AuthService which saves authentication needs of my apps, included authentication token.
#IonicPage()
#Component({
selector: 'page-login',
templateUrl: 'login.html',
})
export class LoginPage {
constructor(public navCtrl: NavController, public navParams: NavParams, public modalCtrl:ModalController,public auth: AuthService) {
}
ionViewDidLoad() {
console.log(this.auth)
console.log(this.auth.loggedIn)
if(this.auth.loggedIn){
console.log(this.auth);
this.navCtrl.push("TabsPage");
}
}
}
when i call
console.log(this.auth)
it returned authentication
buth when i call
console.log(this.auth.loggedIn)
it return null
this my auth.service.ts
import { Injectable, NgZone, Component } from '#angular/core';
import { Storage } from '#ionic/storage';
// Import AUTH_CONFIG, Auth0Cordova, and auth0.js
import { AUTH_CONFIG } from './auth.config';
import Auth0Cordova from '#auth0/cordova';
import * as auth0 from 'auth0-js';
#Injectable()
export class AuthService {
Auth0 = new auth0.WebAuth(AUTH_CONFIG);
Client = new Auth0Cordova(AUTH_CONFIG);
accessToken: string;
user: any;
loggedIn: boolean;
loading = true;
constructor(
public zone: NgZone,
private storage: Storage
) {
this.storage.get('profile').then(user => this.user = user);
this.storage.get('access_token').then(token => this.accessToken = token);
this.storage.get('expires_at').then(exp => {
this.loggedIn = Date.now() < JSON.parse(exp);
this.loading = false;
});
}
login() {
this.loading = true;
const options = {
scope: 'openid profile offline_access'
};
// Authorize login request with Auth0: open login page and get auth results
this.Client.authorize(options, (err, authResult) => {
if (err) {
throw err;
}
// Set access token
this.storage.set('access_token', authResult.accessToken);
this.accessToken = authResult.accessToken;
// Set access token expiration
const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
this.storage.set('expires_at', expiresAt);
// Set logged in
this.loading = false;
this.loggedIn = true;
// Fetch user's profile info
this.Auth0.client.userInfo(this.accessToken, (err, profile) => {
if (err) {
throw err;
}
this.storage.set('profile', profile).then(val =>
this.zone.run(() => this.user = profile)
);
});
});
}
logout() {
this.storage.remove('profile');
this.storage.remove('access_token');
this.storage.remove('expires_at');
this.accessToken = null;
this.user = null;
this.loggedIn = false;
}
isLoggedIn() :boolean{
return this.loggedIn;
}
}
i'm using ionic3 and auth0 authentication, previously i think that was my fault to not use public identifier on my property. but when i change the property to public, or create getter method that returned the property it still not working at all.
This is due to when the chrome console evaluates the object. If you open the object in your console, you'll see a tiny blue info icon. This will say:
Value was evaluated just now
Basically what happens is that the object content changed between the time you logged it, and the time you opened it in your console.
The login action is asynchronous, which means that the loggedIn property on the auth object will be set after the ionViewDidLoad is called. Perhaps a good thing would be to set the auth inside an APP_INITIALIZER provider, or have some Observable on your auth on which you can listen for auth changes
1.you have colling this.loggedIn before assign so that it's undefined.
in your case write console.log(this.auth.loggedIn) after login. please check that scenario.
2.for now, assign some value for loggedIn variable then print it will print a value
loggedIn: boolean; => loggedIn: boolean=false;
then print a value it will work
in some other component
ngOnInit() {
console.log(this.auth.loggedIn)
}

Profile Picture not updating across components Angular 5

I have a change profile picture modal that pops up so you upload the image press save and what should happen is that the profile picture is updated across the site but that doesnt happen, only after you refresh the profile picture has updated
my save function on the profile picture change modal
save(): void {
const self = this;
this.saving = true;
self._profileService.updateProfilePicture(input)
.finally(() => { this.saving = false; })
.subscribe(() => {
const self = this;
self._$jcropApi.destroy();
self._$jcropApi = null;
abp.event.trigger('profilePictureChanged');
console.log('changed');
this._userService.updateProfilePicture();
self.close();
});
}
so when the user presses save it uploads the image then it calls the updateProfilePicture function on my user service...
my user service is set up like so..
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Subject } from 'rxjs/subject';
#Injectable()
export class UserService {
private profilePictureSource = new Subject<any>();
profilePicture = this.profilePictureSource.asObservable();
updateProfilePicture() {
this.profilePictureSource.next();
}
}
then in the component I want the profile picture to change
import { UserService } from '/userService';
import { ProfileService } from '/profileService';
export class ....
profilePicture: any;
constructor(
private _userService: UserService,
private _profileService: ProfileService
) { }
ngOnInit() {
// grab the profile picture on init
this.userPic();
// Listen for profile picture change
this._userService.profilePicture.subscribe(result => {
this.userPic();
}
}
userPic() {
this._profileService.getProfilePicture().subscribe(result => {
if (result && result.profilePicture) {
this.profilePicture = 'data:image/jpeg;base64,' + result.profilePicture;
}
});
}
then in my HTML
<img [src]="profilePicture" />
I tried to comment out self.close(); just incase that was causing an issue like it was closing before it got a change to call the service but that didnt change anything
EDIT
When I use the chrome debugger Ive put breakpoints on all the functions and the service call.. when I press save the userService function triggers a breakpoint.. but no other functions in the stack are called after that Im not sure why?
2nd EDIT
Ive followed Abylay Kurakbayev answer and changed
profilePicture = this.profilePictureSource.asObservable(); //from
profilePicture = this.profilePictureSource; //to
but that didn't fix the issue
EDIT 3
here is the getProfilePicture() function
getProfilePicture(): Observable<GetProfilePictureOutput> {
let url_ = this.baseUrl + "/api/services/app/Profile/GetProfilePicture";
url_ = url_.replace(/[?&]$/, "");
let options_ : any = {
method: "get",
headers: new Headers({
"Content-Type": "application/json",
"Accept": "application/json"
})
};
return this.http.request(url_, options_).flatMap((response_ : any) => {
return this.processGetProfilePicture(response_);
}).catch((response_: any) => {
if (response_ instanceof Response) {
try {
return this.processGetProfilePicture(response_);
} catch (e) {
return <Observable<GetProfilePictureOutput>><any>Observable.throw(e);
}
} else
return <Observable<GetProfilePictureOutput>><any>Observable.throw(response_);
});
}
EDIT 4
This is the processGetProfilePicture() method
protected processGetProfilePicture(response: Response): Observable<GetProfilePictureOutput> {
const status = response.status;
let _headers: any = response.headers ? response.headers.toJSON() : {};
if (status === 200) {
const _responseText = response.text();
let result200: any = null;
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result200 = resultData200 ? GetProfilePictureOutput.fromJS(resultData200) : new GetProfilePictureOutput();
return Observable.of(result200);
} else if (status !== 200 && status !== 204) {
const _responseText = response.text();
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}
return Observable.of<GetProfilePictureOutput>(<any>null);
}
EDIT 5
Im wondering if there is a way to force refresh the component that the userPic() function is in?? Because the profile picture updates as soon as you refresh the page??
Thanks
From what I see UserService how is declared in the providers ?
In order to check this
a console.log() in the UserService constructor to see if the constructur is called multiple times
also check the providers from components you should have only one provider.
You must have the same istance in order for this flow to work as you
are describing it. If you have one instance across all components can you make a plunker or provide some access to the full implementation of the components.
This issue could be caused by Angular digest too, because I ran and checked your code, I'm able to listen to profilePicture Observable every time. So below block will execute every time, Which calls getProfilePicture always.
this._userService.profilePicture.subscribe(result => {
this.userPic();
}
I can see on ngOnInit() you are able to fetch profile pic using userPic() but not on observable level. So I doubt that you might be doing some Javascript level manipulations on your data, which Angular will not be aware of. I faced this issue multiple times, So I'm not sure if you are doing anything like that in this.processGetProfilePicture(response_) method or somewhere else. Please make sure.
Hope this helps.
Change this code:
userPic() {
this._profileService.getProfilePicture().subscribe(result => {
if (result && result.profilePicture) {
this.profilePicture = 'data:image/jpeg;base64,' + result.profilePicture;
}
});
}
to
userPic() {
this._profileService.getProfilePicture().subscribe(result => {
if (result && result.profilePicture) {
this.profilePicture = 'data:image/jpeg;base64,' + result.profilePicture;
}
}, (err) => console.log(error));
}
Edit
getProfilePicture(): Observable {
let url_ = this.baseUrl + "/api/services/app/Profile/GetProfilePicture";
url_ = url_.replace(/[?&]$/, "");
let options_ : any = {
method: "get",
headers: new Headers({
"Content-Type": "application/json",
"Accept": "application/json"
})
};
return this.http.request(url_, options_).flatMap((response_ : any) => {
console.log(respone); // <-----
return this.processGetProfilePicture(response_);
}).catch((response_: any) => {
console.log(response); // <-----
if (response_ instanceof Response) {
try {
return this.processGetProfilePicture(response_);
} catch (e) {
console.log(e);//<--------
return <Observable<GetProfilePictureOutput>><any>Observable.throw(e);
}
} else
return <Observable<GetProfilePictureOutput>><any>Observable.throw(response_);
});
}
And see if there is anything in console regarding this. I have marked my addition into code as //----
This is a hack, but should work. Call the function inside a timeout after you changed the picture. This will trick Angular to reload the view.
setTimeout(() => {
this.userPic();
}, 500);

Angular 4 Observables Chaning Subscribers - Fired Two times

I have a strange problem while using Angular 4 Observables.
I have created a ServiceProxy.ts that manages all my HTTPS calls for my app
#Injectable()
export class ServiceProxy
{
private base_url = 'https://localhost:8443';
constructor (private http:Http) {}
public Request(route:ServiceRegistry, data : any , protocol:HttpProtocol)
{
let url : string = this.FormURI(route);
let headers = new Headers();
this.createAuthorizationHeader(headers);
if(protocol==HttpProtocol.get)
{
return this.http.post(url , data , {headers: headers})
.map(this.extractData)
.catch(this.handleError);
}
else
{
return this.http.post(url , data , {headers: headers})
.map(this.extractData)
.catch(this.handleError);
}
}
}
Now I go ahead and INJECT this ServiceProxy class in every SERVICE which needs an HTTP calls
#Injectable()
export class AuthenticationService
{
constructor(private proxy:S.ServiceProxy){ }
attemptLogin(d:L.LoginAuth): Observable<any>
{
let r:S.ServiceRegistry =S.ServiceRegistry.STAFF_LOGIN;
let p: S.HttpProtocol = S.HttpProtocol.post;
return this.proxy.Request(r,d,p);
}
}
Once that is done. I call the authentication service from my component
this.authService.attemptLogin(payload).subscribe(response =>
{
alert("Subscription Received");
if(response.status==R.STATUS.OK)
{
this.HandleLogin(JSON.stringify(response.data));
}
else
{
this.HandleFailedLogin();
}
});
Problem is - The subscription function is being called two times instead of just once.
I understand, Promise would be a better fit here as this is just one HTTP call , however I want to standardize the interface with Observables and hence not considering Promises
Observable Chain is not proper, it's broken in AuthenticationService.
Modify AuthenticationService class
#Injectable()
export class AuthenticationService
{
constructor(private proxy:S.ServiceProxy){ }
attemptLogin(d:L.LoginAuth): Observable<any>
{
let r:S.ServiceRegistry =S.ServiceRegistry.STAFF_LOGIN;
let p: S.HttpProtocol = S.HttpProtocol.post;
return this.proxy.Request(r,d,p).map(
(data) => {
return data;
}
).catch(this.handleError);
}
}

Angular 2 http many subscribers

I have a Page and a Service in Angular 2.
When i call the service function this.Auth.login(), it makes a http post request. My problem is, that as soon as the request returns data, i want to work with that data in the service AND the page.
I tried all kinds of stuff, but couldn't figure out how to do it.
I know that my code can't work like this because right now this.Auth.login() return a subscriber object. If i remove the '.subscribe()' in the service, it works in the Page. But thats not what i need.
I also tried to return a promise, but couldn't make it work either.
Does anybody know how i can achieve to have the data from the http.post in both controllers and work with it as soon as the request is finished?
Here's my code
Page:
import {AuthService} from '../auth/auth.service';
#Page({
templateUrl: 'build/pages/signin/signin.html'
})
export class Signin {
constructor(app: IonicApp, auth: AuthService){
this.Auth = auth;
}
signIn = function() {
this.Auth.login(this.user.email, this.user.password)
.subscribe(data => {do some stuff here});
// maybe a promise with .then can solve this
};
}
Service:
import {Http, Headers} from 'angular2/http';
import {Injectable} from 'angular2/core';
#Injectable()
export class AuthService {
private http;
constructor(http:Http) {
this.http = http;
}
login(email, password) {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
// maybe i need to return this as a promise
return this.http.post('http://localhost:9000/auth/local', JSON.stringify({
email: email,
password: password
}), {
headers: headers
})
.subscribe(data => {
do some stuff here too with the data
));
// i tried to add .toPromise() but that didn't work
}
}
I left out the other lines of code so there might be some dependencies missing. It's all good though.
You can use map in the body of the Service login. i.e
.map(data => {
do some stuff here too with the data
// still bubble up
return data;
));
Ok, i don't know if this is legit but it seems to work for me:
var res = this.http.post('http://localhost:9000/auth/local', JSON.stringify({
email: email,
password: password
}), {
headers: headers
});
res.subscribe(
data => {
console.log('data');
},
err => {
console.log('err');
},
() => {
console.log('next');
}
);
return res;

Categories