I am trying to create the modal, which will load on page load in angular 6, its working fine on click method which will pass one argument
I tried with ngOnInit, but its void type not taking the argument
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Select user</h4>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<select >
<option value="volvo">Rohait</option>
<option value="saab">Anuj</option>
</select>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="c('Save click')">Save</button>
</div>
</ng-template>
Launch demo modal
that I running using this
open(content) {
this.modalService.open(content);
}
But its not working here, its working on click method
If you are using Angular 8, first, you can declare this.
#ViewChild('content', { static: true }) content: TemplateRef<any>;
However, for those who are using Angular 7 and below, you should use this instead.
#ViewChild('content') content: TemplateRef<any>;
The above will allow you to access the content template reference variable within your Class.
And on your ngOnInit,
ngOnInit() {
this.modalService.open(this.content);
}
And do remember to import ViewChild, as well as TemplateRef into your component.ts,
import { TemplateRef, ViewChild } from '#angular/core';
This should allow your modalService to open the modal.
Related
I'm using NgbDropdown in my angular application. I have two components CompParent and CompChild. Where compChild holds the HTML for dropdown and it is included into CompParent. I'm trying close all the opened dropdowns in page when a scroll event happens on scrollable-div.
comp-child.component.html:
<div ngbDropdown container="body">
<button class="btn btn-outline-primary btn-sm" ngbDropdownToggle>Actions</button>
<div ngbDropdownMenu>
<button ngbDropdownItem>Edit</button>
<button ngbDropdownItem>Duplicate</button>
<button ngbDropdownItem>Move</button>
<div class="dropdown-divider"></div>
<button ngbDropdownItem>Delete</button>
</div>
</div>
And CompChild is included into CompParent like this:
comp-parent.component.html
<div class="scrollable-div" (scroll)="closeAllDropdowns($event)">
<div class="dropdown-container" *ngFor="let item of items">
<app-comp-child></app-comp-child>
</div>
</div>
What i have tried so far is:
in compParent TS:
export class compParentComponent{
#ViewChild(NgbDropdown) private readonly dropdown: NgbDropdown;
#HostListener('scroll', ['$event'])
closeAllDropdowns(event) {
this.dropdown.close();
}
}
But this.dropdown returns undefined and says close method is not a function associated with it. Also i tried to select all dropdowns using templateRef, that too didn't work.
#ViewChild and #ViewChildren can only query elements from the component itself, not from childs. The possible option is to have a public reference to dropdown inside child, have reference to childs inside parent.
Parent:
export class CompParentComponent{
#ViewChildren(CompChild) compChild!: QueryList<CompChild>;
#HostListener('scroll', ['$event'])
closeAllDropdowns(event) {
this.compChild.forEach(dropdown => dropdown.close());
}
}
Child:
export class CompChildComponent{
#ViewChild(NgbDropdown) public dropdown: NgbDropdown;
}
I am quite new to Angular 5 and have just started learning it.
Recently, I have been trying to create a menu bar with multiple menus for my app using Angular 5 Material.
The menu will be triggered/opened during mouse enter and closed when the mouse leaves the menu.
My problem is that everytime the mouse mouse hovers to the first menu it loads the menu items of the 2nd menu. Here is a screenshot of the problem:
Here are my codes:
mainmenu.component.html:
<div>
<button mat-button [matMenuTriggerFor]="menu1"
(mouseenter)="openMyMenu()">Trigger1</button>
<mat-menu #menu1="matMenu" overlapTrigger="false">
<span (mouseleave)="closeMyMenu()">
<button mat-menu-item>Item 1</button>
<button mat-menu-item>Item 2</button>
</span>
</mat-menu>
</div>
<div>
<button mat-button [matMenuTriggerFor]="menu2"
(mouseenter)="openMyMenu()">Trigger2</button>
<mat-menu #menu2="matMenu" overlapTrigger="false">
<span (mouseleave)="closeMyMenu()">
<button mat-menu-item>Item 3</button>
<button mat-menu-item>Item 4</button>
</span>
</mat-menu>
</div>
mainmenu.component.ts:
import { Component, OnInit, ViewChild } from '#angular/core';
import {MatMenuTrigger} from '#angular/material'
#Component({
selector: 'app-mainmenu',
templateUrl: './mainmenu.component.html',
styleUrls: ['./mainmenu.component.css']
})
export class MainmenuComponent implements OnInit {
#ViewChild(MatMenuTrigger) matMenuTrigger: MatMenuTrigger;
constructor() { }
ngOnInit() {
}
openMyMenu() {
this.matMenuTrigger.openMenu();
}
closeMyMenu() {
this.matMenuTrigger.closeMenu();
}
}
I also tried this: #ViewChild('menu1') matMenuTrigger: MatMenuTrigger;
but I am getting errors.
Your opinions and advices are very much appreciated!
Thanks,
Artanis Zeratul
References:
https://coursetro.com/posts/code/113/How-to-Build-an-Angular-5-Material-App
How to make Material Design Menu (mat-menu) hide on mouseleave
The correct solution for this problem:
#ViewChildren(MatMenuTrigger) trigger: QueryList<MatMenuTrigger>;
//And call:
me.trigger.toArray()[indexOfMenu].openMenu();
I had the same issue.
Create two separate components, each will then contain its own mat-menu and will not affect the other.
<!-- component1 -->
<div>
<button mat-button [matMenuTriggerFor]="menu1"
(mouseenter)="openMyMenu()">Trigger1</button>
<mat-menu #menu1="matMenu" overlapTrigger="false">
<span (mouseleave)="closeMyMenu()">
<button mat-menu-item>Item 1</button>
<button mat-menu-item>Item 2</button>
</span>
</mat-menu>
</div>
<!-- component2 -->
<div>
<button mat-button [matMenuTriggerFor]="menu2"
(mouseenter)="openMyMenu()">Trigger2</button>
<mat-menu #menu2="matMenu" overlapTrigger="false">
<span (mouseleave)="closeMyMenu()">
<button mat-menu-item>Item 3</button>
<button mat-menu-item>Item 4</button>
</span>
</mat-menu>
</div>
I had the same issue and I solved it by using the read metadata property of #ViewChild decorator
mainmenu.component.html
<button #menuBtn1 [matMenuTriggerFor]="menu1">Trigger1</button>
<button #menuBtn2 [matMenuTriggerFor]="menu2">Trigger2</button>
mainmenu.component.ts
#ViewChild('menuBtn1', { read: MatMenuTrigger, static: false}) menu1: MatMenuTrigger;
#ViewChild('menuBtn2', { read: MatMenuTrigger, static: false}) menu2: MatMenuTrigger;
foo() {
this.menu1.openMenu(); // also closeMenu()
this.menu2.openMenu();
}
The trick is to use the template reference menuBtn1 or menuBtn2 and specify through the read property what you want to get that is the MatMenuTrigger directive
NOTE: I saw that the question refers to angular and angular-material 5. I tested it with angular 8 but it should be the same
This issue is related to the element referencing in angular, so you cannot directly use the multiple mat-menu in a single component.
The trick to do is to create a separate component that implements the mat-menu:
Eg,
mat-menu.component.html:
`<div>
<span>
<a (click)="openSelectMenu()">Menu
<mat-icon>arrow_drop_down</mat-icon>
<div #menuTrigger="matMenuTrigger" [matMenuTriggerFor]="menu1"></div>
</a>
<mat-menu #menu1="matMenu" overlapTrigger="false">
<a mat-menu-item *ngFor="let menu of menuItems">{{menu}}</a></mat-menu>
</span>
</div>`
mat-menu.component.ts
`#ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;`
`menuItems=['1', '2', '3'];`
`openSelectMenu() {
this.trigger.openMenu();
}`
Now you can use this component multiple times in any component.
Eg,
app.component.html
`<app-menu></app-menu>
<app-menu></app-menu>`
It will work.
I have two matmenus in my toolbar each one is a separate component and triggers a separate matmenu.
See images below:
Here is my notifications component(component 1 in the image above)
In my editor view :
In my notifications.component.html file :
<button mat-icon-button [matMenuTriggerFor]="notificationsMenu" (mouseover)="openNotifications()">
<mat-icon class="material-icons ele-text-color-grey">notifications</mat-icon>
</button>
<mat-menu #notificationsMenu="matMenu" [overlapTrigger]="false"></mat-menu>
I don't think it is possible to have two in one component but I hope this helps.
<ul class="navbar-nav ml-auto">
<li class="nav-item dropdown">
<button mat-button [matMenuTriggerFor]="admin">ADMIN</button>
<mat-menu #admin="matMenu">
<button mat-menu-item>User Management</button>
</mat-menu>
</li>
<li class="nav-item dropdown">
<button mat-button [matMenuTriggerFor]="profile">PROFILE</button>
<mat-menu #profile="matMenu">
<button mat-menu-item>Change Password</button>
<button mat-menu-item>Logout</button>
</mat-menu>
</li>
I'm making simple application, there are 3 components right now, one Parent component, called MAIN COMPONENT, and two child components, one of the child component is displaying selected vehicles and that works fine, but after selection is done I need to click on add button in my app, to open modal (which is another component) where I should choose a customer/client so I could mark that vehicle's are selected customers ownership.
This is how it looks in general:
So basically when add button is clicked (that add button is part of toolbox component) there should be shown modal (another component which holds customers) where I could choose a client.
Component that should shown when add is pressed is: customer.component.html
Now I will post my code:
1.) posting component that should hold clients customer.component.html
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Clients</h4>
</div>
<div class="modal-body item-edit-container">
<h4 class="modal-title" style="text-align: center;">Find client by ID</h4>
<div class="form-group">
<input type="text" class="form-control dash-form-control" id="" placeholder="" autofocus>
<div style="margin-top: 10px;">
<select class="form-control dash-form-control select2" style="width: 100%;">
<option selected disabled>Search for client..</option>
<option>Person 1</option>
<option>Person 2</option>
<option>Person 3</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn save-keyboard-btn" data-dismiss="modal"></button>
</div>
</div>
#Component({
selector: 'app-customer',
templateUrl: './customer.component.html',
styleUrls: ['./customer.component.css']
})
export class ClientComponent implements OnInit {
ngOnInit() {
}
}
I general I've no idea how this should be done, maybe I should place something like this on my main component :
<app-customer></app-customer> and somehow show it when I click on button add, but I really don't know how to achieve this? Could anyone write me some simple steps which I might follow or whatever...
Thanks guys
Cheers
So please keep in mind my question at the end is simple, I need to show a modal which represeting my component when button is clicked..
Thanks
Cheers
as you said it's simple, in the main component for example you add this:
import {Component, OnInit} from'#angular/core';
export class MainComponent implements OnInit{
public shoud_open = false;
openChildComponent(){
this.should_open = true;
}
itemSelected(item){
console.log(item);
}
}
in the html of the MainComponent you add:
<button (click)="openChildComponent()">Open</button>
<div *ngIf="shouldOpen">
<child-component (onItemSelect)="itemSelected($event)" [items]="persons"></child-component>
</div>
now because you are using bootstrap modal, you need to open it programmatically, i assume that you already using JQuery and it's defined somewhere, so in the ChildComponent you do something like this:
import {Component, AfterViewInit, OutPut, Input, EventEmitter} from'#angular/core';
export class ChildComponent implements AfterViewInit{
#OutPut()
public onItemSelect: EventEmitter<any>;
#Input()
public items: Array<any>;
constructor(){
this.onItemSelect= new EventEmitter<any>();
}
ngAfterViewInit(){
$('#modal_id').modal('show');
}
selectItem(item){
this.onItemSelect.emit(item);
}
}
in the html code of ChildComponent you add this:
<select multiple name="items" (change)="selectItem($event)">
<option *ngFor="let item of items" [value]="item">Item</option>
</select>
i didn't test this code, but i'm just giving you example of how you communicate with components, conditionally display them
I am having some trouble trying to figure out the correct way to bind a value in my slidebar that slides out.
Event.html:
<template>
<require from="./sidebar/event-builder-sidebar"></require>
<require from="./event-item"></require>
<div class="flex-row">
<aside class="event-builder-settings-panel">
<!-- need to bind the single clicked task back to this -->
<event-builder-sidebar containerless data.two-way="?"></event-builder-sidebar>
</aside>
<div class="content-panel">
<div class="eb-actions-row row-flip">
<div class="action-row-buttons">
<button type="button" class="btn btn-sm btn-default"><i class="icon-ion-ios-book"></i> Task Library</button>
<button type="button" class="btn btn-sm btn-default"><i class="icon-ion-plus"></i> Add Task</button>
</div>
</div>
<section class="outer-content outer-content-spacing">
<div class="inner-content-div inner-content-padding">
<ul class="eb event-list">
<!-- Loop happens here -->
<li class="event-item eb-item-created" repeat.for="t of tasks">
<event-item containerless data.two-way="t"></event-item>
</li>
</ul>
</div>
</section>
</div>
</div>
</template>
You see where event-item is being looped. It generates multiple cards and when a user clicks on those cards they get a slidebar (event-builder-slidebar) that slides in from the left and allows the user to edit information in the card. I am currently at a loss for the correct way to do this. I am guessing I would need the event that triggers the slidebar to slide in to pass the current data object back up to the parent event.html and into the event-builder-slidebar. That is where I am at a loss to figure out how to do.
import { bindable, bindingMode } from 'aurelia-framework';
import { CssHelper } from '../../../shared/css-helper';
export class EventItem {
#bindable({ defaultBindingMode: bindingMode.twoWay }) data;
static inject() {
return [CssHelper];
}
constructor(cssHelper) {
this.cssHelper = cssHelper;
this.toggleEdit = e => { this.edit(e); };
}
attached() {
document.addEventListener('click', this.toggleEdit);
}
edit(e) {
// this needs to pass this.data back to event builder sidebar somehow
}
}
Using the containerless attribute is likely gumming things up for you. If you stop using that, you can simply use event binding to bind the custom element's click event to a function on your parent VM that sets a, e.g. selectedEvent property. You can even set it without a VM function directly in the template like this:
<template>
<require from="./sidebar/event-builder-sidebar"></require>
<require from="./event-item"></require>
<div class="flex-row">
<aside class="event-builder-settings-panel">
<!-- bind the single clicked task back to this -->
<event-builder-sidebar containerless data.bind="selectedEvent"></event-builder-sidebar>
</aside>
<div class="content-panel">
<div class="eb-actions-row row-flip">
<div class="action-row-buttons">
<button type="button" class="btn btn-sm btn-default"><i class="icon-ion-ios-book"></i> Task Library</button>
<button type="button" class="btn btn-sm btn-default"><i class="icon-ion-plus"></i> Add Task</button>
</div>
</div>
<section class="outer-content outer-content-spacing">
<div class="inner-content-div inner-content-padding">
<ul class="eb event-list">
<!-- Loop happens here -->
<li class="event-item eb-item-created" repeat.for="t of tasks">
<event-item data.bind="t" click.delegate="selectedEvent = t" ></event-item>
</li>
</ul>
</div>
</section>
</div>
</div>
</template>
However, if you really want to use containerless custom elements, then you'll need to fire the click event as a custom event (and you'll end up having a container element anyways it'll just be a div or something). Here is a gist that shows this in action: https://gist.run/?id=eb9ea1612c97af91104a35b0b5b10430
element vm
import {inject, bindable, containerless} from 'aurelia-framework';
#containerless
#inject(Element)
export class Thing {
#bindable value;
constructor(element) {
this.element = element;
}
fireClick() {
let e = new CustomEvent('click', { bubbles: true });
this.element.dispatchEvent(e);
}
}
element template
<template>
<div click.delegate="fireClick()" style="border: solid red 1px; height: 30px; width: 40px; display: inline-block; margin: 10px; text-align: center;">
${value}
</div>
</template>
For some reason the modal box works just fine but it does not load any templates I specify.
My Controller has this code;
var brnSearchModal = $modal({ scope: $scope, template: "app/rrn/searchBrn.html", contentTemplate: false, html: true, show: false });
$scope.showModal = function () {
brnSearchModal.$promise.then(brnSearchModal.show);
};
My HTML looks like this;
<button data-ng-click="showModal()" type="button" class="btn btn-info" data-animation="am-fade-and-slide-top">
BRN Lookup
</button>
And my template is in a file and looks like this;
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header" ng-show="title">
<button type="button" class="close" ng-click="$hide()">×</button>
<h4 class="modal-title" ng-bind="title">Hello, World!</h4>
</div>
<div class="modal-body" ng-bind="content">Lorem ipsum dolor.</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" ng-click="$hide()">Close</button>
</div>
</div>
</div>
</div>
There is no error everything seems to be working fine but the template does not load. If I debug angular-strap at one point the template is loaded but then disappears in and leaves a blank modal.
Remove ng-show="title", ng-bind="title" from the template.
Try adding the bs-modal attribute: see examples
<button data-ng-click="showModal()" type="button" class="btn btn-info" data-animation="am-fade-and-slide-top" bs-modal="brnSearchModal">
BRN Lookup
</button>
Otherwise use the data-template attribute directly without using the JS to show the modal:
<button type="button" class="btn btn-info" data-animation="am-fade-and-slide-top" data-template="app/rrn/searchBrn.html" bs-modal="modal">
BRN Lookup
</button>
I think when you put bs-modal="modal" modal is somehow pre-defined in the angular-strap library.
you should remove ng-show="title" , ng-show="content" or ng-bind="title" from the template. You should make sure that the template has no ng-show.
I know it is an old question, but in case others are looking for a solution to the same problem. I found I had to explicitly set templateUrl to falsy when setting template to an html string