I wanted to retrieve an information from backend if some email address from input already exists. Based on this information I'm calling a function that make a post that inserts user into database. The problem is that user is inserted only after second click on my SignUp button (function registerUser is called on this button).
Component stuff:
registerUser(form: NgForm) {
let date: Date = new Date();
this.newUser.registrationDate = date;
this.checkEmailStatus(); //IMPLEMENTATION BELOW
if (this.signupForm.valid === true && this.emailStatus) {
this.portfolioAppService.registerUser(this.newUser).subscribe((data) => {
this.clearFields();
this.navigateToLogin();
},
error => console.error(error)
);
}
}
checkEmailStatus() {
this.portfolioAppService.checkEmailStatus(this.newUser.email).subscribe((data: string) => {
if (data != "") {
this.emailStatus = true;
}
else this.emailStatus = false;
},
error => console.error(error)
);
}
Here is my service:
checkEmailStatus(email: string): Observable<string> {
return this.http.get<string>(`/api/Users/CheckEmailStatus_${email}`, this.httpOptions);
}
Here is backend:
[HttpGet]
[Route("~/api/Users/CheckEmailStatus_{email}")]
public string CheckEmailStatus(string email)
{
try
{
User user = _context.Users.Where(u => u.Email == email).FirstOrDefault();
if (user != null)
{
return user.Email;
}
else
{
return "";
}
}
catch (Exception e)
{
throw new Exception("Error!");
}
}
Call to this.portfolioAppService.checkEmailStatus() is asynchronous. So when you check if (this.signupForm.valid === true && this.emailStatus) after the this.checkEmailStatus() call, the variable this.emailStatus is still undefined. To fix it, you could return an observable from the checkEmailStatus() in the component. Try the following
Component
registerUser(form: NgForm) {
let date: Date = new Date();
this.newUser.registrationDate = date;
this.checkEmailStatus().pipe(take(1)).subscribe(status => {
if (this.signupForm.valid === true && status) { // <-- check the status of email address
this.portfolioAppService.registerUser(this.newUser).subscribe((data) => {
this.clearFields();
this.navigateToLogin();
},
error => console.error(error)
);
}
});
}
checkEmailStatus() : Observable<boolean> {
const result = new Subject<boolean>();
this.portfolioAppService.checkEmailStatus(this.newUser.email).subscribe(
(data: string) => {
if (data !== '') {
result.next(true);
}
else result.next(false);
},
error => {
console.error(error);
result.next(false);
}
);
return result.asObservable();
}
Related
Currently working on a 1:1 live chat messenger. when i send a message its shows up at the bottom of chat where i want it to, but upon page refresh it returns to the top of the messenger. How do i set it so newest message is always at the bottom? heres some code i believe the problem is in setActiveChat or or addMessageToConversation.
const sendMessage = (data, body) => {
socket.emit("new-message", {
message: data.message,
recipientId: body.recipientId,
sender: data.sender,
});
};
const postMessage = async (body) => {
try {
const data = await saveMessage(body);
if (!body.conversationId) {
addNewConvo(body.recipientId, data.message);
} else {
addMessageToConversation(data);
}
sendMessage(data, body);
} catch (error) {
console.error(error);
}
};
const addNewConvo = useCallback(
(recipientId, message) => {
setConversations(previousState => previousState.map(convo => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message)
convo.latestMessageText = message.text;
convo.id = message.conversationId;
return convo
}
return convo
}))
},
[setConversations],
);
const addMessageToConversation = useCallback(
(data) => {
// if sender isn't null, that means the message needs to be put in a brand new convo
const { message, sender = null } = data;
if (sender !== null) {
const newConvo = {
id: message.conversationId,
otherUser: sender,
messages: [message],
};
newConvo.latestMessageText = message.text;
setConversations((prev) => [newConvo, ...prev]);
}
conversations.forEach((convo) => {
if (convo.id === message.conversationId) {
const convoCopy = { ...convo };
convoCopy.messages.push(message);
convoCopy.latestMessageText = message.text;
return convoCopy;
} else {
return convo;
}
});
setConversations([...conversations]);
},
[setConversations, conversations],
);
const setActiveChat = useCallback((username) => {
setActiveConversation(username);
}, []);
I am doing with Ionic News App. I have one problem with getting a response from the service file.
home.page.ts
getNews(): void {
this.loading = true;
this.language = localStorage.language;
this.checkForToken();
var userId = this.loggedInUser;
this._newsService.getAllNews().subscribe(
(res: any) => {
console.log("all news==========>", res)
this.loadNewsToPage(res, userId);
},
(err) => {
this.loading = false;
this.error = err;
});
}
news.service.ts
getAllNews(){
if(this.network.type == 'none' ){
console.log(JSON.parse(localStorage.getItem("newsArray")));
this.newsArray = JSON.parse(localStorage.getItem("newsArray"))
return this.newsArray;
}else{
return this.http.get(config.baseApiUrl + 'news?isApproved=APPROVED').pipe(
map((res) => {
this.newsArray = res['data'];
localStorage.setItem('newsArray',JSON.stringify(this.newsArray))
return this.newsArray;
}),
catchError(this.handleError));
}
}
Now the problem is when the network is 'none' it goes in 'if' condition in service file and return response from local storage. But it gives me below error when network is none.
ERROR TypeError: this._newsService.getAllNews(...).subscribe is not a
function
It works properly when it goes in else condition or when a network is present. Why like this?
Your getAllNews function isn't an Observable. So you can't subscribe to it. See the example below where you return an Observable for the first if condition and the second else condition. You need to close the Observable with observer.complete() after each next function.
getAllNews(): Observable<any>{
return new Observable(observer => {
if(this.network.type == 'none' ){
console.log(JSON.parse(localStorage.getItem("newsArray")));
this.newsArray = JSON.parse(localStorage.getItem("newsArray"))
observer.next(this.newsArray);
observer.complete();
} else{
this.http.get(config.baseApiUrl + 'news?isApproved=APPROVED').subscribe(
(result: object) => {
this.newsArray = result['data'];
localStorage.setItem('newsArray',JSON.stringify(this.newsArray))
observer.next(this.newsArray);
observer.complete();
},
(error) => {
observer.error(error);
});
}
});
}
You can now access the next block in your subscribe > result and the error block in subscribing> error.
this._newsService.getAllNews().subscribe(
(res: any) => { // observer.next() brings you here
console.log("all news==========>", res)
this.loadNewsToPage(res, userId);
},
(err) => { // observer.error() brings you here
this.loading = false;
this.error = err;
});
I have a Mat Dialog with an input field and some buttons and whenever I press one of those buttons I'm supposed to get the value inserted on the input field, retrieve some info from my database and send it to my component via a Service.
The problem is that whenever I click on the button, I'm executing several requests instead of just one.
The first request returns undefined and because of that my component isn't being populated with the values I want.
The second request retrieves the values I want but because I'm already on my component, the info isn't being propagated via my Service.
Here is my code:
I'm assuming the service is working fine because I'm using it with two other components successfully.
my.service.ts
private ordersSource = new BehaviorSubject({});
currentOrders = this.ordersSource.asObservable();
private returnsSource = new BehaviorSubject({});
currentReturns = this.returnsSource.asObservable();
constructor() { }
setOrders(el){
this.ordersSource.next(el);
}
setReturns(el){
this.returnsSource.next(el);
}
In my mat dialog, I click a button that executes the createReturn(value) function. I already checked that the value is correct via debug so it is also correctly executing the else statement.
The getOrderByID and getOrderByNumber functions (called depending on the value) are being executed several times, however I can't see a reason why that is.
my-dialog.component.ts
private orders: {};
private returns: Return[] = [];
constructor(public rest: RestService, private route: ActivatedRoute, private router: Router,
private dialogRef: MatDialogRef<MyDialogComponent>, #Inject(MAT_DIALOG_DATA) public data: any[],
private myService: MyService, public dialog: MatDialog) { }
private createReturn(value) {
if (value === undefined || value === "") {
this.inputForm.setErrors({ 'invalid': true });
} else {
this.getOrder(value);
if (this.orders !== undefined) {
this.router.navigate(['my-component']);
this.closeDialog();
}
}
}
// Also working as expected, no problem detected here
private getOrder(value) {
if (value.length > 0 && !isNaN(value)) {
this.getOrderByID(value);
} else if (value.length > 0 && isNaN(value)) {
this.getOrderByNumber(value);
}
this.myService.setOrders(this.orders);
this.myService.setReturns(this.returns);
}
private getOrderByID(value) {
this.rest.getOrder(value).subscribe((orderIdData: {}) => {
if (Object.entries(orderIdData).length !== 0) {
this.orders = orderIdData;
this.rest.getReturnByOrderId(value).subscribe((returnOrdIdData: Return[]) => {
if (Object.entries(returnOrdIdData).length !== 0) {
this.returns = returnOrdIdData;
} else {
this.returns = [];
}
}, error => {
if (error.status === 404) {
this.returns = [];
}
});
} else {
this.inputForm.setErrors({ 'invalid': true });
}
}, error => {
this.inputForm.setErrors({ 'invalid': true });
});
}
private getOrderByNumber(value) {
this.rest.getOrderByNumber(value).subscribe((orderNrData: {}) => {
if (Object.entries(orderNrData).length !== 0) {
this.orders = orderNrData;
this.rest.getReturnByOrderNumber(value).subscribe((returnOrdNrData: Return[]) => {
if (Object.entries(returnOrdNrData).length !== 0) {
this.returns = returnOrdNrData;
} else {
this.returns = [];
}
}, error => {
if (error.status === 404) {
this.returns = [];
}
});
} else {
this.inputForm.setErrors({ 'invalid': true });
}
}, error => {
this.inputForm.setErrors({ 'invalid': true });
});
}
And this is the component that router.navigate(['my-component']) redirects to. This component works as intended when I use the service in another component so I assume the problem isn't here.
my-component.component.ts
constructor(public rest: RestService, private route: ActivatedRoute, private router: Router, private myService: MyService) { }
ngOnInit() {
this.myService.currentOrders.subscribe(orderData =>
this.setOrdersArray(orderData));
this.myService.currentReturns.subscribe(returnData =>
this.setReturnsArray(returnData));
this.setOrderValues(this.orders);
this.onChangeReturnType();
}
I tried debugging using Chrome's DevTools and I found out that the request was being executed three to six times when I only want it to be executed once.
Anyone know what might be wrong?
For anyone who might want to know, here is the solution I found...
I finally understood and figured out what was the issue with my code.
Basically I was trying to send data to my service and navigate to my component before the database gave a response with the data I needed. Also, there were multiple http requests because of this https://stackoverflow.com/a/53371896/11033212.
So I made the following changes:
my-dialog.component.ts
private getData(value) {
if (value === undefined || value === "") {
this.inputForm.setErrors({ 'invalid': true });
} else {
this.getOrder(value);
}
}
private getOrder(value) {
if (value.length > 0 && !isNaN(value)) {
this.getOrderByID(value);
} else if (value.length > 0 && isNaN(value)) {
this.getOrderByNumber(value);
}
}
private getOrderByID(value) {
this.rest.getOrder(value).subscribe((orderIdData: {}) => {
if (Object.entries(orderIdData).length !== 0) {
this.orders = orderIdData;
this.rest.getReturnByOrderId(value).subscribe((returnOrdIdData: Return[]) => {
if (Object.entries(returnOrdIdData).length !== 0) {
this.returns = returnOrdIdData;
} else {
this.returns = [];
}
this.createReturn(orderIdData);
}, error => {
if (error.status === 404) {
this.returns = [];
}
this.createReturn(orderIdData);
});
} else {
this.inputForm.setErrors({ 'invalid': true });
}
}, error => {
this.inputForm.setErrors({ 'invalid': true });
});
}
private getOrderByNumber(value) {
this.rest.getOrderByNumber(value).subscribe((orderNrData: {}) => {
if (Object.entries(orderNrData).length !== 0) {
this.orders = orderNrData;
this.rest.getReturnByOrderNumber(value).subscribe((returnOrdNrData: Return[]) => {
if (Object.entries(returnOrdNrData).length !== 0) {
this.returns = returnOrdNrData;
} else {
this.returns = [];
}
this.createReturn(orderNrData);
}, error => {
if (error.status === 404) {
this.returns = [];
}
this.createReturn(orderNrData);
});
} else {
this.inputForm.setErrors({ 'invalid': true });
}
}, error => {
this.inputForm.setErrors({ 'invalid': true });
});
}
// This only happens after my database's response occurs
private createReturn(el) {
this.setData(el);
this.closeDialog();
this.router.navigate(['my-component']);
}
My input field now calls getData() when a click event occurs.
Previously, I was calling the functions to close the dialog and to navigate to my component outside the getOrderByID and getOrderByNumber function requests. That lead the former functions to execute before the requests had a response.
With these changes, my code now waits for the database's response before continuing with its execution.
i have a function called 'updateProfile()' which has a condition which is if(emailChangeConfirm), this condition depends upon the value of variable 'emailChangeConfirm' , this variable gets the value returned by another function called 'updateEmailAllProcessing()'
the condition 'if(emailChangeConfirm)' is not getting satisfied at all because compiler is not waiting for the function 'updateEmailAllProcessing()' to return the value for variable 'emailChangeConfirm'.
I used async/await for this but that is also not working as i want
Desired Solution :
function 'updateProfile()' must wait for the function 'updateEmailAllProcessing()' to get the result in 'emailChangeConfirm' so that i can enter in the condition 'if(emailChangeConfirm)'.
I am using typescript and working on hybrid app with ionic 3 and angular 5.
async updateProfile(updatedData : Credentials,tarUser : Credentials)
{
// console.log(tarUser,'<<--->>>',updatedData)
let count : number = undefined;
let emailChangeConfirm : boolean;
if(updatedData.name)
{
if(tarUser.name != updatedData.name)
tarUser.name = updatedData.name;
else
count++;
}
if(updatedData.email)
{
if(tarUser.email != updatedData.email)
{
**emailChangeConfirm = await this.updateEmailAllProcessing();**
console.log(emailChangeConfirm)
**if(emailChangeConfirm)
tarUser.email = updatedData.email;**
}
else
count++;
}
if(updatedData.phoneNo)
{
if(tarUser.phoneNo != updatedData.phoneNo)
tarUser.phoneNo = updatedData.phoneNo;
else
count++;
}
if(updatedData.photoURL)
{
if(tarUser.photoURL != updatedData.photoURL)
tarUser.photoURL = updatedData.photoURL;
else
count++;
}
if(count)
this.mesService.presentToast('Nothing Updated!!')
else **if(emailChangeConfirm)**
{
this.dbServe.editUser(tarUser).then(() =>
{
console.log("User Edited Successfully with email change too");
this.authServ.updateEmail(tarUser.email).then(() =>
{
console.log('login email updated');
this.authServ.logout();
})
//this.close();
})
}
else
{
this.dbServe.editUser(tarUser).then(() =>
{
console.log("User Edited Successfully with No email change");
this.close();
})
}
}
**async updateEmailAllProcessing()**
{
let result : boolean;
let alert = this.mesService.emailChangeConfirmation();
alert.present();
alert.onDidDismiss((data) => {
console.log('data->',data);
if(data)
{
let alert1 = this.mesService.passwordPrompt();
alert1.present();
alert1.onDidDismiss(data1 =>
{
console.log('password->',data1);
if(data1)
{
this.authServ.reauthenticateUser(data1).then(() =>
{
console.log('User Reauth Done');
result = true;
})
}
else
result = false;
})
}
else
result = false;
})
**return result;**
}
You need updateEmailAllProcessing to return a promise so you can await on it. and resolve the promise with the result inside the callback.
async updateEmailAllProcessing()
{
return new Promise((resolve, reject) => {
let result: boolean;
let alert = this.mesService.emailChangeConfirmation();
alert.present();
alert.onDidDismiss((data) => {
console.log('data->', data);
if (data) {
let alert1 = this.mesService.passwordPrompt();
alert1.present();
alert1.onDidDismiss(data1 => {
console.log('password->', data1);
if (data1) {
this.authServ.reauthenticateUser(data1).then(() => {
console.log('User Reauth Done');
resolve(true);
})
}
else
resolve(false);
})
}
else
resolve(false);
})
});
}
i'm working with a angular and i'm trying to apply some AuthGard on some Paths.
The problem is canActivate() renders the content before it checks with the SecurityContext, after a verification that no SecurityContext is applied then a redirection to the default page (login) page is applied.
This is the portion of code responsible for this.
app.routing.ts
{
path: 'admin',
canActivate: [AuthGard],
component: HomeComponent,
children : [
{
path: 'add-merchant-admin',
component : AddMerchantAdminComponent,
},
{
path: 'list-merchant-admin',
component : ListMerchantAdminComponent,
}
]
},
AuthGard.ts
canActivate(_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
this._authService.getRoles().subscribe(
res => {
if (res.status == 200) {
this.roles = JSON.parse(res.text());
this.role = this.roles[0].authority;
localStorage.setItem('role', this.role);
if (this.role == 'ROLE_ADMIN') {
this._router.navigate(['admin']);
} else {
if (this.role == 'ROLE_ANONYMOUS') {
this._router.navigate(['login']);
this.error = false;
}
}
} else {
this._router.navigate(['login']);
this.error = true;
}
}, err => {
this._router.navigate(['login']);
this.error = true;
}
);
return !this.error;
};
AuthService
getRoles() {
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers, withCredentials: true});
return this.http.get('http://10.0.0.239:8080/**/**/RolesResource/getRole', options)
.map((res) => res)
.catch((error: any) => Observable.throw(error.text() || 'Server error'));
}
All Services are correctly injected,
Normally a redirection to protected area or default page should be applied after the verification is made using getRole() method.
The problem you are having is that this._authService.getRoles() makes a network call which is asynchronous. return !this.error; is being fired before the network call is being returned so !this.error does not change and is therefore still truthy.
To solve this issue you should be able to return an observable as follows:
return this._authService.getRoles().map(
res => {
if (res.status == 200) {
this.roles = JSON.parse(res.text());
this.role = this.roles[0].authority;
localStorage.setItem('role', this.role);
if (this.role == 'ROLE_ADMIN') {
this._router.navigate(['admin']);
} else {
if (this.role == 'ROLE_ANONYMOUS') {
this._router.navigate(['login']);
return false;
}
}
} else {
this._router.navigate(['login']);
return true;
}
}).catch((err) => {
this._router.navigate(['login']);
return Observable.of(false);
}
);
Something like this should work
canActivate(_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return this._authService.getRoles()
.map(response => JSON.parse(response.text())[0].authority)
.do(role => localStorage.setItem('role', role))
.map( role => role === 'ROLE_ADMIN')
.catch(() => this._router.navigate(['login']));
};
You can try with return observable, which can be updated either true or false.
instead of returning return !this.error; which is always true, try to return
canActivate(_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return this._authService.getRoles().map(
res => {
if (res.status == 200) {
this.roles = JSON.parse(res.text());
this.role = this.roles[0].authority;
localStorage.setItem('role', this.role);
if (this.role == 'ROLE_ADMIN') {
this._router.navigate(['admin']);
} else {
if (this.role == 'ROLE_ANONYMOUS') {
this._router.navigate(['login']);
return false;
}
}
} else {
this._router.navigate(['login']);
return true;
}
}, err => {
this._router.navigate(['login']);
return Observable.of(false);
}
);
};
Edited