Angular displays logged user on load even though localstorage is empty - html

So I am currently following some course.
I have simple login form inside the nav bar
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-primary">
<div class="container">
<a class="navbar-brand" routerLink='/' routerLinkActive='active' >Dating app</a>
<ul class="navbar-nav mr-auto" >
<ng-container *ngIf="accountService.currentUser$ | async">
<li class="nav-item">
<a class="nav-link" routerLink="/members" routerLinkActive='active'>Matches</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/lists" routerLinkActive='active'>Lists</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/messages" routerLinkActive='active'>Messages</a>
</li>
</ng-container>
</ul>
<div class="dropdown" *ngIf="(accountService.currentUser$ | async) as user" dropdown>
<a class="dropdown-toggle text-light" dropdownToggle>Welcome {{user.username | titlecase}}</a>
<div class="dropdown-menu mt-3" *bsDropdownMenu>
<a class="dropdown-item">Edit profile</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="logout()">Logout</a>
</div>
</div>
<form *ngIf="(accountService.currentUser$ | async) === null" #loginForm="ngForm" class="form-inline mt-2 mt-md-0" (ngSubmit)="login()"
autocomplete="off">
<input
name="username"
[(ngModel)]="model.username"
class="form-control mr-sm-2"
type="text"
placeholder="Username">
<input
name="password"
[(ngModel)]="model.password"
class="form-control mr-sm-2"
type="password"
placeholder="Password">
<button class="btn btn-success my-2 my-sm-0" type="submit">Login</button>
</form>
</div>
</nav>
I want to show Welcome {username} when the client is logged in, and login form when nobody is logged in. The thing is when I start the angular app, it is displaying this
instead of this:
Localstorage is empty when I start the app so I literaly have no idea why is this happening.
The service I am using looks like this:
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { ReplaySubject } from 'rxjs';
import { map } from 'rxjs/operators'
import { User } from '../_models/User';
#Injectable({
providedIn: 'root'
})
export class AccountService {
baseUrl = "https://localhost:5001/api/";
private currentUserSource = new ReplaySubject<User>(1);
currentUser$ = this.currentUserSource.asObservable();
constructor(private http: HttpClient) { }
login(model:User)
{
return this.http.post<User>(this.baseUrl + 'account/login',model).pipe(
map((response: User) => {
const user = response;
if(user)
{
localStorage.setItem('user',JSON.stringify(user));
this.currentUserSource.next(user);
}
})
)
}
register(model:User)
{
return this.http.post<User>(this.baseUrl+'account/register',model).pipe(
map((user: User) => {
if(user)
{
localStorage.setItem('user',JSON.stringify(user));
this.currentUserSource.next(user);
}
})
)
}
setCurrentUser(user: User){
this.currentUserSource.next(user);
}
logout()
{
localStorage.removeItem('user');
this.currentUserSource.next(null!);
}
}
The only difference I can spot between my code and the code in tutorial is
this.currentUserSource.next(null!);
and in tutorial it is
this.currentUserSource.next(null);
But when I use null without !, it gives me error and I could not fix it.
I know this might be something silly but I could not get around it

In your component, you check if the current user is truthy:
<div class="dropdown" *ngIf="(accountService.currentUser$ | async) as user" dropdown>
The problem is that when the sessionStorage is empty, you put an empty object ({}) as the current user. And, since objects are always truthy, the welcome message appears:
// src/app/app.component.ts:27
const user: User = JSON.parse(localStorage.getItem('user') || '{}');
this.accountService.setCurrentUser(user);
In this case, you should set the current user to null:
// src/app/app.component.ts:27
const user: User | null = JSON.parse(localStorage.getItem('user') || null);
And in the service change the types to allow this:
private currentUserSource = new ReplaySubject<User|null>(1);
// ...
setCurrentUser(user: User | null){
// ...

Try to change ReplaySubject for BehaviorSubject :
private currentUserSource = new BehaviorSubject<User|null>(null);
and
this.currentUserSource.next(null);

Related

How to pass value from one component to another? (Angular)

I just recently started learning Angular and I have a question. I want to implement a search method to search for a product on my site, I made search.pipe.ts, which works, but the input for input is in the header.component.ts component, and the products array is in the car-list.component.ts component.
car-list.component.html
<div *ngFor="let car of cars | paginate: { itemsPerPage: pageNumber, currentPage: currentPg} | **search:searchStr**" class="col-md-3">
<div class="product box">
<img src="{{'data:image/jpg;base64,' + car.image }}" alt="">
<h3>{{ car.name }}</h3>
<div class="price">{{ car.price | currency:'USD' }}</div>
<button class="btn btn-primary btn-sm">Add to cart</button> <!--(click)="addToCart(tempProduct)"-->
</div>
<br>
</div>
header.component.html
<form class="d-flex me-5">
<input type="text" class="form-control me-2" placeholder="Search cars...">
</form>
header.component.ts
export class HeaderComponent implements OnInit {
searchStr: string = '';
constructor() {
}
ngOnInit(): void {
}
}
search.pipe.ts
#Pipe({
name: 'search'
})
export class SearchPipe implements PipeTransform {
transform(cars: any[], value: any) {
return cars.filter(car => {
return car.name.includes(value);
})
}
}
I want the input values ​​from the header component to be passed to the car-list component so that I can find the product I need.
In this case you can use a shared service where you can pass data from your header component and load that data in your products component.
For further reference - Angular 4 pass data between 2 not related components
use #Input and #Output decorators to communicate between components

Retaining response data in multiple checkboxes (Vue.js)

Iam new to vue. So iam trying to experiment requests and response through web servers. I have mutiple checkboxes as shown:
<li>
<div class="config-label">Photoshop IRB</div>
<div class="config-value">
<input
type="checkbox"
class="rounded text-light mr-3 bigger-checkboxes"
value="photoshop"
v-model="checkedCategories"
/>
</div>
</li>
<li>
<div class="config-label">Flashpix</div>
<div class="config-value">
<input
type="checkbox"
class="rounded text-light mr-3 bigger-checkboxes"
value="flashpix"
v-model="checkedCategories"
/>
</div>
</li>
Multiple checkboxes this way. The script is:
export default{
data(){
return {
checkedCategories : []
};
},
created() {
this.getExtractionConfig().then((data) => {
this.checkedCategories = data;
});
},
Now how do i retain the values of the checkboxes from the response ?
Ohkay, after debugging i found the solution for the silly lapse on my side. I had to select the key in data. So it was :
created() {
this.getExtractionConfig().then((data) => {
this.checkedCategories = data.selected_categories;
});

ngx bootstrap dropdown not showing items with ngFor Angular 9

As the title says I can make the dropdown items to be display when using ngFor
visual results
Inspected element
Html
<li class="nav-item" *ngFor="let item of menu">
<button
class="btn btn-warning btn-flat"
[routerLink]="['/', item.name]"
*ngIf="item.submenu.length === 0"
>
{{ item.name | link | titlecase }}
</button>
<div
class="btn-group"
dropdown
[autoClose]="true"
*ngIf="item.submenu.length > 0"
>
<button
id="button-animated-menu"
dropdownToggle
type="button"
class="btn btn-warning btn-flat dropdown-toggle"
aria-controls="dropdown-animated-menu dropdown"
>
{{ item.name | titlecase }}
<span class="caret"></span>
</button>
<div
id="dropdown-animated-menu"
*dropdownMenu
class="dropdown-menu"
aria-labelledby="button-animated-menu"
>
<li *ngFor="let k of item.submenu">
<a class="dropdown-item" [routerLink]="['/', k.name]">
{{ k.name | link | titlecase }}
</a>
</li>
</div>
</div>
</li>
Component.ts
export class NavbarComponent implements OnInit {
constructor(
private authService: AuthService,
private router: Router,
private alert: AlertService
) {}
menu: Menu[] = this.authService.menus;
ngOnInit(): void {
console.log(this.menu);
}
}
UPDATED
AuthService
export class AuthService {
baseUrl = environment.apiUrlLogIn;
private USER: UserDetails;
jwthelper = new JwtHelperService();
constructor(private http: HttpClient) {}
get roles(): string[] {
return this.USER.authorities;
}
get menus(): Menu[] {
return this.USER.menu;
}
login(data: any) {
return this.http
.post(this.baseUrl, data)
.pipe(
map((response: Token) => {
if (response) {
localStorage.setItem('token', response.access_token);
localStorage.setItem(
'_token',response.refresh_token);
this.USER = this.jwthelper.decodeToken(response.access_token);
}
})
);
}
I've tried different approache, but what I can see, it's a bug of some sort, due to the elements are rendered in the html, but the dropdown is not taking their heights.
Any ideas?
Thanks before hand...
As suggested by MikeOne I started making a stackblitz project, and after finishing it everything worked just fine, but then I realised that the project in stackblitz had the ngx-bootstrap#5.6.0 version while mine was ngx-bootstrap#5.5.0 I thought it was a small issue but I tried anyways and surprisedly it worked.
So the changes were
Updating the packages
from
to
Changing the imports
The import way from the left side of the previous picture for some reason didn't work anymore, after checking the modules folder, it seems the ngx-bootstrap.ts main file was delete in the lastest version, that's why I had to change the import to the way of the right side of the picture

Why does using *ngIf = "authService.getUser()" in my top level app component html break my login component?

I am new to Angular so please bear with my naivety. I created a login component, which I use to prevent access to router navigation links in the main app-component html until the user logs in. The login component itself is another routed page, and I wanted to instead add the login component to my mainpage, and hide the routing navigation links until the user logs in.
To hide the user navigation links in the app-component html I tried using
*ngIf="authService.getUser()".
*ngIf="authService.getUser()" hides the navigation components until the user is logged in as expected, but it unexpectedly also didn't render the entirety of the login component (the user and password fields and submit button), except the first text which simply says "LOGIN". So the user has no way to login.
this is app.component.html:
<app-login>
</app-login>
<div class="page-header" >
<div class="container">
<div *ngIf="authService.getUser()" class="navLinks">
<a [routerLink]="['/home']"> Home</a>
<a [routerLink]="['/about']"> About App</a>
<a [routerLink]="['/login']">Login</a>
<a [routerLink]="['/protected']">Protected</a>
</div>
</div>
</div>
<div id="content">
<div class="container">
<router-outlet></router-outlet>
</div>
</div>
and these are my login component files
login.component.ts:
import { Component } from '#angular/core';
import { AuthService } from '../auth.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
message: string;
constructor(public authService: AuthService) {
this.message = '';
}
login(username: string, password: string): boolean {
this.message = '';
if (!this.authService.login(username, password)) {
this.message = 'Incorrect credentials.';
setTimeout(function() {
this.message = '';
}.bind(this), 2500);
}
return false;
}
logout(): boolean {
this.authService.logout();
return false;
}
}
login.component.html:
<h1>Login</h1>
<div class="alert alert-danger" role="alert" *ngIf="message">
{{ message }}
</div>
<form class="form-inline" *ngIf="!authService.getUser()">
<div class="form-group">
<label for="username">User: (type <em>user</em>)</label>
<input class="form-control" name="username" #username>
</div>
<div class="form-group">
<label for="password">Password: (type <em>password</em>)</label>
<input class="form-control" type="password" name="password" #password>
</div>
<a class="btn btn-default" (click)="login(username.value, password.value)">
Submit
</a>
</form>
<div class="well" *ngIf="authService.getUser()">
Logged in as <b>{{ authService.getUser() }}</b>
<a href (click)="logout()">Log out</a>
</div>
What is in your authService.getUser() method? If it is asynchronous all you have to do is *ngIf="(authService.getUser() | async)".
I don't understand why are you using *ngIf="!authService.getUser()" inside your login component like this.
Semantically the purpose of this component is to be used when you're not logged.
So simply try to wrap your <app-login></app-login> in your app.component.html into a div wich have the *ngIf="!authService.getUser()"
Like this :
<div *ngIf="!authService.getUser()">
<app-login></app-login>
</div>
Also I recommand you to not use directly the service method like this in the html but a flag instead for example isLogged init to false and update it when the user successfully logged.
Your ng-if will be : *ngIf="!isLogged | async"

access array inside array in angular 4

I'm using angular to create a web project that uses google's custom search engine to populate the web pages with the first ten results returned. The data I get back from the API is in JSON format which I can assess and display using an interface. I'm able access the array "items", my problem is I don't know how to access the array inside the items array. Any help is welcome. Ps. i'm new to angular.
interface ISite{
kind: string;
title: string;
htmlTitle: string;
link: string;
displayLink: string;
srcImage: string;
snippet: string;
}
//Second interface to deal the api
interface ISearchResponse {
kind: string;
context: string;
items: ISite[];
cse_images: string;
company: string;
snippet: string;
}
//and my service
constructor(private _http: HttpClient) { }
getSites(): Observable<ISearchResponse> {
return this._http.get<ISearchResponse>(this._siteURL)
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
private handleError(err: HttpErrorResponse) {
console.log('SearchEngineService: ' + err.message);
return Observable.throw(err.message);
}
}
//My html
<h2>Search Results</h2>
<div class="list-group" *ngFor='let site of sites.items'>
<a href="{{site.link}}" class="list-group-item list-group-item-action flex-column align-items-start active" >
<div class="d-flex w-100 justify-content-between">
<h4 class="mb-1">Title: {{site.title}}</h4>
</div>
<h5>URL: {{site.link}}</h5>
<p class="mb-1">Description: {{site.snippet}}</p>
</a>
</div>
sample of the data form google api, I want to access the image in
cse_image
"items": [
{
"kind": "customsearch#result",
"title": "Sports News,Scores,Schedules,Standings,Stats,Photos",
"pagemap": {
"cse_image": [
{
"src": image.jpg"
}
]
}
},
Try this:
<h2>Search Results</h2>
<div class="list-group" *ngFor='let site of sites.items'>
<a href="{{site.link}}" class="list-group-item list-group-item-action flex-column align-items-start active" >
<div class="d-flex w-100 justify-content-between">
<h4 class="mb-1">Title: {{site.title}}</h4>
</div>
<h5>URL: {{site.link}}</h5>
<p class="mb-1">Description: {{site.snippet}}</p>
<img src={{site.pagemap.cse_image[0].src}}>
</a>
</div>