Angular DELETE API request doesn't clear my cart - javascript

In the cart component of my app I have an "Clear cart" button. I am trying to clear the cart with a API call that sends a delete request to the API.. My compiler isn't showing me any errors and the console of web browser also isn't showing any errors... but when I click the "Clear cart" button nothing happens.. the items stay in my cart...
What am I doing wrong?
here is the code:
cart.service.ts:
clearCart() {
return from(Preferences.get({key: 'TOKEN_KEY'})).pipe(
switchMap(token => {
const headers = new HttpHeaders().set('Authorization', `Bearer ${token.value}`);
return this.httpClient.delete<ShoppingCart>(`${environment.apiUrl}cart`, {headers, observe: 'response'});
}),
catchError(err => {
console.log(err.status);
if (err.status === 400) {
console.log(err.error.message);
}
if (err.status === 401) {
this.authService.logout();
this.router.navigateByUrl('/login', {replaceUrl: true});
}
return EMPTY;
}),
);
}
cart.component.ts:
clearCart() {
this.cartService.clearCart();
}
And html file just in case :)
<ion-button fill="outline" color="medium" slot="end" size="small" shape="round" (click)="clearCart()">
Clear cart
<ion-icon slot="end" name="trash-outline"></ion-icon>
</ion-button>
Here is the model of cart:
export interface ShoppingCart {
total_products: number;
totals: number;
notes: string;
shipping_method: string;
products: Product[];
}

In order to clear the cart, you need to subscribe to the observable that is returned by the clearCart method in the CartService.
clearCart() {
this.cartService.clearCart().subscribe(() => {
// You can update your cart data here
});
}
make sure to update your local cart also after the API called passed.

Related

POST request in angular returning 401 token invalidate

I need help with understanding and implementing how to add a product to my cart when clicking a button. In my example I have a list of products that I get from an API and each product has a + and - button to increase its quantity and on first click on + button to add it to cart...
I want to achieve that on the click of + button a post request happens to my API that then adds this product to my cart. My API call also check and updates the price if there has been a price change.
Now I have created a cart.service file where I added the bellow code but I keep getting a 401 error (
{status: 401, message: "token_invalidate"}
message: "token_invalidate"
status: 401
) in console on button click... What is my mistake here?
cart.service.ts file:
addUpdateProductToCart(quantity: number, produkt: Product) {
return from(Preferences.get({ key: 'TOKEN_KEY' })).pipe(
switchMap(token => {
const headers = new HttpHeaders().set('Authorization', `Bearer ${token.value}`);
// Initialize Params Object
let params = new HttpParams();
// Begin assigning parameters
params = params.append('product', produkt.id);
params = params.append('quantity', quantity);
return this.httpClient.post(`${environment.apiUrl}cart`, { headers, observe: 'response' }, {params});
}),
catchError(err => {
console.log(err.status);
if (err.status === 400) {
console.log(err.error.message);
}
if (err.status === 401) {
// this.authService.logout();
// this.router.navigateByUrl('/login', { replaceUrl: true });
}
return EMPTY;
}),
);
};
and this is how I'm triggering this POST request in the page where the products are listed (subcategory.page.ts):
incrementQty(index: number, produkt: Product) {
const newQty = this.products[index].step += 1.00;
this.cartService.addUpdateProductToCart(newQty, produkt).subscribe(
data => {
console.log(data);
},
error => {
console.log('Error', error);
}
);
}
And the html code:
<ion-label>
<div class="d-flex ion-justify-content-between">
<div class="prod-details-wrapper">
<p class="product-id">{{ produkt.product_code }}</p>
<p class="product-em">EM: {{ produkt.unit }}</p>
<p class="product-packaging">Pakiranje: {{ produkt.min_weight }} {{ produkt.unit }}</p>
</div>
<div class="qty-input-wrapper">
<ion-item lines="none" slot="end">
<ion-icon color="vigros" name="remove-circle" (click)="decrementQty(i, produkt)"></ion-icon>
<ion-input type="number" value="0" min="0" step="1" [(ngModel)]="produkt.step"></ion-input>
<ion-icon color="vigros" name="add-circle" (click)="incrementQty(i, produkt)"></ion-icon>
</ion-item>
</div>
</div>
</ion-label>
Post needs a body doesn't matter if its empty or not.
if you use Visual Code you can hover over Methods to see what parameters are necessary and in what order. ! or ? after a Parameter doesnt mean you can just skip it just put it down as null or undefined.
-> post(url, body, headers, params)
The way you add your token should work. Are you 100% sure you have a valid token?
If not put console.log(token) after you receive it and try the request with postman/swagger and see if you still get a 401 error.
Also if its your API just debug it and find out at what point the Authorization fails.

Angular search by clicked tag

In my app I am trying to make a feature that when the user click the tag it shows him all the products that have this tag...
My search request is being made with GET method over an API call... so what I am trying to achieve is that on a tag click the tag value is sent as a parameter in the url and thus returning all products with this tag in a new page... My API call works in POSTMAN but I am having trouble implementing it in Angular...
So my main questions and issues are:
How to make the tag clickable so it sends the value with the api request
How to add routerlink to the tag so it redirects to new page where it shows all the products with this tag
I am very new to Angular so please help :)
This is the image how tags are displayed in the app:
Here is my code:
HTML in home.page.html for outputing the tags:
<ion-chip *ngFor="let tag of tags">
<ion-label>{{ tag.tags }}</ion-label>
</ion-chip>
Code for search API in search.service.ts:
searchByTagCall(tag: string) {
return from(Preferences.get({key: 'TOKEN_KEY'})).pipe(
switchMap(token => {
const headers = new HttpHeaders().set('Authorization', `Bearer ${token.value}`);
let params = new HttpParams();
params = params.append('tag', tag);
return this.httpClient.get(`${environment.apiUrl}search`, {headers, observe: 'response', params});
}),
catchError(err => {
console.log(err.status);
if (err.status === 400) {
console.log(err.error.message);
}
if (err.status === 401) {
this.authService.logout();
this.router.navigateByUrl('/login', {replaceUrl: true});
}
return EMPTY;
}),
);
}
Code of home.page.ts:
searchByTag() {
this.searchService.searchByTagCall(tag).subscribe(
(data: any) => {
/* what do I put here? */
},
error => {
console.log('Error', error);
});
}
My JSON looks like this:
{
"tags": [
{
"tags": "fruit"
},
{
"tags": "sour"
},
{
"tags": "sweet"
},
{
"tags": "frozen"
},
{
"tags": "fresh"
},
{
"tags": "vegetable"
},
{
"tags": "raw"
}
]
}
Do the following changes:
home.page.html:
<ion-chip *ngFor="let tag of tags">
<ion-label class="tag" (click)="searchByTag(tag.tags)">{{ tag.tags }}</ion-label>
</ion-chip>
home.page.scss:
// In case you need to change the cursor to be the hand icon
.tag {
cursor: pointer;
}
home.page.ts:
constructor(/*whatever parameters you have in your constructor already*/, private router: Router, private dataService: DataService) {
// Whatever code you have in your constructor
}
searchByTag(tag: string) {
this.searchService.searchByTagCall(tag).subscribe((data: any) => {
// 1. Store the data in some service so it will be accessible for the other page
this.dataService.tagData = data;
// 2. Navigate to the other page (you can store the tag in the route params if you need it)
this.router.navigate(['/tagPage/', tag]);
},
error => {
console.log('Error', error);
});
}

How to get the confirmation service of primeng wait for the approval or denial of the user?

How can I make the primng confirmation service behave equal to the browser native confirm?
When a user clicks on a button, I need under certain conditions to request their confirmation to proceed, in case of denying it, exit the method.
The following is an example using the primeng confirmation service. The problem is that the last line of the method that calls openFile is executed without waiting for user approval.
confirm(): void {
if (this.condition) {
this.confirmationService.confirm({
message: 'Are you sure that you want to proceed?',
header: 'Confirmation',
icon: 'pi pi-exclamation-triangle',
accept: () => {
this.openFile();
this.messageService.add({
severity: 'info',
summary: 'Confirmed',
detail: 'You have accepted'
});
return;
},
reject: () => {
return;
}
});
}
this.openFile();
}
On the other hand by implementing the same logic using the browser native confirmation. It works as expected, so that in the proper condition it will wait for the user's confirmation
confirm1(): void {
if (this.condition) {
const result = confirm('Are you sure that you want to proceed?');
if (result) {
this.openFile();
return;
} else {
return;
}
}
this.openFile();
}
The openFile methodo has a simple console log
openFile(): void {
console.log('Some file');
}
How to get the confirmation service of primeng wait for the approval or denial of the user?
You can interact with an example of the behavior described in this repository https://github.com/ilmoralito/sample-primeng-confirmation-service-vs-native-confirmation
you can create a service as layer above primeng confirm service then use a promise as return type of custom confirm method that call primeng confirm service , the promise resolve as true for accept and false in case of reject.
confirm service
#Injectable({
providedIn: "root"
})
export class ConfirmService {
constructor(private confirmationService: ConfirmationService) {}
confirm({
message = "Are you sure that you want to proceed?",
header = "Confirmation",
icon = "pi pi-exclamation-triangle"
} = {}): Promise<boolean> {
return new Promise(resolve => {
console.log(
this.confirmationService.confirm({
message,
header,
icon,
accept: () => {
resolve(true);
},
reject: () => {
resolve(false);
}
})
);
});
}
}
we can use async/await because confirm method return promise
export class AppComponent {
msgs: Message[] = [];
constructor(private confirmService: ConfirmService) {}
async confirm() {
if (await this.confirmService.confirm())
this.msgs = [
{ severity: "info", summary: "Confirmed", detail: "You have accepted" }
];
else {
this.msgs = [
{ severity: "info", summary: "Rejected", detail: "You have rejected" }
];
}
}
}
stackblitz demo 🚀

Refresh a particular component in Angular

New to angular, so might sound a trivial question, however all solution provided on SO have failed to work so far.
I have a simple login component wherein on submit, I redirect the user to profile page. I am able to send the user to the said component but the nav bar on top does not auto refresh, i.e. I have kept a session check, so when user logs in, the nav bar should automatically show the Logout button instead of Login/Register button. My code files are something like this:
login-page.component.html
<form #loginForm="ngForm" (ngSubmit)="loginUser(loginForm)" id="loginForm" class="loginbackground">
<input ngModel #emailAddress="ngModel" type="text" autocomplete="off" placeholder="Email" id="emailAddress" name="emailAddress" />
<button type="submit" id="submit">LOGIN</button>
login-page.component.ts
#Output() refreshEvent = new EventEmitter<any>();
loginUser(event) {
// Validations. If successful, proceed
const formData = event.value;
this.auth.loginUser(formData);
.subscribe(data => {
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
this.errorPopup = true;
this.errorText = 'Email Address and Password do not match';
} else {
this.refreshEvent.emit();
this.emailAvailable = true;
this.showLogin = false;
this.showRegister = false;
this.router.navigateByUrl('/404', { skipLocationChange: true }).then(() =>
this.router.navigate(['user-profile']));
}
});
});
}
Problem
When I manually refresh the page, the nav bar reflects the changes as per written logic which works fine. However, I want this to happen when the user actually logs in and there shouldn't be a need to manually refresh the page.
What I've tried
I have tried using the answer here but it does not work.
As shown above, I have tried an event emitter but I am not able to get it to work.
I tried refreshing the entire page using ngOnInit() to reload the
nac bar component but it goes into and infinite loop (which is
obviously a hack; but why not)
Is there a way that this can be achieved cleanly?
This is how I solved it:
nav.component.html
...
<li *ngIf="!auth.isLoggedIn()">
...
</li>
<li *ngIf="auth.isLoggedIn()">
...
</li>
...
nav.component.ts
export class NavComponent implements OnInit {
constructor(public auth: AuthService) {
}
...
auth.service.ts
export class AuthService {
...
public isLoggedIn() {
return this.getId() !== null;
}
...
In this last method, 'this.getId()' could be to get the token from localStorage.
The solution for this is basic, you should use the most common features of Angular. I will get you through the thought process and then show you some sample code.
Thought process:
Problem: We need to know whether the user is logged in or not at all times.
Solution: We will have a service that tells us whether the user is logged in or not
Problem: The navigation bar should rely on the user's authentication status
Solution: We will use the status returned by the authentication service, to conditionally show one set of items or another set of items based on the user's authentication status
Code level issues:
You have certain issues within your code that will make your life difficult to further develop other features that rely on authentication status.
I've wrote two steps in how to improve your code, this first one is just improving the flow of your data and the code quality. The second step is completing the corrected code with a more dynamic data flow.
Step 1
Service
We will have a variable within the Authentication Service that tells us whether the user is already logged in or not:
private isUserLoggedIn: boolean = false;
We need to move all the authentication logic into the Authentication Service. Since I don't have the code for this.auth.loginUser(formData), I will call it myself from the new Authentication Service, but note, that the code from that function, should be in our new login function.
Also, there is no need to keep the HTTP call for login as an observable, as you only get one answer, so we can convert it to a promise with .toPromise().
The login function that calls the API will look like this:
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
We will also want to check if the user is logged by checking the localStorage (in case we want the user to not have to log in after each refresh):
The double negation !! tells us whether a value is truthy or falsy, so if we have something on the loggedUser key in the localStorage, we will take it as the user is logged in
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
We will also need the login function that we invoke when we press the login button (we invoke it from the service, through the component):
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
And to make it complete, we will first check if the user is logged in (we do this by invoking isAlreadyLoggedIn() in the constructor of the service. Also, we will have a public function through which we can check if the user is already logged in:
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
The complete service looks like this:
#Injectable()
export class AuthService {
private isUserLoggedIn: boolean = false;
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
// Use this function to check if the user is already logged in
// Use this function to login on the server
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
}
Login Component:
This will check if the user is already logged in on initialization, if it is, then we redirect the user to the profile page, otherwise we show the login form. (The HTML remains the same as you have it, I would also add an error into a span tag). Please note that there are missing properties that you have in your login.ts, I just did the authentication part, add the form related variables to make the component complete and functional.
#Component({
selector: 'app-login'
})
export class LoginComponent implements OnInit {
public isLoggedIn: boolean = false;
public error: string = '';
constructor(authService: AuthService, router: Router) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
if (this.isLoggedIn) {
this.router.navigate(['user-profile']);
}
}
loginUser(event) {
const formData = event.value;
this.authService.login(formData)
.then(res => this.router.navigate(['user-profile']))
.catch(error => this.error = error);
}
}
Navigation Component:
The component gets the login status of the user, and renders its items accordingly:
#Component({
selector: 'app-nav'
})
export class NavComponent implements OnInit {
public isLoggedIn: boolean = false;
constructor(authService: AuthService) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
}
}
Nav Template:
ng-template is a container that we are going to show, in case the user is not logged in.
<ul *ngIf="isLoggedIn; else notLoggedIn">
<li>Home</li>
<li>Profile</li>
<li>Log Out</li>
</ul>
<ng-template #notLoggedIn>
<ul>
<li>Home</li>
<li>Log In</li>
</ul>
</ng-template>
This approach would be the basic, using redirects.
Step 2
We can complete this now with a more dynamic way (although I would personally stick to redirects):
We will add the following variables to our service:
private subject = new Subject();
private observable = this.subject.asObservable();
What this does, is that we can subscribe to observable from any component, and from the service, we can pass data live through the subject to the observable's subscribers. More about these, here
Now, whenever we update the login status, we invoke the following:
this.subject.next(this.isUserLoggedIn);
And this way, all the subscribers will be notified of this change.
We need a function that returns the observable to which the components can subscribe:
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
And all that's left, is to subscribe to this observable from the components that need live updates regarding the authentication status, in our case, the nav component (within ngOnInit):
this.authService.isLoggedInObservable.subscribe(isLoggedIn => this.isLoggedIn = isLoggedIn);
This is how the final service looks like:
#Injectable()
export class AuthService {
private isUserLoggedIn: boolean = false;
private subject = new Subject();
private observable = this.subject.asObservable();
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
// Use this function to check if the user is already logged in
// Use this function to login on the server
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
this.subject.next(this.isUserLoggedIn);
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
this.subject.next(this.isUserLoggedIn);
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
}
And this is how the final Nav Component looks like:
#Component({
selector: 'app-nav'
})
export class NavComponent implements OnInit {
public isLoggedIn: boolean = false;
constructor(authService: AuthService) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
this.authService.isLoggedInObservable.subscribe(isLoggedIn => this.isLoggedIn = isLoggedIn);
}
}
I hope this clarifies how the code should look like. As a recap, you should handle all your login within the service, and expose a boolean that you can get from any component, so that you know the status of the authentication and act based upon it, and with the observables, you will get the latest status at all times.

Promises break while building a pages

I'm using promise and http.get to fetch data from JSON API from Wordpress
After fetching the data, I will show the data on a page..
But the error show because the data is not available while building the page
How to solve that error ?
Here's my service code
loadPost(id) {
return new Promise(resolve => {
this.http.get('http://blog.macg.xyz/api/get_post/?id='+id)
.map(res => res.json())
.subscribe(post => {
this.post = post;
resolve(this.post);
console.log(this.post);
});
});
}
And here's the controller
export class PostPage {
public id:any;
public post: any;
public loading : any;
constructor(public postService: PostService, public navCtrl: NavController, params: NavParams, public loadCtrl: LoadingController) {
this.id = params.get("id");
// this.onPageWillEnter();
}
onPageWillEnter() {
// Starts the process
this.showLoading();
}
showLoading() {
this.loading = this.loadCtrl.create({
content: "Please wait..."
});
// Show the loading page
this.loading.present();
// Get the Async information
this.loadPost();
}
loadPost(){
this.postService.loadPost(this.id) // here where I call the service
.then(data => {
this.post = data;
console.log(this.post);
this.hideLoading();
});
}
hideLoading(){
// Hide the loading component
this.loading.dismiss();
}
}
Here's the html code
<ion-header>
<ion-navbar>
<ion-title>{{id}}</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
{{post.post.content}}
</ion-content>
And here's the error
Since the error is thrown from your template file, maybe post.post.content is throwing the error. If your post is null (which is true at first run), trying to reach its post variable may throw that error.
You can control whether the post is null or not, such as
<ion-content padding ng-if="post">
{{post.post.content}}
</ion-content>
You can use the elvis operator ? to avoid that error from being thrown when the post property is still null
<ion-header>
<ion-navbar>
<ion-title>{{id}}</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
{{post?.post.content}}
</ion-content>

Categories