Change detection from child to parent - html

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;
}
}
}
}
}

Related

Best way to implement a pageable to Angular Project

I have products in my database and I have created controller on my backend app that I tested and works really good, so now I need to implement that to my frontend. I have product.component.ts file that looks like this
import { Component, OnInit } from "#angular/core";
import { FormControl, FormGroup } from "#angular/forms";
import { debounceTime, switchMap } from "rxjs";
import { ProductService } from "./product.service";
#Component({
selector: 'app-product',
templateUrl: './product.component.html'
})
export class ProductComponent implements OnInit {
products: any[] = [];
productSearchForm!: FormGroup;
page: number = 0;
size: number = 4;
constructor(
private productService: ProductService
) { }
loadMore() {
this.page = this.page+1;
this.productSearchForm.get('searchTerm')?.valueChanges
.pipe(
debounceTime(500),
switchMap(value => {
return this.productService.searchByTermPageable(value, this.page, this.size);
})
).subscribe(data => {
this.products = data;
}, error => {
console.log(error);
// this.products = [];
});
}
ngOnInit(): void {
this.initializeForm();
this.productSearchForm.get('searchTerm')?.valueChanges
.pipe(
debounceTime(500),
switchMap(value => {
return this.productService.searchByTermPageable(value, this.page, this.size);
})
).subscribe(data => {
this.products = data;
}, error => {
console.log(error);
// this.products = [];
});
}
private initializeForm(): void {
this.productSearchForm = new FormGroup({
searchTerm: new FormControl(null)
});
}
}
searchTerm is a query param that is used to find products with name starting with that term. I call function from file product.service.ts that looks like this
import { HttpClient } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
import { environment } from "src/environments/environment";
#Injectable({ providedIn: 'root'})
export class ProductService {
constructor(private httpClient: HttpClient) { }
searchByTerm(searchTerm: string): Observable<any> {
const url = `${environment.apiUrl}product/search/by-name-or-desc?term=${searchTerm}`;
return this.httpClient.get(url);
}
searchByTermPageable(searchTerm: string, page: number, size: number): Observable<any> {
const url = `${environment.apiUrl}product/search/by-name-or-desc?term=${searchTerm}&page=${page}&size=${size}`;
return this.httpClient.get(url);
}
}
When I click the button load more I want to load next 4 products from database, but keep the first 4 products on my html page, so what is the best way to do this? This is my html page, and also I am using Bulma as my css framework
<div class="mt-5">
<form>
<div class="field" [formGroup]="productSearchForm">
<div class="control has-icons-left">
<input class="input" type="text" formControlName="searchTerm" placeholder="Find products">
<span class="icon is-small is-left">
<i class="fas fa-search"></i>
</span>
</div>
</div>
<hr />
</form>
</div>
<div class="columns">
<div class="column" *ngFor="let product of products">
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-content">
<p class="title is-4">{{ product.name }}</p>
<p class="subtitle is-6">{{ product.category?.name}}</p>
</div>
</div>
<div class="content">
{{ product.description }}
</div>
</div>
</div>
</div>
</div>
<button type="button" class="button is-primary" (click)="loadMore()">Load more...</button>
You can to have a new function in your service that return the elements of the page plus the other elements search. If you has a function that return the paginate elements
searchByTermPageable(search:string,page:number,size:number)
{
...
}
Some like
product:any[]=[] //define an empty array where you store the product
searchOld:string=null //and a variable to store the search
getData(search:string,page:number,size:number)
{
if (search!=this.searchOld) //if is a new search
{
this.searchOld=search
this.product=[]
}
return this.searchByTermPageable(search,page,size).pipe(
map((res:any[])=>[...this.product,...res]), //concat the before store product
tap((res:any[])=>{
this.product=res //store in the product the new array
})
)
}
Allow you some like
<div *ngFor="let product of product$|async">
{{product.name}}
</div>
<button (click)="more()">more</button>
pageIndex:number=-1;
product$:Observable<any[]>
more()
{
this.pageIndex++
this.product$=this.dataService.getData("",this.pageIndex,4)
}
The other solution is search all the product and simple use slice pipe
<div *ngFor="let product of allProduct$|async |slice:0:(index+1)*pageSize">
{{product.name}}
</div>
<button (click)="index=index+1">more</button>
a litle stackblitz

Problem with My Wish List Items Showing Source unknown

I am having a problem showing my wish list items on the page. I'm using Angular 10 and json. Wen I click to add to favorites it color the heart and add it to to my json folder under wishlistitem, but when I route to page to look at the items no products are there. I can tell it hits the the *ngFor because the pipe for the dollar amount for each item appears but no images. When inspecting the source it shows src=unknown.
I have two folders wishlist-list and wishlistitem. I have a service for wishlistitem that is where I think my problem resides. I have included my code.
import { Component, OnInit, Input } from '#angular/core';
import {ProductService} from 'src/app/services/product.service'
import { MessengerService } from 'src/app/services/messenger.service';
import { WishlistService } from 'src/app/services/wishlist.service';
import { WishlistItemService } from '#app/services/wishlist-item.service';
import { Wish} from 'src/app/models/wish';
import {Product} from 'src/app/models/product';
#Component({
selector: 'app-wishlist-list',
templateUrl: './wishlist-list.component.html',
styleUrls: ['./wishlist-list.component.scss']
})
export class WishlistListComponent implements OnInit {
productList: Product[]= [];
wishlistItem: Wish[]= [];
wishItem = []
constructor( private msg: MessengerService,
private productService: ProductService,
private wishlistService: WishlistService,
private _wishlistitemService: WishlistItemService ) { }
ngOnInit(): void {
this.loadWishlistList();
}
loadWishlistList(){
this._wishlistitemService.getWishlistitem().subscribe((items: Wish[]) => {
this.wishItem= items;
this.msg.sendMsg("Is the item being captured" + items)
})
}
}
//Here is my Wishlist-list HTML
<p>wishlist-list works!</p>
<div class="container">
<div class="row">
<div class="col-md-2" *ngFor="let product of wishItem">
<app-wishlistitem [wishitemItem]="product"></app-wishlistitem>
</div>
</div>
</div>
//Here is my wishlist item service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { wishlistitemUrl } from 'src/app/config/api';
import { map } from 'rxjs/operators';
import { ProductItemComponent } from '#app/shopping-cart/product-list/product-
item/product-item.component';
import { Observable, of } from 'rxjs';
import { catchError} from 'rxjs/operators';
import {Product} from 'src/app/models/product';
import {Wish} from 'src/app/models/wish';
#Injectable({
providedIn: 'root'
})
export class WishlistItemService {
product:any
wishlistitemUrl = 'http://localhost:3000/wishlistitem';
constructor(private http: HttpClient) { }
getWishlistitem(): Observable<Wish[]>{
return this.http.get<Wish[]>(wishlistitemUrl)
.pipe(
map((result: any[]) => {
let wishItem: Wish[]= [];
for(let item of result) {
let productExists = false
if (!productExists){
wishItem.push(new Wish(item.id, item.name, item.description,
item.price, item.imageUrl);
}
}
return wishItem;
})
);
}
addProductToWishlistItem(product:Wish):Observable<any>{
return this.http.post(wishlistitemUrl, {product});
}
}
//Here is wishlistitem
import { Component, Input, OnInit } from '#angular/core';
import { ProductService } from 'src/app/services/product.service'
import { WishlistService } from 'src/app/services/wishlist.service';
import { WishlistItemService } from '#app/services/wishlist-item.service';
import { MessengerService } from 'src/app/services/messenger.service';
import { map } from 'rxjs/operators';
import { Wish } from '#app/models/wish';
import { Product } from '#app/models/product';
#Component({
selector: 'app-wishlistitem',
templateUrl: './wishlistitem.component.html',
styleUrls: ['./wishlistitem.component.scss']
})
export class WishlistitemComponent implements OnInit {
#Input() wishitemItem: Wish
#Input() productItem: Product
#Input() product: string
constructor(private wishlistService: WishlistService, private _wishlistitemService:
WishlistItemService, private msg:MessengerService ) { }
ngOnInit(): void {
}
//This function works as expected
handleAddToWishlistitem(){
this._wishlistitemService.addProductToWishlistItem (this.wishitemItem).subscribe(()
=>{
alert("Get wish list item");
this.msg.sendMsg(this.wishitemItem)
})
}
}
//Here is wishlistitem Html
<p>wishlistitem works!</p>
<div class="test">
<div class="container" style="margin:0 auto">
<div class="row no-gutters" style="margin-top: 30px">
<div class="col-4">
<img class="shacker" [src]="wishitemItem.imageUrl" />
<div class="card-body">
<p class="card-text" style="text-align:left; width:130px">
{{wishitemItem.name}}</p>
<p class="card-text" style="text-align:left; width:130px;">
<strong>{{ wishitemItem.price | currency }}</strong>
</p>
<p class="card-text" style="text-align:left; width: 150px">
{{wishitemItem.description | slice: 0:20}}...</p>
</div>
</div>
</div>
</div>
</div>
//I hope the explanation is sufficient. I have tried many scenarios, the issue I'm
having is with the property for the wishlist item, carItem does not have a property
and when I create one the application doesn't behave as expected.
Thank you in advance
PDH

How to make the creation of a card dynamic?

My Problem is that every template created will be Added to the same Card like the image below shows it
And if i set the height rather than auto it will cut the card until the giving measurement.
is there anyone who can help me to make each template have it own card ?
Card.Html
<div class="content" >
<div fxLayout="row wrap" fxLayoutGap="16px grid">
<div [fxFlex]="(100/gridColumns) + '%'" fxFlex.xs="100%" fxFlex.sm="33%" >
<mat-card class="mat-elevation-z4 info" >
<mat-card-content *ngFor="let item of templates" [innerHTML]="item.templatebody | safeTemplate"></mat-card-content> <!-- named one way flow syntax property binding-->
</mat-card>
</div>
</div>
</div>
Card.ts
import { Component, OnInit, ViewChild,Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from "#angular/platform-browser";
import { EmailEditorComponent } from 'angular-email-editor';
import { SenderService } from "../sender.service";
import { TemplateCard } from "src/app/layouts/default/templates/template";
//this pipe is designed to make the HtmlRender Safe using SanitizerDOM
#Pipe({ name: "safeTemplate" })
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(value: any) {
return this.sanitizer.bypassSecurityTrustHtml(value);
}
}
#Component({
selector: 'app-cards',
templateUrl: './cards.component.html',
styleUrls: ['./cards.component.scss']
})
export class CardsComponent implements OnInit {
public templates:TemplateCard[];
public model: any;
options = {
};
gridColumns = 3;
toggleGridColumns() {
this.gridColumns = this.gridColumns === 3 ? 4 : 3;
}
dangerousUrl='<input type="checkbox">';
constructor(public http:SenderService) { }
ngOnInit(): void {
this.model=this.http.getEmail("http://localhost:3000/savetemplate").subscribe( data => {
this.templates=data;
console.log(this.templates);
},
err => console.error(err),
() => console.log('template completed')
);}
}

#viewChild and #ViewChildern gives undefined

I'm working on Angular 9 and want to access an input field after clicking on a button. right now it gives me undefined. I have tried #ViewChild and #viewChildern because I'm using ngIf.
Template.html file
<div class="search-input" #searchDiv *ngIf="serachActive">
<input
#searched
autofocus
type="text"
class="serach-term"
placeholder="Search"
[(ngModel)]="searchTerms"
(ngModelChange)="applySearch()"
/>
<button (click)="toggleSearch(!serachActive)">
<span class="material-icons"> search </span>
</button>
<ul class="search-list">
<li *ngFor="let result of results">
<a [routerLink]="['/', 'video', 'details', result._id]">{{
result.title ? result.title : ''
}}</a>
</li>
</ul>
</div>
Template.ts file
import { Component, OnInit,AfterViewInit,ElementRef,ViewChild,ViewChildren } from '#angular/core';
import { UserService } from '../../../user.service';
import { VideoService } from '../../../services/video.service';
import { Subject } from 'rxjs';
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { Router } from '#angular/router';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css'],
})
export class HeaderComponent implements OnInit,AfterViewInit{
serachActive: boolean = false;
#ViewChildren('searched') searchElement: ElementRef;
#ViewChildren("searched") input: ElementRef;
user;
subject = new Subject<string>();
results = [];
searchTerms;
loggedIn: Boolean = false;
constructor(
private userService: UserService,
private videoService: VideoService,
private router: Router
) {
this.user = this.userService.getUser();
this.loggedIn = this.userService.isAuthenticated();
}
ngOnInit() {
console.log('on init', this.input); //undefined
this.subject
.pipe(debounceTime(400), distinctUntilChanged())
.subscribe((value) => {
this.router.navigate(['search'], { queryParams: { term: value } });
});
}
ngAfterViewInit() {
console.log('on after', this.input); //undefined
}
toggleSearch(toggledata) {
this.serachActive = toggledata;
this.results = [];
this.searchTerms = '';
console.log(this.input) //undefined
console.log(this.searchElement.nativeElement) //undefined
}
applySearch() {
const searchText = this.searchTerms;
this.subject.next(searchText);
this.searchElement.nativeElement.focus(); //undefined
}
menuButtonClick(button){
if(button === "history"){
this.router.navigate(['history'])
}
}
}
Use ViewChild since you're only searching for 1 element ID.
If adding { static: true } or { static: false } in your ViewChild options doesn't work as what is stipulated on Angular Static Query Migration Documentation
Use ChangeDetectorRef instead:
#Component({...})
export class AppComponent {
#ViewChild('searchInput') input: ElementRef;
isShow: boolean = false;
constructor(private cdr: ChangeDetectorRef) {}
toggle(): void {
this.isShow = !this.isShow;
this.cdr.detectChanges(); // Detects changes which this.isShow is responsible on showing / hiding
// the element you're referencing to in ViewChild
if (this.isShow) // If element is shown, console the referenced element
console.log(this.input);
}
}
Have created a Stackblitz Demo for your reference

angular 2 - how to hide nav bar in some components

I am created nav bar separately in nav.component.html ,how to hide nav bar in some components like login.component.
nav.component.html
<nav class="navbar navbar-default navbar-fixed-top navClass">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
(click)="toggleState()">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="collapse navbar-collapse"
[ngClass]="{ 'in': isIn }">
enter code here <ul class="nav navbar-nav">
<li class="active">Home</li>
<li>about</li>
</ul>
</div>
</div>
</nav>
Navbar control and formatting is often needed throughout an app, so a NavbarService is useful. Inject in those components where you need.
navbar.service.ts:
import { Injectable } from '#angular/core';
#Injectable()
export class NavbarService {
visible: boolean;
constructor() { this.visible = false; }
hide() { this.visible = false; }
show() { this.visible = true; }
toggle() { this.visible = !this.visible; }
doSomethingElseUseful() { }
...
}
navbar.component.ts:
import { Component } from '#angular/core';
import { NavbarService } from './navbar.service';
#Component({
moduleId: module.id,
selector: 'sd-navbar',
templateUrl: 'navbar.component.html'
})
export class NavbarComponent {
constructor( public nav: NavbarService ) {}
}
navbar.component.html:
<nav *ngIf="nav.visible">
...
</nav>
example.component.ts:
import { Component, OnInit } from '#angular/core';
import { NavbarService } from './navbar.service';
#Component({
})
export class ExampleComponent implements OnInit {
constructor( public nav: NavbarService ) {}
}
ngOnInit() {
this.nav.show();
this.nav.doSomethingElseUseful();
}
I was able to solve this without using a nav/toolbar service by adding a data object to the route in the route.module. I expanded on Todd Motto's example of adding dynamic titles to a page and added toolbar: false/true to the data object in my path. I then subscribed to the router events in my toolbar.component. Using Todd's event listener func, I read the path object and used the boolean value to set the toolbar visible or not visible.
No service needed and works on pagerefresh.
routing module
...
const routes: Routes = [
{ path: 'welcome', component: WelcomeComponent, data: { title: 'welcome', toolbar: false} }, ...];
toolbar.component
constructor(private router: Router, private activatedRoute: ActivatedRoute, public incallSvc: IncallService) {
this.visible = false; // set toolbar visible to false
}
ngOnInit() {
this.router.events
.pipe(
filter(event => event instanceof NavigationEnd),
map(() => this.activatedRoute),
map(route => {
while (route.firstChild) {
route = route.firstChild;
}
return route;
}),
)
.pipe(
filter(route => route.outlet === 'primary'),
mergeMap(route => route.data),
)
.subscribe(event => {
this.viewedPage = event.title; // title of page
this.showToolbar(event.toolbar); // show the toolbar?
});
}
showToolbar(event) {
if (event === false) {
this.visible = false;
} else if (event === true) {
this.visible = true;
} else {
this.visible = this.visible;
}
}
toolbar.html
<mat-toolbar color="primary" *ngIf="visible">
<mat-toolbar-row>
<span>{{viewedPage | titlecase}}</span>
</mat-toolbar-row>
</mat-toolbar>
Adding to Dan's answer.
One more detail required for a complete answer. Which is registering the NavbarService as a provider for the whole application from app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { SharedModule } from './shared/shared.module';
import { AppComponent } from './app.component';
import { NavbarModule } from './navbar/navbar.module';
import { NavbarService } from './navbar/navbar.service';
import { AppRoutingModule, routedComponents } from './routing.module';
#NgModule({
imports: [
BrowserModule, FormsModule, HttpModule,
NavbarModule,
SharedModule,
AppRoutingModule
],
declarations: [
routedComponents,
],
providers: [
// Here we register the NavbarService
NavbarService
],
bootstrap: [AppComponent]
})
export class AppModule { }
I like this answer by Dan above. However, it does create some update console errors, which I do not want in a production app. I would suggest instead using this method: answer.
It might also be helpful to use a canDeactivate to complete the implementation. Where I was hiding the navbar, such as on login, I added a navigate away 'canDeactive' service:
{ path: 'login', component: LoginComponent, canDeactivate: [NavigateAwayFromLoginDeactivatorService] },
The deactivate service looks like this:
import { Injectable } from '#angular/core';
import { CanDeactivate } from '#angular/router';
import { LoginComponent } from "app/user/login/login.component";
import { NavbarTopService } from "app/navbar-top/navbar-top.service";
#Injectable()
export class NavigateAwayFromLoginDeactivatorService implements CanDeactivate<LoginComponent> {
constructor(public nav: NavbarTopService) { }
canDeactivate(target: LoginComponent) {
this.nav.show();
return true;
}
}
This way, I can hide only on login and do not need to call show() on every other component.
You can use ngIF directive on components where nav is located
<nav *ngIf="this.currentRoute!=='login'" navigation>
</nav>
after you get the current route:
this.router.events.subscribe(event => {
if (event.constructor.name === "NavigationEnd") {
this.name = (<any>event).url.split("/").slice(-1)[0];
this.isLogin = this.currentRoute === 'login';
}
})
Add *ngIf='!showNav' in template
<nav class="navbar navbar-default navbar-fixed-top navClass" *ngIf='!showNav' >
And in LoginComponent
showNav = true;
This will show nav rest of the all the pages , if you want to hide in any pages just put showNav = true; in that component.
How it works :
First for it will check for showNav variable but it will not be available , so it will return false for the other pages where we want to show menu , so need to declare that variable any other pages.
In login page we set the value to true, so it will make it false and hide the nav.
In order for it to work also add "Providers" wherever you're importing the NavbarService
navbar.component.ts and also example.component.ts
#Component({
moduleId: module.id,
selector: 'sd-navbar',
templateUrl: 'navbar.component.html',
providers: [NavbarService ]
})
Another solution to this problem, specially if you are looking to open/close/toggle/ the side nav bar from other controls is to hold a reference to the side nav bar in a service as discussed below:
https://stackoverflow.com/a/48076331/1013544
this worked well for me as I had an application where the side nav was more like the root element and the router components were its content so they would be disabled in the background when the side nav menu is opened.
The linked answer above by JaganY is the best answer if you are hiding a mat-sidenav element. You should never have simple code like this require change detection. Here is an example for other types of elements:
app.componenent.html
<nav #rNav>
<app-rightnav></app-rightnav>
</nav>
app.componenent.ts
#ViewChild('rNav') rNav!: ElementRef;
constructor(public nav: NavbarService) { }
ngAfterViewInit(): void {
this.nav.setRight(this.rNav);
}
navbar.service.ts
import { Injectable, ElementRef } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class NavbarService {
private right!: ElementRef;
private visible!: boolean;
hideR() {
this.visible = false;
this.right.nativeElement.style.display = 'none';
}
showR() {
this.visible = true;
this.right.nativeElement.style.display = 'block';
}
toggleR() { this.visible ? this.hideR() : this.showR(); }
setRight(e: ElementRef) {
this.right = e;
}
}
child-components.html
constructor() {
this.nav.hideR(); // or this.nav.showR();
}