Waiting till component variable is set by subscribe - javascript

I have to calls in my component. The secound depends on the result from the first call.At the first call, I set the value for my component variable "locked". The secound call should be executed when the result is true --> locked = true. But it will reach the code, before the first call is finished and the value is set. How can I wait with the execution of my code, until the call "getByUsernamer" is finished.
Here is my code:
export class LoginComponent implements OnInit {
errorMessage: string = "";
isLocked = false;
username: string | null = "";
constructor(
private authService: AuthService,
private cookieService: CookieService,
private router: Router,
private jwtTokenService: JwtTokenService,
private userService: UserService) {
}
ngOnInit(): void {
}
test() {
this.userService.getUserByUsername(this.username)?.subscribe(
{
next: response => {
let user: User = response;
this.isLocked = user.locked
}
}
);
if (!this.isLocked) {
this.router.navigate(['/home']).finally(
() => this.userService.setLastLogin(this.username).subscribe());
} else {
this.errorMessage = "The user is locked."
}
}
}

You can check the isLocked variable in the callback of the first call, so you are sure that you received the answer and the isLocked variable is set
export class LoginComponent implements OnInit {
errorMessage: string = "";
isLocked = false;
username: string | null = "";
constructor(
private authService: AuthService,
private cookieService: CookieService,
private router: Router,
private jwtTokenService: JwtTokenService,
private userService: UserService) {
}
ngOnInit(): void {
}
test() {
this.userService.getUserByUsername(this.username)?.subscribe(
{
next: response => {
let user: User = response;
this.isLocked = user.locked;
// Moved here the test so it happens only after
// the first request receive the answer
if (!this.isLocked) {
this.router.navigate(['/home']).finally(() => this.userService.setLastLogin(this.username).subscribe());
} else {
this.errorMessage = "The user is locked."
}
}
}
);
}
}

Related

How do I exclude routing validation in nest.js?

// jwt guard
#UseGuards(AuthStrategyGuard)
#Controller('users')
export class UsersController {
constructor(private readonly authService: AuthService, private readonly usersService: UsersService) {}
#Post()
async getIndex (#Body() body) {
if (!body.user) {
throw new UnauthorizedException('error auth');
} else {
const token = await this.authService.sign(body);
return { token }
}
}
#Post('auth')
validating (#Body('token') token) {
console.log(token, 'token');
return {success: 1}
}
#Post('test')
getTestIndex () {
return {success: 1}
}
}
Here is the JSONWebToken validation logic
I wanted to exclude the #post () decorator from the JWT validation
what should I do?
You can do that by applying the guard only on the methods (endpoints ) you need to validate, in your case, it'll be: ( I assume you want to exclude the validation from the first route
#Controller('users')
export class UsersController {
constructor(private readonly authService: AuthService, private readonly usersService: UsersService) {}
#Post()
async getIndex (#Body() body) {
if (!body.user) {
throw new UnauthorizedException('error auth');
} else {
const token = await this.authService.sign(body);
return { token }
}
}
#UseGuards(AuthStrategyGuard)
#Post('auth')
validating (#Body('token') token) {
console.log(token, 'token');
return {success: 1}
}
#UseGuards(AuthStrategyGuard)
#Post('test')
getTestIndex () {
return {success: 1}
}
}

Angular push objects to array return not function

I'm trying to push returned data from socket-io into my messages array but it says:
ERROR TypeError: this.messages.push is not a function
Code
messages: any[] = [];
constructor(...){...}
ngOnInit() {
this.socket.fromEvent('message').subscribe((message: any) => {
console.log('from socket', message.msg.message); // sample data provided in down
this.messages.push(message.msg.message);
});
}
sample data
from socket
{id: 51, group_id: 1, user_id: 1, note: "wrwgwwg", deleted_at: null, …}
created_at: "2020-05-18T08:19:59.000000Z"
deleted_at: null
group_id: 1
id: 51
note: "wrwgwwg"
updated_at: "2020-05-18T08:19:59.000000Z"
user: {id: 1, name: "Sam", username: "admin", phone: "081200000001", photo: null, …}
user_id: 1
__proto__: Object
Any idea?
Update
my full component code as requested.
import { Component, OnInit } from '#angular/core';
import { Plugins } from '#capacitor/core';
import { MenuController, LoadingController } from '#ionic/angular';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { AlertService } from 'src/app/Services/alert.service';
import { SendChatService } from 'src/app/Services/send-chat.service';
import { ActivatedRoute } from '#angular/router';
import { GroupsService } from 'src/app/Services/groups.service';
import { AuthService } from 'src/app/Services/auth.service';
import { User } from 'src/app/Services/user.service';
const { Toast } = Plugins;
// socket.io
import { Socket } from 'ngx-socket-io';
#Component({
selector: 'app-chat',
templateUrl: './chat.page.html',
styleUrls: ['./chat.page.scss'],
})
export class ChatPage implements OnInit {
newSegment: string;
public chat: FormGroup;
messages: any[] = [];
loading: any;
public user: User;
constructor(
private sendChatService: SendChatService,
private groupsService: GroupsService,
private menu: MenuController,
private alertService: AlertService,
public formBuilder: FormBuilder,
public loadingController: LoadingController,
private activatedRoute: ActivatedRoute,
private authService: AuthService,
private socket: Socket,
) {
this.menu.enable(true);
const id = this.activatedRoute.snapshot.paramMap.get('id');
this.chat = this.formBuilder.group({
newMessage: ['', Validators.required],
group_id: id
});
}
async ionViewDidEnter() {
(await this.authService.user()).subscribe(
user => {
this.user = user;
}
);
}
ionViewWillLeave() {
this.socket.disconnect();
}
ngOnInit() {
Toast.show({
text: 'Selamat Datang Ke grup chat.'
});
this.getData();
// socket.io
this.socket.connect();
// get back stored data form "sendMessage" and add it to the list
this.socket.fromEvent('message').subscribe((message: any) => {
console.log('from socket', message.msg.message);
this.messages.push(message.msg.message);
});
// end of socket.io
}
async getData() {
this.loading = await this.loadingController.create({
message: 'Please wait...',
spinner: 'crescent',
duration: 2000
});
await this.loading.present();
const id = this.activatedRoute.snapshot.paramMap.get('id');
this.groupsService.getGroupsDetails(id).subscribe(res => {
this.messages = res.data;
this.hideLoading();
});
}
private hideLoading() {
this.loading.dismiss();
}
doRefresh(event) {
console.log('Begin async operation');
setTimeout(() => {
console.log('Async operation has ended');
event.target.complete();
}, 2000);
}
ionViewWillEnter() {
this.newSegment = 'chats';
}
sendMessage() {
const chatt = this.chat.value;
this.sendChatService.messagesend(chatt.newMessage, chatt.group_id).subscribe(
(data: any) => {
this.alertService.presentToast(data.message);
console.log(data);
// this.messages.push(data);
// chatt.newMessage.reset();
// socket.io (send returned data to socket server - get it back in "ngOnInit")
this.socket.emit('send-message', { message: data.data });
},
error => {
this.alertService.presentToast(error.statusText);
console.log(error);
},
() => {
//
}
);
}
}
Messages is in the beginning an array, but then I see this line:
this.messages = res.data;
Make sure, that res.data is really an array. Or do this:
this.messages = [...res.data];
or:
this.messages.push(res.data);
or fix the backend: this.socket.fromEvent('message')...
Solved
my messages had notes array inside (ref) all I needed to do was to point new message inside that notes array instead of messages itself.
So the final code is:
this.messages.notes.push(message.msg.message);
instead of
this.messages.push(message.msg.message);

Angular constructor doesn't set a local variable before ngOnInit() is called

I am struggling with calling the backend from angular. When I create a component I also get the parameter "category" from the URL like this:
export class ProductsComponent{
productList = []
category = ""
$params;
$products;
constructor(
private products: ProductsService,
private route: ActivatedRoute
){}
ngOnInit() {
this.$params = this.route.params.subscribe(params => {
this.category = params['category']
});
this.$products = this.products.products(this.category).subscribe(
productList => {
this.productList = productList.result
},
err => {
console.log(err)
}
)
}
ngOnDestroy(){
// DON'T FORGET TO UNSUBSCRIBE!!!
this.$params.unsubscribe();
this.$products.unsubscribe();
}
}
This works well, but now in the ProductsService, where I call the http.get I think it is not working fine.
#Injectable()
export class ProductsService {
constructor(private http: HttpClient, private router: Router) {}
public products(category: string): Observable<any> {
return this.http.get(`/products/getallproducts`, {headers: {'Content-Type': 'application/json'}, params: {'category': category}})
}
}
Because when I try to log the req.body.category in the backend, it says it is null. But it is not, it is the right value.
This is what I am trying to do in Node:
products.get(('/getallproducts'), (req, res) => {
let category = req.body.category;
console.log("REQ" + req.body)
if(category === "all") {
ProductModel.findAll()
.then(result => {
res.json({result: result})
})
.catch(err => {
res.json({error: err})
})
} else {
ProductModel.findAll({
where: {
productsubcategory: category
}
})
.then(result => {
res.json({result: result})
})
.catch(err => {
res.json({error: err})
})
}
})
Review this article: Todd MoTTo: Angular constructor versus ngOnInit
Then move your constructor code into your ngOnInit method.
// Add these;
$params;
$products;
constructor(
private products: ProductsService,
private route: ActivatedRoute
){}
ngOnInit() {
this.$params = this.route.params.subscribe(params => {
this.category = params['category']
});
this.$products = this.products.products(this.category).subscribe(
productList => {
this.productList = productList.result
},
err => {
console.log(err)
});
}
ngOnDestroy(){
// DON'T FORGET TO UNSUBSCRIBE!!!
this.$params.unsubscribe();
this.$products.unsubscribe();
}
Update: I see what you're doing now. It appears to be a bit backwards to me. First you are loading the component, then going to GET some backend data. If you are routing to something new that requires some data, then try a resolver. With a resolver, you can fetch new data on route change. It is up to you if you want to pause the resolver until you get data (and have a spinner on the link that was clicked), or show a loading screen and wait for it. But the resolver will load when the route is loaded and it will publish the result. Then listen for the resolver Observable in the component.
// In Routes
{
path: 'products/:category',
component: YourComponent,
resolve: {
data: ProductsResolver
}
},// rest of routes.
#Injectable()
export class ProductsResolver implements Resolve<any> {
constructor(
private http: HttpClient
){}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any>|Promise<any>|any {
return this.http.get('/products/getallproducts',
{
headers: {
'Content-Type': 'application/json'
},
params: {
'category': route.params.category
}
});
}
And the component then would be...
$products;
constructor(
private route: ActivatedRoute
){}
ngOnInit() {
this.$products = this.route.data.subscribe(productList => {
this.productList = productList.result;
},
err => {
console.log(err)
});
}
ngOnDestroy(){
this.$products.unsubscribe();
}

TypeError: result is null when subscribing to a post request

When I'm trying to subsrcibe to a post request, it always returns the TypeError: result is null
I'm using a Angular CLI that connects with a Spring boot application, with a simple login page. Where I want to save the header of my response in local storage
This is the stacktrace:
"LoginComponent.prototype.login/<#webpack-internal:///../../../../../src/app/components/login/login.component.ts:32:13\nSafeSubscriber.prototype.__tryOrUnsub#webpack-internal:///../../../../rxjs/_esm5/Subscriber.js:245:13\nSafeSubscriber.prototype.next#webpack-internal:///../../../../rxjs/_esm5/Subscriber.js:192:17\nSubscriber.prototype._next#webpack-internal:///../../../../rxjs/_esm5/Subscriber.js:133:9\nSubscriber.prototype.next#webpack-internal:///../../../../rxjs/_esm5/Subscriber.js:97:13\nMapSubscriber.prototype._next#webpack-internal:///../../../../rxjs/_esm5/operators/map.js:88:9\nSubscriber.prototype.next#webpack-internal:///../../../../rxjs/_esm5/Subscriber.js:97:13\nFilterSubscriber.prototype._next#webpack-internal:///../../../../rxjs/_esm5/operators/filter.js:92:13\nSubscriber.prototype.next#webpack-internal:///../../../../rxjs/_esm5/Subscriber.js:97:13\nMergeMapSubscriber.prototype.notifyNext#webpack-internal:///../../../../rxjs/_esm5/operators/mergeMap.js:156:13\nInnerSubscriber.prototype._next#webpack-internal:///../../../../rxjs/_esm5/InnerSubscriber.js:27:9\nSubscriber.prototype.next#webpack-internal:///../../../../rxjs/_esm5/Subscriber.js:97:13\nonLoad#webpack-internal:///../../../common/esm5/http.js:2310:21\nZoneDelegate.prototype.invokeTask#webpack-internal:///../../../../zone.js/dist/zone.js:421:17\nonInvokeTask#webpack-internal:///../../../core/esm5/core.js:4939:24\nZoneDelegate.prototype.invokeTask#webpack-internal:///../../../../zone.js/dist/zone.js:420:17\nZone.prototype.runTask#webpack-internal:///../../../../zone.js/dist/zone.js:188:28\nZoneTask.invokeTask#webpack-internal:///../../../../zone.js/dist/zone.js:496:24\ninvokeTask#webpack-internal:///../../../../zone.js/dist/zone.js:1517:9\nglobalZoneAwareCallback#webpack-internal:///../../../../zone.js/dist/zone.js:1543:17\n"
This is my login.service.ts:
const httpOptions = { headers: new HttpHeaders({'Content-type': 'application/json'}) };
#Injectable() export class LoginService {
private loginUrl = 'https://music-makers.herokuapp.com/login';
constructor(private http: HttpClient) { }
public login(user: User): Observable<any> {
return this.http.post(this.loginUrl, user, httpOptions); }
And my login.components.ts:
export class LoginComponent implements OnInit {
model: any = {};
constructor(private loginService: LoginService, public router: Router) {
}
ngOnInit() {
}
login() {
const user = <User>({
email: this.model.email,
password: this.model.password,
});
console.log('email: ' + user.email + '\npass: ' + user.password);
this.loginService.login(user)
.subscribe(
result => {
// Handle result
localStorage.setItem('Authorization', result.headers.get('Authorization'));
console.log(result);
},
error => {
// Handle error
console.log('Error');
},
() => {
console.log('complete');
// No errors, route to new page
}
);
}
}
Your service should be use map() to return as an observable collection
public login(user: User): Observable<any> {
return this.http.post(this.loginUrl, user, httpOptions)
.map(responce => <any>responce)
.catch(error => {
return Observable.throw(error);
});
}

Angular2 Injecting Service into another Service

I can't find my error.
app.module.ts
...
providers: [ValidateService,AuthService]
...
I do the following in my register.component.ts:
import {AuthService} from '../../services/auth.service';
...
constructor( private _validateService: ValidateService,
private _fms: FlashMessagesService,
private _authService: AuthService,
private _router: Router
) { }
...
ngOnInit() {
this._authService.uniqueUser({username:'zomh'}).subscribe(data => {
console.log("data.success: "+data.success);
if(!data.success) { // Username already exists
console.log('exists');
}
else {
console.log('does not exist');
}
});
}
Works as expected the user is already in the database therefore I get the a user exists in the console.
I do pretty pretty much the very same thing (I broke it down to this point) in my validate.service.ts:
import { AuthService } from './auth.service';
import { Injectable } from '#angular/core';
import { FormControl } from '#angular/forms';
#Injectable()
export class ValidateService {
constructor( public _authService: AuthService) { }
validateRegister(user) {
if(user.name == undefined || user.email == undefined || user.username == undefined || user.password == undefined)
return false;
else
return true;
}
validateEmailPattern(c: FormControl) {
const re = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (re.test(c.value))
return null;
else
return {invalidPattern:true};
}
validateUsernamePattern(c: FormControl) {
const re = /^[A-Za-z0-9]+(?:[ _-][A-Za-z0-9]+)*$/
if (re.test(c.value))
return null;
else
return {invalidPattern:true};
}
validateUsernameIsUnique (c: FormControl) {
let ret:any;
if (c.value.length >= 3)
{
console.log(c.value);
this._authService.uniqueUser({username:'zomh'}).subscribe(data => {
if(!data.success) { // Username already exists
console.log('call from service: exists');
}
else {
console.log('call from service: does not exist');
}
});
}
return {usernameIsTaken:true};
}
}
But here I get a Cannot read property _authService of undefined Exception
For me it looks like the service did not inject correctly. But I can't find my error.
Update 1:
So i did copy the auth Service call into the Constructor and its working. Therefore it has to be some this. related error (?) i can't get the value of this._authService from any other method outside of the constructor ?
#Injectable()
export class ValidateService {
constructor( private _authService: AuthService ) {
this._authService.uniqueUser({ username: 'zomh' }).subscribe(data => {
if (!data.success) { // Username already exists
console.log('call from service: exists');
}
else {
console.log('call from service: does not exist');
}
});
}
I dont think you can have a new line between #Injectable and export class ValidateService {
Try it without that line.
After reading an article I rewrote my method into an instance method:
validateUsernameIsUnique = (c: FormControl) => {
let ret: any;
if (c.value.length >= 3) {
this._authService.uniqueUser({ username: c.value }).subscribe(data => {
if (!data.success) { // Username already exists
console.log('call from service: exists');
}
else {
console.log('call from service: does not exist');
}
});
}
...
It fixed the problem. I am still not sure why this had to be done though, feel free to add knowledge

Categories