Toggle routeLink in angular 4 - angular2-routing

I'm creating a web app that will have a side menu loaded from another page in angular 4.
My menu-button with routerLink is defined in here.
<a class="menu-button"routerLink="/menu">
<div class="menu" >
<div class="bar" id="bar-1"></div>
<div class="bar" id="bar-2"></div>
<div class="bar" id="bar-3"></div>
</div>
</a>
The navbar will be visible even when the menu is open, and I was wondering if there's a way in angular 4 to toggle the routerLink from "/menu" to "/home" when the menu is open.

I just had the need to toggle between routes and the app root route. I added that capability with the following directive:
import { Directive, HostListener, Input, Self } from '#angular/core';
import { RouterLink, Router } from '#angular/router';
/**
* This directive adds the ability to toggle a route with Angular's
* {#link RouterLink} directive.
*
* ### Example:
* ```html
* <button routerLink="/my-path" routerLinkToggle></button>
* ```
* The example above creates a toggle button that navigates between
* `http://localhost/my-path` and `http://localhost`.
*/
#Directive({
selector: '[routerLinkToggle]'
})
export class RouterLinkToggleDirective {
/**
* The {#link RouterLink.onClick} method.
*/
private readonly _onClick: () => boolean;
/**
* Navigation commands.
*/
private commands: any[] = [];
constructor(#Self() private routerLink: RouterLink,
private router: Router) {
// Keep a reference to the original `routerLink.onClick` method.
this._onClick = this.routerLink.onClick;
// Replace the `routerLink.onClick` method with a dummy method.
// This is needed because the order in which directives on the
// same host element are executed is undefined. By overwriting
// routerLink's onClick method but keeping a reference to it, we
// now have control over when it will be called.
this.routerLink.onClick = () => true;
}
/**
* Set the path of the route that you want to toggle to. If no path
* is provided, the directive navigates to the root of the app.
*/
#Input()
set routerLinkToggle(commands: any[] | string) {
if (commands != null) {
this.commands = Array.isArray(commands) ? commands : [commands];
} else {
this.commands = [];
}
}
#HostListener('click')
onClick() {
if (this.router.isActive(this.routerLink.urlTree, true)) {
this.router.navigate(this.commands);
} else {
// Call the `_onClick` method within its original `routerLink` conext.
this._onClick.call(this.routerLink);
}
}
}

Not sure if answers your question,
But you may use routerLinkActive directive to check which route is active and based upon that hide or show respective links.
Check below implementation,
#Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1>
<hr />
<a routerLink="/home" [ngClass]="{'hide': !menu.isActive}"
routerLinkActive #home="routerLinkActive" >Home</a>
<a routerLink="/menu" [ngClass]="{'hide': !home.isActive}"
routerLinkActive #menu="routerLinkActive" >Menu</a>
<hr />
<router-outlet></router-outlet>
`,
styles:[
`
.hide{
display: none;
}
`
]
})
class AppComponent { name = 'Angular'; }
Here is the Plunker!!

Related

How can I toggle a class in a LitElement Web Component

I am working with precompiled stylesheet (from SASS) and only need to toggle classes.
I have two elements that will be writing to an event. Based on the event being true/false I want to to toggle a class on my component.
Would this work:
import { LitElement, html } from 'lit-element'
/**
*
*
* #export
* #class MenuMainButton
* #extends {LitElement}
*/
export class MenuMainButton extends LitElement {
static get properties() {
return {
name: { type: String },
toggled: { type: String }
}
}
constructor() {
super()
this.name = 'Menu'
this.toggled = ''
this.addEventListener('toggle-main-menu', this.handleEvents)
}
render() {
return html`
<a #click=${this._onClick} class="menu-button wk-app-menu-button app-menu-open ${this.toggled} govuk-body"
>${this.name}</a
>
`
}
handleEvents(event) {
this.toggled = event.toggle ? 'hide-item' : ''
}
_onClick() {
const toggleMainMenu = new CustomEvent('toggle-main-menu', {
toggle: this.toggled === '' ? 1 : 0
})
this.dispatchEvent(toggleMainMenu)
}
}
window.customElements.define('main-menu-button', MenuMainButton)
One way to make styles dynamic is to add bindings to the class or style attributes in your template.
The lit-html library offers two directives, classMap and styleMap, to conveniently apply classes and styles in HTML templates.
Styles - LitElement

Nextjs - link to '/' without refreshing

// import Link from "next/link";
<Link href="/">Home</Link>
A link to / refreshes the page. Is there anything I can do to stop the page from refreshing, whenever the user navigates to the homepage?
The docs states this: When linking between pages on websites, you use the HTML tag. In Next js you use the Link Component from next/link to wrap the tag. it allows you to do client-side navigation to a different page in the application.
Just do
<Link href="/">
<a>
Home
</a>
</Link>
Use this hook and you'll never have to use Link ever again. Just use regular HTML anchors and they'll work as you would expect.
// useNextClickHandler.ts
import type { Router } from 'next/router'
import { useEffect } from 'react'
/**
* Place this in a Next.js's _app.tsx component to use regular anchor elements
* instead of Next.js's <code>Link</code> component.
*
* #param router - the Next.js router
*/
export default function useNextClickHandler(router: Router): void {
useEffect(() => {
async function onClick(event: MouseEvent) {
// Only handle primary button click
if (event.button !== 0) {
return
}
// Use default handling of modifier+click events
if (
event.metaKey ||
event.ctrlKey ||
event.altKey ||
event.shiftKey
) {
return
}
const anchor = containingAnchor(event.target)
// Only handle anchor clicks
if (!anchor) {
return
}
// Use default handling of target="_blank" anchors
if (anchor.target === '_blank') {
return
}
// If the link is internal, prevent default handling
// and push the address (minus origin) to the router.
if (anchor.href.startsWith(location.origin)) {
event.preventDefault()
await router.push(anchor.href.substr(location.origin.length))
}
}
window.addEventListener('click', onClick)
return () => window.removeEventListener('click', onClick)
}, [router])
}
function containingAnchor(
target: EventTarget | null
): HTMLAnchorElement | undefined {
let parent = target
while (
parent instanceof HTMLElement &&
!(parent instanceof HTMLAnchorElement)
) {
parent = parent.parentElement
}
return parent instanceof HTMLAnchorElement ? parent : undefined
}
// _app.tsx
export default function App({
Component,
pageProps,
router,
}: AppProps): JSX.Element {
useNextClickHandler(router)
Other alternative is by using next/router and utilise onClick event.
import Router from "next/router";
<a onClick={() => Router.push("/")}">
Home
</a>

shadowRoot.activeElement is not working in safari

Currently I am working on stencilJS which has feature to implement shadow dom. I am facing an issue related to activeElement of the shadowRoot.It is working fine with Chrome but when I am testing my component then activeElement is getting null in safari.
Here is the code snippet
import { Component, Prop, Listen } from '#stencil/core';
#Component({
tag: 'my-component',
styleUrl: 'my-component.css',
shadow: true
})
export class MyComponent {
/**
* The first name
*/
#Prop() first: string;
/**
* The middle name
*/
#Prop() middle: string;
/**
* The last name
*/
#Prop() last: string;
#Listen('click')
onHadnleClickEvent(ev) {
console.log('===== 31 =====', ev.target.shadowRoot.activeElement)// getting null in safari
}
render() {
return ( <div>
<button>Click Me!!!</button>
</div>
)
}
}
I found the result to get the clicked element when shadowDom is enabled. Here is the solution:
#Listen('click')
onHadnleClickEvent(ev) {
console.log('===== 31 =====', ev.composedPath()[0]// It will give you the clicked element
}

Navmenu items doesn't change unless it's refreshed it.

I have an angular app that has authentication system. Inside the navmenu, I have login, logout and a text that displays username. It works just fine, however, when the user logged in, I still see the same navbar. login should be gone and logout along with username text should be there. But it's not. They are there only when user refreshes the page. I don't know what I'm missing.
Here is my navmenu.component.html
<li *ngIf="!currentUser"><a [routerLink]="['/login']"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
<li *ngIf="currentUser"><a (click)="logout()"><span class="glyphicon glyphicon-log-out"></span> Logout</a></li>
<li *ngIf="currentUser"><a><span class="glyphicon glyphicon-user"></span> {{ currentUser.firstName }} {{ currentUser.lastName }}</a></li>
And here is my navmenu.component.ts code:
import { Observable } from 'rxjs';
import { User } from './../../models/user';
import { Router } from '#angular/router';
import { AuthService } from './../../services/auth.service';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'nav-menu',
templateUrl: './navmenu.component.html',
styleUrls: ['./navmenu.component.css']
})
export class NavMenuComponent implements OnInit {
currentUser: User;
ngOnInit() {
this.currentUser = JSON.parse(localStorage.getItem('currentUser') || '');
}
constructor(private authService: AuthService, private router: Router) { }
logout() {
this.authService.logout();
this.router.navigate(['/home']);
}
}
Lastly, here is my app.component.html file looks like:
<nav-menu></nav-menu>
<router-outlet></router-outlet>
ngOnInit will be called only once, so I suggest to do the following:
auth.service.ts
// Your code here
authStateChanged: Subject<AuthState> = new Subject<AuthState>();
// Your code here
auth-state.enum.ts
export enum AuthState {
Authorized,
Unauthorized
}
component.ts
Leave everything as is, just change ngOnInit slightly.
ngOnInit() {
this.authService.authStateChanged.subscribe(
authState => { this.refreshCurrentUser(); }
)
}
refreshCurrentUser() {
this.currentUser = JSON.parse(localStorage.getItem('currentUser') || '');
}
You can add more login to check authState, this is just basic example that will work in your case.
Now inside your auth.service when you do logon, on success simply do:
this.authStateChange.next(AuthState.Authorized);
You can use a BehaviorSubject that checks if there is a user in localStorage and also emits changes in the status.
Service:
private user = new BehaviorSubject<any>(JSON.parse(localStorage.getItem('currentUser')))
public user$ = this.user.asObservable();
setUser(user) {
localStorage.setItem('currentUser', JSON.stringify(user))
this.user.next(true)
}
we call setUser() when the status changes. And every subscriber get's the status.
Login:
login() {
this.authService.setUser({user: 'Admin'});
}
and each component that you want to listen to, add this to constructor:
constructor(private authService: Service) {
authService.user$.subscribe(val => {
this.currentUser = JSON.parse(localStorage.getItem('currentUser'))
})
}
and then of course when you want to log out user, do...
logout() {
this.authService.setUser(null)
}

Angular 2 Inheritance components

I have a basewindow.component which will be the base component for all my components. This basewindow.component will be having buttons like save, delete etc and while clicking "New" button I would like to call basewindow function ufbNew() after executing it should execute parent window function ufNew(). Please check my code and help me whether I'm doing it correctly. I'm able to call base function but parent not
//basewindow.component///
import { Component } from '#angular/core';
#Component({
selector: 'basewindow',
templateUrl: './Basewindow.component.html',
styleUrls: ['./Basewindow.component.css']
})
export class BasewindowComponent {
/////////////////////// User Base Functions ///////////////////
ufbNew() {
this.ufNew();
}
ufbSave() {
this.ufSave();
}
/////////////////////// User Functions for parent ///////////////////
ufNew() {
alert("I m In Base ufbNew")
}
ufSave() {
}
}
//// Basewindow.component.html
<div class="container">
<h1>Hero Form</h1>
<form>
<div class="form-group">
<button (click)="ufbNew()">New</button>
<button (click)="ufbSave()">Save</button>
</div>
</form>
</div>
/////////////////////////// AccountsCategory.Component (Parent 1) ////
import { Component } from '#angular/core';
import { BasewindowComponent } from '../base/basewindow.component';
#Component({
selector: 'app',
templateUrl: './AccountsCategory.component.html'
})
export class AccountsCategory extends BasewindowComponent {
/////////////////////// User Functions for parent ///////////////////
ufNew() {
alert("I m in Acounts category (parent)")
}
ufSave() {
}
}
//////////// AccountsCategory.component.html /////////////
<basewindow> </basewindow>
my purpose is to reuse base component objects , functions and override from child if requires.
please see test application in plnkr
https://plnkr.co/edit/bVxt4GjNXwIg7pDbR4sE?p=preview
Use this
abstract class Animal {
//OTHER STUFF
abstract ufSave(): void;
//OTHER STUFF
}

Resources