I am working on an angular project, I've implemented angular material components like the side-nav. The side-nav has ul li elements, where the links are nested and are router links not href. my question when I select a link to go to another view, I want to make a call to side nav to toggle and close. cause it always remains open
Option 1:
You can subscribe to Router events and close sidenav whenever router event takes place.
Code:
requires these imports:
import { Component,ViewChild, OnInit, } from '#angular/core';
import { Router, RouterModule } from '#angular/router';
import { MdSidenav, MdSidenavModule } from '#angular/material';
in component class:
#ViewChild(MdSidenav) sidenav: MdSidenav;
title = 'Tour of Heroes';
constructor(private appService: AppService,
private _router: Router){
}
ngOnInit() {
this._router.events.subscribe(() => {
this.sidenav.close();
});
}
demo
Option 2:
Add a click event to each li to close sidenav on click.
html:
<ul class="sidenav">
<li class="sidenav__list">
<a class="sidenav__list__link " (click)="sidenav.close()" >about</a>
</li>
</ul>
demo 2
Related
I'm trying to decide what menubar to use and I'm testing react-site-nav which I found it to be one the best menubars in this review.
But I need submenus and all content of the submenus is shifted to the left side of the screen making the contents unaccessible.
I tried a simple example like this:
import React, { Component } from "react";
import { Switch, Link, Route } from "react-router-dom";
import SiteNav, { ContentGroup } from "react-site-nav";
import "./styles.css";
export default class App extends Component {
render() {
return (
<div>
<SiteNav>
<ContentGroup title="About" height="200">
<SiteNav>
<ContentGroup title="Test" height="200">
test
</ContentGroup>
</SiteNav>
</ContentGroup>
</SiteNav>
</div>
);
}
}
Here is a test sandbox where you can see the problem: https://codesandbox.io/s/react-site-nav-mwjkef
I am using Angular 12 and I have a button:
<button class="action">Sign In</button>
I need to add a CSS Class when it is being clicked by the user so I can change its style.
The moment it stops being clicked the style should be removed.
I tried with CSS but wasn't able. I think it is not possible.
Is it possible to do this with an Angular directive?
Import ElementRef from the angular core and define in constructor then try the below code:
The below line of code will give you the first occurrence of the button tag from the Component. querySelector gives you the first item and querySelectorAll gives you all items from DOM.
import { Component, ElementRef } from "#angular/core";
constructor(private el: ElementRef) {
}
let myTag = this.el.nativeElement.querySelector("button");
Add Class:
if(!myTag.classList.contains('myClass'))
{
myTag.classList.add('myClass');
}
Remove Class:
myTag.classList.remove('myClass');
I'm currently experimenting with StencilJS to create some web components.
Now I know that there is <slot /> and named slots and all that stuff. Coming from React, I guess slot is similar to children in React. You can do a lot of stuff using children in React. Things I often did:
Check if any children are provided
Iterate over children to do something to each child (e.g. wrap it in a div with a class etc.)
How would you do that using slot/web components/stencilJS?
I can get the Host Element of my web component in Stencil using
#Element() hostElement: HTMLElement;
I use my component like
<my-custom-component>
<button>1</button>
<button>2</button>
<button>3</button>
</my-custom-component>
I want to render something like
render() {
return slottedChildren ?
<span>No Elements</span> :
<ul class="my-custom-component">
slottedChildren.map(child => <li class="my-custom-element>{child}</li>)
</ul>;
}
Kind regards
Using slots you don't need to put a condition in your render function. You can put the no children element (in your example the span) inside the slot element and if no children are provided to the slot it will fall back to it.
For example:
render() {
return (
<div>
<slot><span>no elements</span></slot>
</div>
);
}
Answering the comment you wrote - you can do such a thing but with some coding and not out of the box. Every slot element has an assignedNodes function. Using that knowledge and the understanding of Stencil component life cycle you can do something such as:
import {Component, Element, State} from '#stencil/core';
#Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
#Element() host: HTMLDivElement;
#State() children: Array<any> = [];
componentWillLoad() {
let slotted = this.host.shadowRoot.querySelector('slot') as HTMLSlotElement;
this.children = slotted.assignedNodes().filter((node) => { return node.nodeName !== '#text'; });
}
render() {
return (
<div>
<slot />
<ul>
{this.children.map(child => { return <li innerHTML={child.outerHTML}></li>; })}
</ul>
</div>
);
}
}
This is not an optimal solution and it will require that the style of the slot should have display set to none (cause you don't want to show it).
Also, it will only work with simple elements that only need rendering and not requiring events or anything else (cause it only uses them as html string and not as objects).
Thank you for the answer Gil.
I was thinking of something similar before (setting state etc. - because of timing issues that might come up). I didn't like the solution though, because you're then doing a state change within componentDidLoad, which will trigger another load just after the component did load. This seems dirty and unperfomant.
The little bit with innerHTML={child.outerHTML} helped me alot though.
It seems like you can also simply do:
import {Component, Element, State} from '#stencil/core';
#Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
#Element() host: HTMLDivElement;
render() {
return (
<div>
<ul>
{Array.from(this.host.children)
.map(child => <li innerHTML={child.outerHTML} />)}
</ul>
</div>
);
}
}
I thought you might run into timing issues, because during render() the child elements of the host have already been removed to make space for whatever render() returns. But since shadow-dom and light-dom coexist nicely within the host component, I guess there shouldn't be any issues.
I don't really know why you have to use innerHTML though. Coming from React I'm used to doing:
{Array.from(this.host.children)
.map(child => <li>{child}</li>)}
And I thought that is basic JSX syntax and that since Stencil is also using JSX I could do that, too. Doesn't work though. innerHTML does the trick for me. Thanks again.
EDIT: The timing issues I mentioned will appear if you're not using shadow-dom though. Some strange things start to happen an you'll end up with a lot of duplicate children.
Though you can do (might have side effects):
import {Component, Element, State} from '#stencil/core';
#Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
children: Element[];
#Element() host: HTMLDivElement;
componentWillLoad() {
this.children = Array.from(this.host.children);
this.host.innerHTML = '';
}
render() {
return (
<div>
<ul>
{this.children.map(child => <li innerHTML={child.outerHTML} />)}
</ul>
</div>
);
}
}
How can I add custom button in in picklist header, like, in here?
I'm trying to add button in Primeng picklist header, but it only takes string as a header value.
Is there a way to add any template (HTML) in automatically generated header?
<p-pickList
sourceHeader="Available" targetHeader="Selected>
</p-pickList>
you can extend p-pickList and add the button in your extending template
import { Component, ElementRef, Input, ViewEncapsulation } from '#angular/core';
import { PickList, DomHandler } from 'primeng/primeng';
import { ObjectUtils } from 'primeng/components/utils/objectutils';
#Component({
selector: 'ex-pick-list',
templateUrl: 'pick-list.component.html',
styleUrls: ['pick-list.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class PickListComponent extends PickList {
constructor(el: ElementRef, domHandler: DomHandler, objectUtils: ObjectUtils) {
super(el, domHandler, objectUtils);
}
}
<div class="pick-list-container">
<!-- put the original html from the primeng pick list component "moveRight()" now you can use moveRight from PickList -->
<div class="custom-buttons">
<button pButton type="button" (click)="moveRight()" class="ui-[label]="''"></button>
</div>
</div>
</div>
I am trying to add a class to an element depending on whether the user has clicked on a link. There is a similar question here but it is not working as I wanted it to be.
I created a component which has its own internal data object which has the property, isShownNavigation: false. So when a user clicks on the a I change isShownNavigation: true and expect my css class isClicked to be added. Alas that is not happening - isShownNavigation stays false in the component when I displayed it {{isShownNavigation}} but I can see in the console that my method is working when clicked.
I imported my header component to the App. Code is below.
Header Component
<template>
<header class="header">
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="showNavigation">
Click
</a>
</header>
</template>
<script>
export default {
name: 'header-component',
methods: {
showNavigation: () => {
this.isShowNavigation = !this.isShowNavigation
}
},
data: () => {
return {
isShowNavigation: false
}
}
}
</script>
Application
<template>
<div id="app">
<header-component></header-component>
</div>
</template>
<script>
import HeaderComponent from './components/Header.vue'
export default {
name: 'app',
components: {
'header-component': HeaderComponent
}
}
</script>
I am using the pwa template from https://github.com/vuejs-templates/pwa.
Thanks.
Don't use fat arrow functions to define your methods, data, computed, etc. When you do, this will not be bound to the Vue. Try
export default {
name: 'header-component',
methods: {
showNavigation(){
this.isShowNavigation = !this.isShowNavigation
}
},
data(){
return {
isShowNavigation: false
}
}
}
See VueJS: why is “this” undefined? In this case, you could also really just get rid of the showNavigation method and set that value directly in your template if you wanted to.
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="isShowNavigation = true">
Click
</a>
Finally, if/when you end up with more than one link in your header, you will want to have a clicked property associated with each link, or an active link property instead of one global clicked property.