How can I show data in an Angular component in Angular 10? - html

I'm starting with Angular 10 and I want to put the current user in the profile.component.html and the navbar in app.component.html. Here is the code.
users.ts
export interface User {
username : string
password: string
edad: number
fechaNacimiento: string
createdAt?: string
updatedAt?: string
id?:number
}
login.component.ts
import { Component, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
import { ActivatedRoute, Router } from '#angular/router';
import { UsersService } from 'src/app/services/users.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
constructor(
public usersServices: UsersService,
private router: Router
) { }
ngOnInit(): void {
}
login(form: NgForm){
this.usersServices.login(form.value).subscribe(
res => {
console.log(res);
localStorage.setItem('token',res['token']);
this.router.navigate(['/profile',form.controls['username'].value],{
state:{username:form.controls['username']}
});
},
err => {
console.log(err)
}
)
}
}
profile.component.ts
import { Component, OnInit } from '#angular/core';
import { UsersService } from '../../services/users.service';
import { NgForm } from '#angular/forms';
import { User } from 'src/app/models/users';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
constructor(public usersService: UsersService,private route: ActivatedRoute) {
this.route.params.subscribe(username => {
console.log(username);
})
}
ngOnInit(): void {
this.getUsers();
}
getUsers(){
this.usersService.getUsers().subscribe(
res => {
this.usersService.user = res
},
err => console.log(err)
)
}
deleteUser(id:number){
if(confirm('Are you sure you want to delete it?')){
this.usersService.deleteUser(id).subscribe(
(res) => {
this.getUsers();
},
(err) => console.log(err)
);
}
}
updateUser(form: NgForm){
this.usersService.editUser(form.value).subscribe(
res => console.log(res),
err => console.log(err)
);
}
}
<div class="col-md-8">
<table class="table table-striped">
<thead>
<tr>
<th>Username</th>
<th>Age</th>
<th>Birthdate</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of usersService.user">
<td>{{user.username}}</td>
<td>{{user.edad}}</td>
<td>{{user.fechaNacimiento}}</td>
<td>
<button class="btn btn-secondary btn-sm" data-toggle="modal" data-target="#staticBackdrop">
<i class="material-icons">edit</i>
</button>
<button class="btn btn-danger btn-sm" (click)="deleteUser(user.id)">
<i class="material-icons">delete</i>
</button>
</td>
</tbody>
</table>
</div>
app.component.html
<nav class="navbar navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="#">MEAN Users</a>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" routerLink="/profile" routerLinkActive = "active">Profile</a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<ng-container *ngIf="!usersService.loggedIn(); else loggedIn">
<li class="nav-item">
<a class="nav-link" routerLink="/register" routerLinkActive = "active">Signup</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/login" routerLinkActive = "active">Signin</a>
</li>
</ng-container>
<ng-template #loggedIn>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" style="cursor: pointer;"></a>
<div class="dropdown-menu">
<a class="dropdown-item" (click)="usersService.logout()">Logout</a>
</div>
</li>
</ng-template>
</ul>
</div>
</nav>
<div class="container p-5">
<router-outlet></router-outlet>
</div>
I want to put a single user in the navbar from app.component.html and profile.component.html but i don't know how to do it.
Beforehand thank you very much.

The problem is not much clear to me, but I think this may help you.
It is better to create an authentication service. The authentication service is used to login & log out, it notifies other components when the user logs in & out, and allows access the currently logged in user.
RxJS Subjects and Observables are used to store the current user object and notify other components when the user logs in and out of the app. Angular components can subscribe() to the public currentUser: Observable property to be notified of changes, and notifications are sent when the this.currentUserSubject.next() method is called in the login() and logout() methods, passing the argument to each subscriber.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { User } from '#app/_models';
#Injectable({ providedIn: 'root' })
export class AuthenticationService {
private currentUserSubject: BehaviorSubject<User>;
public currentUser: Observable<User>;
apiUrl: string;
constructor(private http: HttpClient) {
this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('currentUser')));
this.currentUser = this.currentUserSubject.asObservable();
}
public get currentUserValue(): User {
return this.currentUserSubject.value;
}
login(username: string, password: string) {
return this.http.post<any>(`${this.apiUrl}/users/authenticate`, { username, password })
.pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
return user;
}));
}
logout() {
// remove user from local storage to log user out
localStorage.removeItem('currentUser');
this.currentUserSubject.next(null);
}
}
you can use the current user like this
currentUser: User;
constructor(
private authenticationService: AuthenticationService
) {
this.authenticationService.currentUser.subscribe(x => this.currentUser = x);
}
if the current user is undefined, you can navigate to again login page. if not you can display the current user.

Related

Can't get the specific user information to be displayed on the page in Angular

I have come across the problem, namely I cannot get the program to display the info of the specific user when clicking on it. The best I could manage is the displaying of every user together. Can you help me with this problem?
Here is the code:
service.ts :
import { Injectable } from '#angular/core';
import {HttpClient} from '#angular/common/http'
import {Observable} from 'rxjs';
# Injectable({providedIn: 'root'})
export class JSONPlaceholderpostsService {
constructor(private http: HttpClient) { }
getData():Observable<any> {
const url = "https://jsonplaceholder.typicode.com/users"
return this.http.get<any>(url)}}
The Component.ts:
import { Component, OnInit } from '#angular/core';
import { JSONPlaceholderpostsService } from 'src/app/posts/jsonplaceholderposts.service';
# Component({
selector: 'app-userinfo',
templateUrl: './userinfo.component.html',
styleUrls: ['./userinfo.component.css']})
export class UserinfoComponent implements OnInit {
data:Array<any>
constructor(private JSONPlaceholder: JSONPlaceholderpostsService,){
this.data = new Array<any>()}
ngOnInit(): void {this.getUserInfoFromAPI()}
getUserInfoFromAPI(){
this.JSONPlaceholder.getData().subscribe((data) => {
console.log(data)this.data = data})}
And the component.html file:
<p>USER INFO</p>
<ul *ngFor="let element of data">
<li><button (click)="getUserInfoFromAPI()">{{element.id}}</button></li>
<li><button (click)="getUserInfoFromAPI()">{{element.name}}</button></li>
<li><button (click)="getUserInfoFromAPI()">{{element.email}}</button></li>
<li><button (click)="getUserInfoFromAPI()">{{element.adress}}</button></li>
</ul>
<button ><a hrer="#" routerLink="/userposts" routerLinkActive="actvie">POSTS</a></button>
Thank you all in advance
what I want to happen is that instead of the list of every user just the specific user info to be displayed.
here how I may make it work
for the route I will use the id instead of the name
{path: 'userinfo/:id', component: UserinfoComponent}
and then for useInfo, I will take the user id from the route and filter the list to to get the user and then use this user
Html
<p>USER INFO</p>
<ul >
<li><button (click)="getUserInfoFromAPI()">{{user.id}}</button></li>
<li><button (click)="getUserInfoFromAPI()">{{user.name}}</button></li>
<li><button (click)="getUserInfoFromAPI()">{{user.email}}</button></li>
<li><button (click)="getUserInfoFromAPI()">{{user.adress}}</button></li>
</ul>
<button ><a hrer="#" routerLink="/userposts" routerLinkActive="actvie">POSTS</a></button>
and ts
import { Component, OnInit } from '#angular/core';
import { JSONPlaceholderpostsService } from '../posts/jsonplaceholderposts.service';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-userinfo',
templateUrl: './userinfo.component.html',
styleUrls: ['./userinfo.component.css'],
})
export class UserinfoComponent implements OnInit {
user: any = '';
constructor(private JSONPlaceholder: JSONPlaceholderpostsService, private route: ActivatedRoute) {
}
ngOnInit(): void {
this.getUserInfoFromAPI();
}
getUserInfoFromAPI() {
this.JSONPlaceholder.getData().subscribe((data) => {
const userID = this.route.snapshot.paramMap.get('id') || "";
[this.user] = data.filter((user) => user.id == userID);
});
}
}

Currently logged in user won’t display on the navbar until I manually refresh the browser

The last previous account name is displayed. It's only when I refresh the browser the displayed name changes to the current logged in account.
below is the Auth service.
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { environment } from 'src/environments/environment';
import { map } from 'rxjs/operators';
import { DefaultUrlSerializer } from '#angular/router';
import { JwtHelperService } from '#auth0/angular-jwt';
import { Observable } from 'rxjs';
import { yearsPerPage } from '#angular/material/datepicker';
#Injectable({
providedIn: 'root'
})
export class AuthService {
baseUrl = environment.apiUrl + 'auth/';
decodedToken: any;
jwtHelper = new JwtHelperService();
constructor(private http: HttpClient) { }
login(model: any) {
return this.http.post(this.baseUrl + 'login', model)
.pipe(
map((response: any) => {
const user = response;
if (user) {
localStorage.setItem('token', user.token);
localStorage.setItem('user', JSON.stringify(user.user));
this.decodedToken = this.jwtHelper.decodeToken(user.token);
localStorage.setItem('role', JSON.stringify(this.decodedToken.role));
}
})
);
}
register(model: any) {
return this.http.post(this.baseUrl + 'register', model, { responseType: 'text' });
}
loggedIn() {
const token = localStorage.getItem('token');
return !this.jwtHelper.isTokenExpired(token);
window.location.reload();
}
role() {
return localStorage.getItem('role');
}
}
I’m getting the logged in details from the local storage and storing the details into a variable called userDisplayName in the navbar component. As you can see below the code is in ngONInit method.
“this.userDisplayName = JSON.parse (localStorage.getItem('user'));”
import { AddTaskComponent } from './../addTask/addTask.component';
import { AuthService } from './../_services/auth.service';
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { MatDialog } from '#angular/material/dialog';
import { UpdateTaskComponent } from '../updateTask/updateTask.component';
import { Router } from '#angular/router';
import { UserMemberService } from '../_services/userMember.service';
import { StateStorageService } from '../_services/stateStorage.service';
#Component({
selector: 'app-navigation-bar',
templateUrl: './navigationBar.component.html',
styleUrls: ['./navigationBar.component.css']
})
export class NavigationBarComponent implements OnInit {
model: any = {};
loginReactiveForm: FormGroup;
role;
users: any[];
userAuthorised: boolean;
searchTask: FormControl;
Username: string;
userDisplayName;
constructor(
private authService: AuthService,
public dialog: MatDialog,
private router: Router,
private userMemberService: UserMemberService,
private stateStorageService: StateStorageService) { }
ngOnInit() {
this.initForm();
this.isUserAdmin();
// this.userDisplayName = this.authService.login(this.model);
this.userDisplayName = JSON.parse (localStorage.getItem('user'));
console.log(this.role);
}
login() {
this.model.username = this.loginReactiveForm.value.username;
this.model.password = this.loginReactiveForm.value.password;
this.authService.login(this.model).subscribe(next => {
this.loadUsers();
this.router.navigateByUrl('/CalendarView');
this.isUserAdmin();
}, error => {
console.log('failed to login');
});
}
isUserAdmin() {
// get users role
this.role = JSON.parse(localStorage.getItem('role'));
console.log('this is role = ' + this.role);
// if user is not an Admin
if (this.role !== 'Admin') {
this.userAuthorised = false;
console.log('value of auth is = ' + this.userAuthorised );
} // if user is an Admin
else {
// list of users for the drop down
this.userAuthorised = true;
console.log('value of auth is = ' + this.userAuthorised );
}
}
loadUsers() {
this.userMemberService.getUsers().subscribe((data) => {
this.users = data;
this.stateStorageService.setUserMemberStorage(this.users);
}, error => {
console.log(error);
});
}
initForm() {
this.loginReactiveForm = new FormGroup({
username: new FormControl(),
password: new FormControl()
});
this.searchTask = new FormControl();
}
// has the user logged in
loggedIn() {
return this.authService.loggedIn();
}
loggedOut() {
const token = localStorage.removeItem('token');
const username = localStorage.removeItem('user');
const userId = localStorage.removeItem('role');
localStorage.removeItem('user');
}
}
Below is the html
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<ul *ngIf="!loggedIn()" class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link">
<span class="sr-only"></span></a>
</li>
</ul>
<form *ngIf="!loggedIn()" class="form-inline my-2 my-lg-0" [formGroup]="loginReactiveForm"
(ngSubmit)="login()">
<input formControlName="username" class="form-control mr-sm-2"
type="text" placeholder="Username" aria-label="username">
<input formControlName="password" class="form-control mr-sm-2"
type="password" placeholder="Password" autocomplete="off" aria-label="password">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Login</button>
</form>
<div *ngIf="loggedIn()" class="username" style="position:relative; left:70%; color: white ;" >Username: {{userDisplayName.username}}</div>
<div style="position:relative; left:85%">
<button *ngIf="loggedIn()" class="btn btn-outline-success float-right"
type="submit" (click)="loggedOut()" [routerLink]="['/login']">Log Out</button>
</div>
</nav>
Why is is that the loggedin user is only displayed once the browser has been refreshed?
I'm currently logged in as “Matt Briggs” but it shows “Sallyjones” on the navbar in the image.
Link to the image is here
Problem
The problem in your code is that you only set userDisplayName once in ngOnInit. NgOnInit is only called once when the component is initialised. So this is why you need to refresh to see the new user from localstorage.
There's no other place where you change or update userDisplayName...
Solution
I think your code could use a little refactoring to make it work like you expect. You're putting to much logic in your component code.
Save your decodedToken in a currentUser subject which is exposed as an observable ==> By doing this, you can subscribe to this observabel in your component's ngOnInit function. This way every change will be shown in your component too.
This is a tutorial which gives a complete overview of how you can implement authentication in Angular. Give it a good read and try this out in your code.
https://jasonwatmore.com/post/2020/07/09/angular-10-jwt-authentication-example-tutorial#authentication-service-ts
this sounds like a change detection problem. Can you use a Subject to store the userDisplayName?
e.g.
export class NavigationBarComponent implements OnInit {
...
userDisplayName = new Subject<{username: string}>;
...
const _u = JSON.parse(localStorage.getItem('user'));
this.userDisplayName.next(_u);
then in your template
<div *ngIf="loggedIn()"
class="username"
style="position:relative; left:70%; color: white ;" >
Username: {{(userDisplayName | aasync)?.username}}
</div>
Using a subject will cause ng to redraw the ui when the value of userDisplayName changes.
I am guessing that ng is drawing the dom on init, when there is an old value in userDisplayName and doesn't know the value has changed. Using a subscribable will fix that.
you can directly call it from your server to html page
get loggedInUser(): any {
const uname = localStorage.getItem('uname');
return uname;
}
In your html page
<li><a *ngIf="auth.isLoggedIncheck"><span class="glyphicon glyphicon-log-in"></span> Welcome{{auth.loggedInUser| uppercase}} </a> </li>

updating routes in navbar

I'm writing a public web page at work, with angular 9, where the array of possible routes depends on the user's role.
I get the role from an HTTP request and the response updates an observable, that will be passed to a child component.
the problem is: the response is received but the template is not updated.
here it is components code:
#Component({
selector: 'navbar',
template: `<nav-template [routesArray]="routesArrayObservable | async"></nav-template>`
})
export class NavbarComponent implements OnInit {
private routesSubject: BehaviorSubject<AppRoute[]>;
routesArrayObservable: Observable<AppRoute[]>;
constructor(private accountService: AccountService) {
this.routesSubject = new BehaviorSubject<AppRoute[]>(null);
this.routesArrayObservable = this.routesSubject.asObservable();
}
ngOnInit(): void {
this.accountService.subscribableRole().subscribe((actualRole: string) => {
this.routesSubject.next(ruoli[actualRole]);
});
}
}
#Component({
selector: 'nav-template',
templateUrl: './navbar.component.html',
})
export class NavbarTemplate implements OnInit, OnChanges{
#Input() routesArray: AppRoute[];
user: Observable<User>;
constructor(private accountService: AccountService){
this.user = null;
}
ngOnInit(): void {
this.user = this.accountService.user;
}
ngOnChanges(changes: SimpleChanges){
for (let propName in changes) {
if (propName === 'routesArray'){
this.routesArray = changes[propName].currentValue;
}
}
}
}
and HTML template:
<nav class="navbar navbar-expand navbar-dark bg-dark" *ngIf="user | async">
<div class="navbar-nav" *ngIf="routesArray.length">
<a *ngFor="let route of routesArray" class="nav-item nav-link"
routerLink="{{ route.routerLink }}">{{ route.name }}</a>
</div>
<div class="nav-account">
<nav-account></nav-account>
</div>
</nav>
resolved: the code in the subscribe() function wasn't in the NgZone.
I'm including another question with more data and explanation.
solution

Angular 8 *ngIf does not reflect the UI properly in the navigation bar

I am trying to integrate bootstrap#4 into an Angular 8 application so to make it fully-responsive.
If the user is not registered (signed up) or is not logged-in, some elements in the nav bar should be hidden (logout btn or link) and if he is logged in others should (login btn or link).
To make the case more clear, below are snippets of code where the header.ts file include a verification whether the user object exists or not. This is done through a service injection in the.ts file constructor. Retrospectively, the service makes a call to a firebase REST API to check for the validity of the authentication.
After a valid authentication, the login btn or link should go away and a logout element should appear instead.
This is partially working. I navigate away from the login interface (form) into another component (services) but the navbar does not update respectively. I still have the same ui before logging in.
Any hints or suggestions how to solve the issue?
Is it because of bootstrap#4? (There are no errors in the console)
Thanks in advance
import { Component, OnInit, OnDestroy } from '#angular/core';
import { AuthService } from 'src/app/auth/auth.service';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit, OnDestroy {
isAuthenticated = false;
private userSub: Subscription;
constructor(private authservice: AuthService) { }
ngOnInit() {
this.userSub = this.authservice.user.subscribe(user =>{
this.isAuthenticated = !!user;
}
);
}
ngOnDestroy(){
this.userSub.unsubscribe();
}
}
<nav class="navbar navbar-expand-md navbar-light bg-light sticky-top">
<div class="container-fluid">
<a class="navbar-brand" href="#"><img src="assets/img/logo.png"></a>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarResponsive">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li>
<a class="nav-link" routerLink="/home" routerLinkActive="active" style="cursor: pointer;">Home</a>
</li>
<li>
<a class="nav-link" routerLink="/about" routerLinkActive="active" style="cursor: pointer;">About</a>
</li>
<li routerLinkActive="active">
<a class="nav-link" routerLink="/services" style="cursor: pointer;">Services</a>
</li>
<li routerLinkActive="active">
<a class="nav-link" routerLink="/team" style="cursor: pointer;">Team</a>
</li>
<li routerLinkActive="active" *ngIf="!isAuthenticated">
<a class="nav-link" routerLink="/auth" style="cursor: pointer;" > Login | Sign up </a>
</li>
<li routerLinkActive="active" *ngIf="isAuthenticated">
<a class="nav-link" routerLink="/auth" style="cursor: pointer;" > Logout </a>
</li>
</ul>
</div>
</div>
</nav>
This is weird.
#kurt Hamilton, below is the sevrvice
import { Component } from '#angular/core';
import { NgForm } from '#angular/forms';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
import { AuthResponseData} from'./auth.service'
import { Router } from '#angular/router';
#Component({
selector: 'app-auth',
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.css']
})
export class AuthComponent {
isLoginMode= true;
error: string = null;
constructor(private authservice: AuthService, private router: Router ){}
onSwitchMode() {
this.isLoginMode = !this.isLoginMode;
}
onSubmit(form: NgForm){
if(!form.valid){
return;
}
const email= form.value.email;
const password= form.value.password;
let authObs : Observable<AuthResponseData>;
if(this.isLoginMode){
authObs = this.authservice.login(email, password);
}else{
authObs = this.authservice.signUp(email, password);
}
authObs.subscribe(
response =>{
console.log(response);
this.router.navigate(['/services']);
},
errorMessage =>{
console.log(errorMessage);
this.error = errorMessage;
}
);
form.reset();
}
}
Thank you very much, but yet I do not get the point. Please let me know if you have a solution! Thanks again.
#Fmerco, I do subscribe to observables in the auth.component.ts (included below)
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import {catchError, tap} from 'rxjs/operators'
import { throwError, Subject } from 'rxjs';
import { User } from './user.model';
export interface AuthResponseData {
idToken: string;
email: string;
refreshToken: string;
expiresIn: string;
localId: string;
registered?: boolean;
}
#Injectable({providedIn: 'root'})
export class AuthService{
user = new Subject <User>();
constructor(private http: HttpClient){}
signUp( email: string , password: string){
return this.http.post<AuthResponseData>(
'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[key-omitted]',
{
email: email,
password: password,
returnSecureToken: true
}
).pipe(catchError (this.handleError), tap(resData =>{
this.handleAuthentication(
resData.email,
resData.localId,
resData.idToken,
+resData.expiresIn);
}));
}
login(email: string, password: string){
return this.http.post<AuthResponseData>(
'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[key-omitted]',
{
email: email,
password: password,
returnSecureToken: true
}
).pipe(catchError (this.handleError));
}
private handleAuthentication(email: string, userId: string, token: string, expiresIn: number){
const expirationDate = new Date(new Date().getTime() + expiresIn * 1000);
const user = new User(
email,
userId,
token,
expirationDate
);
this.user.next(user);
}
private handleError(errorRes: HttpErrorResponse){
let errorMessage = 'An unknown error occured';
if(!errorRes.error || !errorRes.error.error){
return throwError(errorMessage);
}
switch(errorRes.error.error.message){
case 'EMAIL_EXISTS':
errorMessage = 'This email already exists'
break;
case 'EMAIL_NOT_FOUND':
errorMessage = 'This email does not exist'
break;
case 'INVALID_PASSWORD':
errorMessage= 'Incorrect password'
break;
}
return throwError(errorMessage);
}
}

Change detection from child to parent

I am trying to enable functionality in my UI which will display the selections dynamically as they are selected/de-selected.
import { Wizard } from './report-common';
import { Router } from '#angular/router';
import { DataService } from './../shared/service/data.service';
import { TreeNode } from './../shared/dto/TreeNode';
import { Component, OnInit } from '#angular/core';
import { Subject } from 'rxjs/Subject';
import 'rxjs/Rx';
import * as STEPS from '../shared/constants';
import html from './report-builder.component.html';
import css from './report-builder.component.css';
#Component({
selector: 'report-builder',
template: html,
providers: [DataService],
styles: [css]
})
export class ReportBuilderComponent implements OnInit {
selectedProductLine: TreeNode<string>[];
rightside: Wizard = new Wizard([STEPS.PRODUCT_LINE]);
productLineSubject = new Subject<TreeNode<string>[]>();
//this allows the html to access the constants
HTML_STEPS = STEPS;
constructor (private dataService: DataService, private router: Router) {}
ngOnInit() {
this.productLineSubject.subscribe((productline) => this.productLineChange(productline));
}
public productLineChange(productLine: TreeNode<string>[]):void {
this.selectedProductLine = productLine;
this.rightside.setSelection(this.extractDisplayNames(productLine), STEPS.PRODUCT_LINE);
}
private extractDisplayNames <T>(nodes: TreeNode<T>[]): string[] {
return nodes.map(node => node.displayName);
}
}
The html relevant code:
<div *ngFor="let step of rightside.steps">
<li *ngIf="!step.hidden">
<rightside-component class="side-button" [selectionSubject]="step.selections">
</rightside-component>
</li>
</div>
The "Wizard" structure is as follows: (report-common.ts)
import { DataService } from './../shared/service/data.service';
import { TreeNode } from './../shared/dto/TreeNode';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export class WizardStep {
selections: BehaviorSubject<any[]> = new BehaviorSubject<any[]>([]);
}
export class Wizard {
currentComponent:WizardStep;
steps:WizardStep[];
public setSelection(selections:any[], component:string) {
let componentStep = this.steps.find(step => step.component === component);
if(!componentStep) { return; }
componentStep.selections.next(selections);
}
}
Rightside-component.ts:
export class RightSideComponent implements OnInit {
selections: string[];
#Input() selectionSubject: BehaviorSubject<string[]>;
constructor(private cd: ChangeDetectorRef) {}
ngOnInit() {
this.selectionSubject.subscribe((selections) => {
this.selections = selections;
this.cd.detectChanges();
});
}
}
Rightside.component.html :
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div>
<ul class="selection-list">
<li *ngFor="let item of selections">
<button class="btn">
<i class="fa fa-close">
{{item}}
</i>
</button>
</li>
</ul>
</div>
Here, I have added a delete icon in front of every item in the list. Whenever the button is clicked, not only should the item disappear from the list, but it should get de-selected from the original structure that is changing it.
I tried using changedetection here but that didn't work as expected.
I basically want to do something similar to this http://next.plnkr.co/edit/1Fr83XHkY0bWd9IzOwuT?p=preview&utm_source=legacy&utm_medium=worker&utm_campaign=next&preview using Angular 5 and for my data structure. Any ideas on how to go ahead from this point would be appreciated. If any additional code is required, please let me know.
this is easy to do this. Only use Pipe and it would be deal.
First, in your display list (like Rightside.component.html), you did not create selections specify. Use the filter out de-select item. Last, you can set the selected and it would be display dynamic.
Rightside.component.html
<div>
<ul class="selection-list">
<li *ngFor="let item of STEP | unselect_filter">
<!-- I couldn't found your data list , so let STEP instead. -->
<button class="btn">
<i class="fa fa-close">
{{item}}
</i>
</button>
</li>
</ul>
</div>
filter.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'unselect_filter'
})
export class MyFilterPipe implements PipeTransform {
transform(items: any[], filter: Object): any {
return items.filter(item => !item.select);
}
}
I think your object structure of **rightside.steps** is like this
[{
'selections':['xyz','abc'],
....
},
{
'selections':['shm','bsm'],
....
}];
by changing the object structure to the following
[{
'selections':[{
'isSelected': true,
'name':'xyz'
},{
'isSelected': true,
'name':'abc'
}],
....
},
{
'selections':[{
'isSelected': true,
'name':'shm'
},{
'isSelected': true,
'name':'bsm'
}],
....
}]
you can show/hide on the list
<div>
<ul class="selection-list">
<li *ngFor="let item of selections" [hidden]="!item.isSelected">
<button class="btn" (click)="removeItem(item)">
<i class="fa fa-close">
{{item}}
</i>
</button>
</li>
</ul>
</div>
export class RightSideComponent implements OnInit {
selections: string[];
#Input() selectionSubject: BehaviorSubject<string[]>;
constructor(private cd: ChangeDetectorRef) {}
#Output() unSelectItem= new EventEmitter<any>();
removeItem(item) {
item.isSelected = false;
this.unSelectItem.emit(item);
}
}
*ReportBuilderComponent html*
<div *ngFor="let step of rightside.steps">
<li *ngIf="!step.hidden">
<rightside-component (unSelectItem)="unSelectItem(item)" class="side-button" [selectionSubject]="step.selections">
</rightside-component>
</li>
</div>
export class ReportBuilderComponent implements OnInit {
// Your existing code
unSelectItem(item){
for(var step of rightside.steps){
for(var selectionItem of step.selections){
if(selectionItem === item){
selectionItem.isSelected = false;
}
}
}
}
}