Create one navbar and use it on different pages with different style - html

Can I create one navbar and use it on different pages with different style in angular ?
On the first page the دnavbar is transparent and appears after scrolling, but I want it to be white and sticky in the rest of the other pages.

You can check url and add different navigation components based on current route.
constructor(private router: Router ) {
console.log(this.router.url); //route name
}

If you have a navbar in the main component, then you can update its sticky behavior with 2 approaches:
Approach 1 (Preferred way):
Let's say that you already have many pages where you want your navbar to be sticky or transparent. Going to each page & changing some configs is time-consuming.
Instead, you subscribe to Router's NavigationEnd event in app.component.ts
constructor(private router: Router) {}
setNavSticky=true; //Default to true if we have many pages
ngOnInit() {
this.router.events.pipe(
filter(ev=>ev instanceof NavigationEnd)
).subscribe((navEndEvent:NavigationEnd)=>{
if(navEndEvent.url.includes('/home')){
//Add more pages in condition for which navbar should not be sticky
this.setNavSticky=false;
}
else{
this.setNavSticky=true;
}
})
}
Bind the sticky class in the app.component's HTML:
<nav class="navbar" [class.sticky]="setNavSticky">
<header>My main navbar</header>
...
</nav>
Approach 2: Create a utility service at the root level which has the property isNavbarSticky. All the pages that want to set it false, can do so by injecting it in their component file.
With approach 2, you have to manually set/unset isNavbarSticky in pages that don't want sticky navbar.

Related

Angular: Using (click) and routerLink on the same Button

I have a button that redirects to a new page and at the same time should save data to a Service. As I use it now it looks like this:
<button [disabled]="!isValid" (click)="saveToService()" routerLink="/link">Next</button>
Now I wonder if this is best practice. It feels like the html button is somewhat cluttered by so many seperate functionalities. The obvious alternative is to move the router navigation to a function that does both things, as in:
<button [disabled]="!isValid" (click)="saveAndNavigate()">Next</button>
and in ts:
private saveAndNavigate():void { this.service.setData(data); this.router.navigate(['/link]); }
Is there a 'right' way to do this? Are there some unwanted side effects from doing both actions in html?
Thanks
I would suggest you to do it in router promises. So you can:
this.router.navigate(['/link]).then(() => {
this.service.setData(data);
});
I would implement the OnDestroy function in your component, so you can store the data when the component terminates.
Something like this in HTML:
<button [disabled]="!isValid" routerLink="/link">Next</button>
And like this in your component:
import { Component } from '#angular/core';
#Component({...})
export class ThisComponent implements OnDestroy {
ngOnDestroy(){
saveToService()
}
}
If your navigation is performed regardless of the outcome of the service call, then Fatih's answer would work just fine.
On the other hand, and what I've normally seen, is that page navigation should only occur after (successful) completion of the request. If this is the case, I would remove the routerLink directive from your button and keep the (click) function. That function could look like this:
// if your service is making an Http request
public saveToService() {
this.service.saveStuff().pipe(
tap(() => this.router.navigate(['/somewhere']))
)
}
tap simply performs some action without affecting the data stream, so it's perfect for router navigation.

HTML Agular Ionic reload page I route to

When I go to a new routing page in HTML, how do I reload the new page in HTML?
<button ion-button routerLink="/desiredRoute" routerDirection="root">
edit:
I am trying to pass variables from another page.
Page1.ts: variables I want variables from
Page2.ts: variables I want to display from page1
If I load page 2, then go into page1 and change the variables, Page2 variables don't change unless I reload.
Solved:
added this to my .ts
constructor(private route: ActivatedRoute) { }
ngOnInit(): void {
this.route.params.subscribe(
params => {
/*Update Variables here*/
}
);
}
keep in mind, any variables you set in one page, are not going to be available in the second page after you route. you have 2 possible solutions:
1) Use queryParams to pass basic strings or numbers as variables through your route, which will show up on the url and can be parsed. (usually to pass an itemId or something very simple)
2) set variable values using localStorage, and then look them up after routing.

How to prevent flickering when using *ngIf?

In my app.component.html:
<html>
<head></head>
<body>
<header *ngIf="loggedIn && showHeader"></header>
<router-outlet></router-outlet>
</body>
</html>
In my app.component.ts:
export class AppComponent {
constructor(private HeaderService: HeaderService, private AuthService: AuthService) { }
get loggedIn(): boolean { return this.AuthService.getUserState(); }
get showHeader(): boolean { return this.HeaderService.getToggleState(); }
}
In my header.service.ts:
We created this service because there are other specific components after login where the header also needs to be hidden.
#Injectable({
providedIn: 'root'
})
export class HeaderService {
showHeader = true;
constructor() { }
setToggleState(state: boolean) {
this.showHeader = state;
}
getToggleState() { return this.showHeader; }
}
Now, in my login.component, the header should be invisible.
What's happening right now, is that there's a brief moment of flicker (seems to happen when you login and then logout, and return to login page) where the header is visible before it's hidden (and yes, also throws up ExpressionChangedAfterItHasBeenCheckedError).
What's the best way to achieve this? Should I just set showHeader to false by default?
login.component.ts
export class LoginComponent implements OnInit {
ngOnInit() {
// To hide the header
this.HeaderService.setToggleState(false);
}
}
When you use *ngIf, the element is not in the DOM (if the condition is false) and will be placed into the DOM in runtime.
Instead, you could use [hidden]="<your condition>", because then your element will be present in the DOM even though the condition is false. If then your condition changes to true, the elements' opacity will be changed from 0 to 1, which makes the element visible.
Now, in order to get a smoother transition, you can put some CSS on your DOM element like this:
.your-element {
transition: visibility 0.5s;
}
By that you get a hover-like effect.
Also, for your second problem (short flickering of the header before the data is there): This can be explained because you initialize showHeader with true. So it shows up at first, then it suddenly disappears when the service is initialized.
So in that case just set it to false at initialization.
Hope that helps :)
You can simply add
ngOnDestroy(){
this.service.setToggleState(true);
}
in login component.
You can set default value to true in service. then when you need to not display the header in which components then set ngoninit to false and ngondestroy to true to avoid flickering.
see here for example https://stackblitz.com/edit/angular-krit8a

How to render component by adding the component using innerHTML?

I have a component A which only contain a div with an id and a buttons that renders a component inside the div using innterHTML document.getElementById('my-router-outlet').innerHTML = '<app-component-b-page></app-component-b-page>';. But this is not rendering I wonder why?.
I'm trying to avoid using ngIf to be a selector for which component should be rendered for performance reason. Also if I clear the innerHTML does the resources of that component will be cleared?
Okay so a few things here
innerHTML = '<app-component-b-page></app-component-b-page>' is never going to work, angular wont recognise the angular component tag from a innerHTML call
using *ngIf wont affect the performance of the page, so doing the following
<app-component-b-page *ngIf="value === true"></app-component-b-page>
is probably you best option here
If you really don't want to use *ngIf you can use #ViewChild and ComponentFactoryResolver
In your HTML
<!-- this is where your component will be rendered -->
<div #entry></div>
In your component
import { Component, OnInit, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '#angular/core'
import { YourComponent } from ... // import the component you want to inject
// ...
export class ...
#ViewChild('entry', {read: ViewContainerRef, static: true }) entry: ViewContainerRef;
constructor(
private _resolver: ComponentFactoryResolver
) {}
showComponent() {
const factory = this._resolver.resolveComponentFactory(YourComponent);
// this will insert your component onto the page
const component = this.entry.createComponent(factory);
}
// and if you want to dynamically remove the created component you can do this
removeComponent() {
this.entry.clear();
}
You are adding the element to the dom directly and it's not rendered by Angular.
You should go for the *ngIf.

Set URL fragment on browser Back navigation in Angular

Is it possible to customize the back behavior of the Angular router? Specifically, I'd like to add a URL fragment based on the resource the browser is navigating from.
For example, if the browser is on http://example.com/purchases/42, navigating back would take the user to http://example.com/purchases#42 instead of http://example.com/purchases. This is desirable because the /purchases page could be very long, and the URL fragment could position the browser in the context of the previous page.
Is such a thing even possible? Is the only way this could be accomplished is by using the History API, or is there some other API that Angular users for managing navigation state?
Well, lucky for you, in the new Angular 6.1 there is a new feature for routers that you can enable and your router will "remember" your last scroll position when you hit back.
You have to set the routers module like that:
RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled'
})
There problem right now is that its a very new feature, and its will only work for static pages. Thats mean that if you fetching content from service or something, the restoration will try to set it self before you actually have the data, so the position will fail. (Currently, it will also fail even if you are using a resolver)
There is a workaround that we can use right now via the new service called viewportScroller from the #angular/router package, but you will have to do it manully. (currently, it will probably get fixed in the near future).
export class PendingRacesComponent {
scrollPosition: [number, number];
races: Array<RaceModel>;
constructor(route: ActivatedRoute, private router: Router, private viewportScroller: ViewportScroller) {
this.races = route.snapshot.data['races'];
this.router.events.pipe(
filter(e => e instanceof Scroll)
).subscribe(e => {
if ((e as Scroll).position) {
this.scrollPosition = (e as Scroll).position;
} else {
this.scrollPosition = [0, 0];
}
});
}
ngAfterViewInit() {
this.viewportScroller.scrollToPosition(this.scrollPosition);
}
}
This is an example of how you can use it right now, for full explanation you should visit the post