I am have an angular 6 project that includes a toolbar on the top of the page with a "Sign In" button. When I click on this button, I want a modal dialog to appear.
However, I ran there are two issues illustrated in the screenshot below. The first is when I load the page, both modals (outlined in red) are loaded. They should only appear if the appropriate button is clicked.
Stackblitz project is at below link:
https://stackblitz.com/github/eugene01a/online-exam/tree/master/frontend
The second issue is why are they not appearing in their own dialog popup?
import {
Component,
OnInit
} from '#angular/core';
import {
ModalService
} from "./_services";
import './_content/app.less';
import './_content/modal.less';
#Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
authenticated = false;
constructor(private modalService: ModalService) {}
ngOnInit() {}
openModal(id: string) {
this.modalService.open(id);
}
closeModal(id: string) {
this.modalService.close(id);
}
}
<!-- main app container -->
<mat-toolbar color="primary" class="mat-elevation-z10">
<button mat-button>Online Exams</button>
<button mat-button>About</button>
<!-- This fills the remaining space of the current row -->
<span class="fill-remaining-space"></span>
<button mat-button (click)="openModal('signin-modal')" *ngIf="!authenticated">Sign In</button>
<button mat-button (click)="openModal('signout-modal')" *ngIf="authenticated">Sign Out</button>
</mat-toolbar>
<div class="view-container">
<router-outlet></router-outlet>
</div>
<jw-modal id="signin-modal">
<h1>Sign in button clicked</h1>
<button (click)="closeModal('signin-modal');">Close</button>
</jw-modal>
<jw-modal id="signout-modal">
<h1>Sign Out button clicked</h1>
<button (click)="closeModal('signout-modal');">Close</button>
</jw-modal>
Related
I was trying to create a search bar component that opens after clicking on a search icon in its parent component, and closes after clicking outside of the search bar. However, after clicking the opening icon it immediately triggered a (document:click) event in the search bar component, which was closing it too early as a result.
I found a workaround for this problem by creating a ‘searchMode’ variable in the search bar component and setting a timeout (in ngOnInit) that set it to true after 100ms. However, I’m wondering, if there’s any better solution to this problem rather than setting the timeout?
(Expected results: user clicks the search icon in navigation component —> search bar component shows up in place of the search icon —> user clicks outside of the search bar component —> the search bar disappears and the search icon shows up again)
navigation.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.scss'],
})
export class NavigationComponent implements OnInit {
searchActive = false;
constructor() {}
ngOnInit(): void {}
openSearchBar(): void {
this.searchActive = true;
}
closeSearchBar() {
this.searchActive = false;
}
}
navigation.component.html
<div class="weather-app__navigation">
<ul class="weather-app__navigation-list">
<li class="weather-app__navigation-list__item">
<i class="fas fa-bars bars-icon"></i>
</li>
<li class="weather-app__navigation-list__item">
<ng-container
*ngIf="searchActive; then searchBarContent; else searchIcon"
></ng-container>
</li>
<ng-template #searchBarContent
><app-search-bar (closeSearch)="closeSearchBar()"></app-search-bar
></ng-template>
<ng-template #searchIcon
><i class="fas fa-search search-icon" (click)="openSearchBar()"></i
></ng-template>
<li class="weather-app__navigation-list__item">
<i class="fas fa-info-circle about-icon"></i>
</li>
</ul>
</div>
search-bar.component.ts
import {
Component,
OnInit,
ElementRef,
ViewChild,
Output,
EventEmitter,
} from '#angular/core';
#Component({
selector: 'app-search-bar',
templateUrl: './search-bar.component.html',
styleUrls: ['./search-bar.component.scss'],
host: {
'(document:click)': 'onClick($event)',
},
})
export class SearchBarComponent implements OnInit {
searchMode = false;
city = '';
#ViewChild('searchBar') searchBar!: ElementRef;
#Output() closeSearch = new EventEmitter();
constructor() {}
ngOnInit(): void {
setTimeout(() => {
this.searchMode = true;
}, 100);
}
onSubmit() {
console.log('submitted');
}
onClick(event: Event) {
if (!this.searchMode) {
return;
}
if (!this.searchBar.nativeElement.contains(event.target)) {
this.closeSearch.emit();
}
}
}
search-bar.component.html
<div class="weather-app__search-bar" #searchBar>
<form class="weather-app__search-bar__form" (ngSubmit)="onSubmit()">
<input type="text" name="city" [(ngModel)]="city" placeholder="City Name" />
<input type="submit" value="Submit" class="btn" />
</form>
</div>
Use event.stopPropogation() on the click event captured on the search button, this will prevent the event from bubbling.
https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
I've created a component with the content of a modal that I want to display on other multiple components.
modal.component.html:
<div class="modal-header">
<h4 class="modal-title"></h4>
<button type="button" class="close" aria-label="Close" (click)="dismissModal()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">...</div>
modal.component.js:
export class ModalComponent implements OnInit {
constructor(
private modal: NgbActiveModal
) { }
dismissModal() {
this.modal.dismiss();
}
}
And I'm calling this content in other parts of my site, such us:
header.component.html:
<button (click)="openModal(myModal)">
Open Modal
</button>
<ng-template #myModal let-modal>
<app-modal-content></app-modal-content>
</ng-template>
header.component.ts:
openModal(content: string) {
this.modalRef = this.modalService.openModal(content, { centered: true });
}
The modal shows up correctly, but when I try to dismiss the modal by pressing X it does not work, because the modal reference is in the header component. How could I dismiss the modal if I'm opening it from other component?
The modal.component.ts file should inject the NgbActiveModal service in order to close the modal.
In the modal.component.ts file constructor:-
import { NgbActiveModal, NgbModal } from '#ng-bootstrap/ng-bootstrap';
export class ModalComponent {
constructor(public modal: NgbActiveModal) {}
}
I´m using ngx-ui from Swimlane: https://swimlane.github.io/ngx-ui/
There is an alert/promp/confirm component that behaves as the browser's native.
The problem is that there is nothing in the docs that says if it is possible to change the label from the buttons.
There is only one example:
alertService.confirm({ title: 'Alert SOC', content: 'Intrusion Happened!!!' })
Does anyone know if it´s possible?
I don't know if it's possible with the alert service, but it looks easy to customize if you use a dialog.
<ng-template #dialogTmpl let-context="context">
<p>Custom dialog</i></p>
<button
type="button"
class="btn">
Custom button text
</button>
</ng-template>
<button
type="button"
class="btn"
(click)="openDialog({ title: 'Alert!', template: dialogTmpl })">
Open Dialog
</button>
And in the .ts:
import { DialogService } from '#swimlane/ngx-ui';
#Component({
selector: 'app',
templateUrl: './app.template.html'
})
export class AppComponent {
#ViewChild('dialogTmpl') dialogTpl: TemplateRef<any>;
constructor(public dialogMngr: DialogService) { }
openDialog(options): void {
this.dialogMngr.create(options);
}
}
I'm trying to show a simple Modal with Ng2-bootstrap as per the following page: https://ng-bootstrap.github.io/#/components/modal
However I am getting errors such as:
Error in ./AppComponent class AppComponent - inline template:7:0 caused by: Missing modal container, add <template ngbModalContainer></template> to one of your application templates.
ORIGINAL EXCEPTION: Missing modal container, add <template ngbModalContainer></template> to one of your application templates.
What am I doing wrong? I've got the following code to try and make this work:
Index.html:
<body>
<app-root>Loading...</app-root>
<template ngbModalContainer></template>
</body>
app.module.ts:
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
...
#NgModule({...
imports: [
BrowserModule,
FormsModule,
HttpModule,
NgbModule.forRoot() ...
app.component.ts:
import { NgbModal, NgbActiveModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
import { SasModalComponent } from './sas-modal/sas-modal.component';
...
constructor(..., private modalService: NgbModal){
}
...
open() {
console.log("trying to open");
const modalRef = this.modalService.open(SasModalComponent);
}
app.component.html
<button class="btn btn-lg btn-outline-primary" (click)="open(content)">Launch demo modal</button>
sas-modal.component.ts
import { NgbModal, NgbActiveModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
...
#Component({
selector: 'sas-modal',
templateUrl: './sas-modal.component.html',
styleUrls: ['./sas-modal.component.css']
})
export class SasModalComponent {
closeResult: string;
constructor(private modalService: NgbModal) {}
open(content) {
this.modalService.open(content);
}
}
sas-modal.component.html:
<template #content let-c="close" let-d="dismiss">
<div class="modal-header">
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
<p>One fine body…</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="c('Close click')">Close</button>
</div>
</template>
sas-component.ts should be : -
import { NgbModal, NgbActiveModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
...
#Component({
selector: 'sas-modal',
templateUrl: './sas-modal.component.html',
styleUrls: ['./sas-modal.component.css']
})
export class SasModalComponent {
closeResult: string;
constructor(private activeModal: NgbActiveModal) {}
}
app.module.ts Should contain SasModuleComponent in the declaration array and a
entryComponent field that contains SasModuleComponent : -
#NgModule({
imports: [ BrowserModule,
FormsModule , HttpModule, NgbModule.forRoot()],
declarations: [ AppComponent, SasModalComponent],
bootstrap: [ AppComponent ],
entryComponents: [SasModalComponent]
})
You need a entryComponent field in #NgModule()in your module file because you are loading the SasModuleComponent dynamically here this.modalService.open(SasModalComponent).
Try adding <template ngbModalContainer></template> in app-root template
Example:
#Component({
selector: 'app-root',
template: '<template ngbModalContainer></template>'
})
saurav1405's answer provided me with enough useful information to figure this out, however his solution didn't 100% work for me.
For this reason I've upvoted saurav1405's response, but couldn't claim is as the answer as I've had to do a bit more to get it to work for me.
The main difference was having a seperate control component and content component.
This is how I got it working:
>> app.module.ts
Note: saurav1405 suggested putting ModalContentComponent into declarations, (possibly a typo after reviewing his comments again).
but you need to put the actual modal custom modal components in here (in this case SasModalComponent & SasModalContent).
And the content component (SasModalContent) needs to be the entry component.
import { SasModalComponent, SasModalContent } from './sas-modal/sas-modal.component';
...
#NgModule({
declarations: [
AppComponent,
SasModalComponent,
SasModalContent
],
entryComponents: [SasModalContent],
...
>> app.component.html:
The ngbModalContainer declaration should be along side the component declaration in the parent component html template, not in the index.html, which I think was the main reason I was getting the errors posted in the question.
<template ngbModalContainer></template>
<sas-modal-component></sas-modal-component>
>> sas-modal.component.ts:
This file combines both the control component [sas-modal-component] (that opens the content / template) and the content component [sas-modal-content] (that has the reference to the active modal and modal template).
import {Component, Input, Compiler } from '#angular/core';
import {NgbModal, NgbActiveModal} from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'sas-modal-content',
templateUrl: 'sas-modal-content.html'
})
export class SasModalContent {
#Input() name;
constructor(public activeModal: NgbActiveModal) { }
}
#Component({
selector: 'sas-modal-component',
templateUrl: 'sas-modal.component.html'
})
export class SasModalComponent {
constructor(private modalService: NgbModal, private compiler: Compiler) { }
open() {
//this.compiler.clearCacheFor(SasModalContent); //Only use when the template is caching when you don't want it to.
const modalRef = this.modalService.open(SasModalContent);
modalRef.componentInstance.name = 'World';
}
}
>> sas-modal.component.html: Contains the button / control to open the modal
<h2> Click the button to open the modal</h2>
<button class="btn btn-lg btn-outline-primary" (click)="open()">Launch demo modal</button>
>> sas-modal-content.html: Holds the modal template - can be whatever you want
<div class="modal-header">
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title">Hi there!</h4>
</div>
<div class="modal-body card-info">
<h3 style="color: white;">So nice to meet you</h3>
<h4 style="color: white;">This modal is great!</h4>
</div>
<div class="modal-footer ">
<button type="button" class="btn btn-secondary" (click)="activeModal.close('Close click')">Close</button>
</div>
My goal is to open a modal in angular when I press a button. I decided to use ng-bootstrap for this. I went to their website and decided to copy paste the default modal code into my application.
The button is showing and when I click it, the screen moves a little bit, but there is no modal to be seen. When I click the button a second time I get a feedback Dismissed by clicking on a backdrop. The console shows me no errors.
Does anyone know how to fix this?
image of what the webpage looks like
homepage HTML file: the ng-template is the modal
<div class="col-md-8 col-md-offset-2">
<h5>Welcome to MovieMeter! <span *ngIf="isLoggedIn"> You are logged in as {{fullName}}</span></h5>
<br>
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title">Modal title</h4>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>One fine body…</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="c('Close click')">Close</button>
</div>
</ng-template>
<button class="btn btn-lg btn-outline-primary" (click)="open(content)">Launch demo modal</button>
<hr>
<pre>{{closeResult}}</pre>
<h3>Trailers:</h3>
<hr>
<ul>
<li *ngFor="let trailer of trailers">
<img src="{{trailer.body.items[0].snippet.thumbnails.high.url}}" alt="nope">
<div class="trailerTitle"><h5>{{trailer.movie.title}}</h5></div>
</li>
</ul>
<app-cinema-featured></app-cinema-featured>
</div>
Home.component.ts
import {Component, OnInit} from "#angular/core";
import {AuthService} from "../auth/auth.service";
import {MovieService} from "../movie/movie.service";
import {NgbModal, ModalDismissReasons} from '#ng-bootstrap/ng-bootstrap';
#Component({
styleUrls: ['./home.component.css'],
selector: 'app-home',
templateUrl: './home.component.html'
})
export class HomeComponent implements OnInit {
isLoggedIn:Boolean;
fullName;
trailers;
closeResult: string;
constructor(private modalService:NgbModal, private authService: AuthService, private movieService: MovieService){}
ngOnInit(){
if (localStorage.getItem('token') !== null || undefined){
this.isLoggedIn = true;
this.fullName = localStorage.getItem('fullName');
}
// get the thumbnails and links of the three most recent movie trailers via the youtube API
this.movieService.getTrailers()
.subscribe(trailers => {
this.trailers = trailers.result;
console.log(this.trailers);
})
}
open(content) {
this.modalService.open(content).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
});
}
private getDismissReason(reason: any): string {
if (reason === ModalDismissReasons.ESC) {
return 'by pressing ESC';
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
return 'by clicking on a backdrop';
} else {
return `with: ${reason}`;
}
}
}
Update: this are the elements in the console after I clicked the button. The ng backdrop and ng-modal are showing, but cannot be seen.
After trying a lot of things I discovered that I needed bootstrap 4 instead of bootstrap 3.
Try to use #ViewChild to select the modal in your component code:
HTML
<button class="btn btn-lg btn-outline-primary" (click)="open()">Launch demo modal</button>
TS
#ViewChild("content") content: NgbModal;
open() {
this.modalService.open(this.content).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
});
}
//1. The modal component has to be registered in entryComponents (angualr 8 and prev)
entryComponents: [
YourModalComponent
],
providers: [
...]
//2. also the model directly inputted in the open:
openConfirmUpdateModal() {
this.modalService.open( YourModalComponent);
}