How to show/hide component on button click/hover in angular? - javascript

I'm implementing a toolbar where each route (nav buttons) I hover on will display a popover, similar to bootstrap, containing the component I send (kind of like the navbar items here).
<div *ngFor="let route of toolbarRoutes">
<button mat-stroked-button class="toolbar-nav-button" (click)="routeTo(route.path)">
{{route.displayName}}
</button>
<app-hoverable *ngIf="route.component" [component]="route.component"></app-hoverable>
</div>
My problem is that I want to render/show app-hoverable only if the button (<button mat-stroked-button class="toolbar-nav-button" (click)="routeTo(route.path)">) is hovered, how can i do this?

Simply like this (I have stacked attributes for readability) :
<div *ngFor="let route of toolbarRoutes">
<button mat-stroked-button
class="toolbar-nav-button"
(click)="routeTo(route.path)"
(mouseenter)="route.showHoverable=true"
(mouseleave)="route.showHoverable=false">
{{route.displayName}}
</button>
<app-hoverable *ngIf="route.showHoverable" [component]="route.component"></app-hoverable>
</div>

Related

Material button not displaying properly despite adding theme

I am using material buttons in my angular project
<button mat-button mat-dialog-close>Buy</button>
<button mat-button mat-dialog-close>Cancel</button>
and added theme in custom-theme.scss
#import '~#angular/material/prebuilt-themes/indigo-pink.css';
but only normal HTML buttons got rendered, instead of mat button.
It's happening similarly for some other components as well (mat-form-field).
Any thoughts?

How to pass class through an Angular Custom component

So I have a tooltip custom component that I need to be able to pass a class through to the underlying html, but not sure how to do it. Currently the tooltip (custom angular component) html looks like this:
<div role="tooltip" aria-haspopup="true" class="tooltip tooltip-md tooltip-bottom-right">
<clr-icon class="icon-shape is-solid" shape="info-circle" size="20"></clr-icon>
<div class="tooltip-content">
<ng-content></ng-content>
</div>
</div>
Using this custom component is like:
<tooltip>This is the text displayed.</tooltip>
What I need to be able to do is pass in a position so that the tooltip isn't always at bottom-right. So I need to pass either a property or class or something so that in the custom component I can change the class of my div to "tooltip-top-right", "tooltip-bottom-left", etc... like so:
<tooltip class="tooltip-bottom-left">Tooltip text</tooltip>
or
<tooltip position="bottom-left">Tooltip text</tooltip>
And then inside the component's .ts or .html, assign the appropriate class to my div.
Thanks in advance!
You could use mat-tooltip for this case.
https://material.angular.io/components/tooltip/overview
It has built in matTooltipPosition directive so you wouldnt need to add any class to it.
1) Import the appropriate module in app.module.ts
import {MatTooltipModule} from '#angular/material/tooltip';
2) Use the mat-tooltip selector and the matTooltipPosition input property
<button mat-raised-button
matTooltip="Info about the action"
[matTooltipPosition]='where you want it to be'
aria-label="Button that displays a tooltip when focused or hovered over">
Action
</button>
more examples:
https://material.angular.io/components/tooltip/examples

Angular Materials - can't get multi items to work

I am using Angular 8 with Angular Materials to build a multi-level menu. I can get the menu to work by using recursion for each level. I recursively call the same directive that displays each level of the menu.
This all works, and the menu is built as expected. However, the menu does not behave as expected. Examples I have seen, when you hover over an item, the nest item is opened, and if you move off an item, its child is closed.
For example, this is a simple version I made:
https://stackblitz.com/edit/dynamic-sidenav-multi-level-menu-tvim5b?file=app/app.component.html
Problem
My issue is when I build my menu, if I click on an item, the child opens. However, I can never get the child to close unless I click off the menu altogether. It is not behaving like the above example.
Question
How can I get my example to be have like the above, and close menu items (children) when the items loses focus?
Info
I have not put my specific example in StackBlitz because I don't own the code, and it needs backend services to support the implementation.
I think my issues are because I am building the menu items recursively , and the [matMenuTriggerFor] is referencing the menu in the next recursion.
Code
sidenav-list.component.html
<mat-nav-list>
<!-- Add the Home item -->
<a mat-list-item routerLink="/home" (click)="onSidenavClose()"><mat-icon>home</mat-icon><span class="nav-caption">Home</span></a>
<!-- Recurse over the app-sidenav-item -->
<app-sidenav-item *ngFor="let item of navItems" [item]="item" [depth]="depth+1" [sidenavClose]="sidenavClose"></app-sidenav-item>
</mat-nav-list>
sidenav-item.component.html <app-sidenav-item>
<div>
<button mat-button *ngIf="depth === 1" [matMenuTriggerFor]="menu"><mat-icon>play_arrow</mat-icon>{{item.name | titlecase}}</button>
<button mat-menu-item *ngIf="depth > 1" [matMenuTriggerFor]="menu">{{item.name}}</button>
<mat-menu #menu="matMenu">
<button *ngIf="item.actions.getItems" mat-menu-item (click)="onItemSelected(item, 0)"><mat-icon>list</mat-icon>Get Items</button>
<button *ngIf="item.actions.updateItem" mat-menu-item (click)="onItemSelected(item, 1)"><mat-icon>edit</mat-icon>Update Items</button>
<button *ngIf="item.actions.addItem" mat-menu-item (click)="onItemSelected(item, 2)"><mat-icon>add</mat-icon>Add Item</button>
<app-sidenav-item *ngFor="let child of item.children" [item]="child" [depth]="depth+1" [sidenavClose]="sidenavClose"></app-sidenav-item>
</mat-menu>
</div>
Screen Print
As you can see, I am able to open more than one item on separate nodes. I cannot get the previous one to close. Also, it only responds to clicks and not mouse hover.
Ok here is the thing, You have to preprocess some data 2 way, that means in your object you have to know if it has children to enable more hierarchy level and you need to know which parent it came from to filter it to build this
and your html should look like this. Since you know you can go 3 - 4 levels you generate template for those levels and play with data when it is there.
There is also another #input for MatMenu called matMenuTriggerData with which the parent will trigger data to child.
<button mat-button [matMenuTriggerFor]="level1" [matMenuTriggerData]="getData(null, 1)">Animal index</button>
<mat-menu #level1="matMenu">
<ng-template matMenuContent let-data="data">
<ng-template ngFor let-item [ngForOf]="data">
<button mat-menu-item *ngIf="item.children" [matMenuTriggerFor]="level2"
[matMenuTriggerData]="getData(item, 2)">{{item.label}}</button>
<button mat-menu-item *ngIf="!item.children">{{item.label}}</button>
</ng-template>
</ng-template>
</mat-menu>
<mat-menu #level2="matMenu">
<ng-template matMenuContent let-data="data">
<ng-template ngFor let-item [ngForOf]="data">
<button mat-menu-item *ngIf="item.children" [matMenuTriggerFor]="level3"
[matMenuTriggerData]="getData(item, 3)">{{item.label}}</button>
<button mat-menu-item *ngIf="!item.children">{{item.label}}</button>
</ng-template>
</ng-template>
</mat-menu>
<mat-menu #level3="matMenu">
<ng-template matMenuContent let-data="data">
<button mat-menu-item *ngFor="let item of data">{{item.label}}</button>
</ng-template>
</mat-menu>
Note the last level has no more triggers.
you can write a function for your filtered data
getData(selected, requested) {
return selected ? {
data:
this['level' + requested].filter(item => item.parent === selected.value)
} : { data: this.level1 };
}
Each item will contain value, label, parent and hasChildren in different levels, you can directly hook up with api make sure the object is passed has a data attribute , see functon getData
You can checkout this solution at https://stackblitz.com/edit/angular-yfslub
Hope you can modify to your needs.

Dynamically wrap multiple instances of a component in one parent component

I'm writing a classical modal component in Vuejs made out of a wrapper, a dark overlay ( that covers the whole page behind it ) and the actual content.
The challenge is that I would like to write this
<button>Trigger Modal 1</button>
<Modal :id="modal1">
Some content
</Modal>
<button>Trigger Modal 2</button>
<Modal :id="modal2">
More content
</Modal>
And get the following rendered code
<button>Trigger Modal 1</button>
<button>Trigger Modal 2</button>
<div class="modal-wrapper">
<div id="modal1">Some content</div>
<div id="modal2">More content</div>
</div>
I'm doing this because I don't want to repeat any of the common elements of each modal, since there can be a lot of them on a page.
What I tried so far
On beforeMount check if this is the first Modal component being rendered ( by checking the DOM for .modal-wrapper ) and if it's in there, append the current instance there, if not add create it using document.createElement. But what I get is a .modal-wrapper element for every modal, probably because the beforeMount event get's fired at once for all the elements present at render time.
Any ideas how I can accomplish this?

Ember.js hiding a divider with buttons, but maintaining didInsertElement functionality

I'm trying to show and hide some buttons in Ember.js as follows.
{{#view App.myView contentBinding="App.myObject"}
<div id="my_widget" class="well span3">
{{#if content.isConditionTrue}}
<button id="stop_button" class="btn btn-primary"> Stop </button>
<button id="start_button" class="btn btn-primary" > Start </button>
<button id="record_button" class="btn btn-primary">Record </button>
</div>
{{else}}
<div id="widget_warning_box" class="well">
<p> No cameras are open, open a camera in Add/Remove Cameras tab. </p>
</div>
{{/if}}
</div>
{{/view}}
and the View looks like:
App.myView = Ember.View.Extend({
didInsertElement: function() {
$record = $("#record_button")
$start = $("#start_button")
$stop = $("#stop_button")
$record.click(function(){
console.log("record clicked")
});
$start.click(function(){
console.log("start clicked")
});
});
And the myObject controller is
App.myObject = Ember.Object.create({
isConditionTrue : false;
});
This sort of works (the buttons are replaced by the text if myObject.isConditionTrue is false, and appear when isCondionTrue is true) but when the buttons are displayed, they have no functionality on click. I guess its because they are not in the DOM when didInsertElement is called. So, is there some way to fix this? Should I do child/parent View setup? Any pointers appreciated.
Well I'm definitely not the guy who can tell what's the right way to do this, because I'm just learning ember too, but here is my working approach on fiddle
http://jsfiddle.net/drulia/zkPQt/
Basically every time you switching myObject.isConditionTrue to false, ember removes your buttons from DOM and that's why your jQuery selectors stops working. I attached ember js {{action}} helper and placed functions responding to the events in myObject, now buttons are working and showing action result on the screen.

Categories

Resources