I have a problem. I need to show toastr informing that changes are not saved when someone wants to hide modal. I need to trigger toastr before modal hide, and when the user tries again to dismiss modal allow this. I tried something like this:
declare let jQuery: any;
declare let $: any;
declare let toastr: any;
#Component({
selector: 'app-trigger',
templateUrl: './trigger.component.html',
styleUrls: ['./trigger.component.scss']
})
export class TriggerComponent implements OnInit {
name: string
private canHideModal = true;
constructor() {
}
ngOnInit(){
const self = this;
$('#triggerModal').on('hide.bs.modal', () => {
if (self.canHideModal) {
//hide modal here <---------
} else {
toastr['warning']('You have unsaved changes');
self.canHideModal = true;
return false
}
});
}
fireModal(changes : {action:string, name:string}){
changes.action = 'show';
changes.name = 'test';
this.name = changes.name
$('#triggerModal').modal(changes.action);
}
}
and it works fine for first time, after this hide event seems to be overwriten and function $('#triggerModal').on('hide.bs.modal', () => { doesn't trigger anymore.
HTML:
<div class="modal fade" id="triggerModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="display: none;" aria-hidden="true">
<div class="modal-dialog modal-lg px-4" role="document">
<!--Content-->
<div class="modal-content">
<!--Header-->
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<!--Body-->
<div class="modal-body mb-0">
<!--Grid row-->
<div class="row d-flex justify-content-center mb-4">
<!--Grid column-->
<div class="col-md-6">
<!--Name-->
<div class="md-form">
<input type="text" id="triggerStartName" (input)="canHideModal = false" class="form-control" #triggerName [value]="name">
<label for="triggerStartName" [ngClass]="{ 'active': name }">Trigger name</label>
</div>
</div>
<!--Grid column-->
</div>
<!--Grid row-->
</div>
<!--Footer-->
<div class="modal-footer justify-content-center">
<button type="button" class="btn btn-primary waves-effect waves-light" data-dismiss="modal">Close</button>
</div>
</div>
<!--/.Content-->
</div>
</div>
You can do it with the ng-bootstrap Modal component, by assigning a method to its beforeDismiss option, as illustrated in this plunker:
import {Component} from '#angular/core';
import {NgbModal, ModalDismissReasons} from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'ngbd-modal-basic',
templateUrl: 'src/modal-basic.html'
})
export class NgbdModalBasic {
closeResult: string;
private canHideModal = false;
constructor(private modalService: NgbModal) {}
open(content) {
this.canHideModal = false;
const options : NgbModalOptions = {
beforeDismiss: () => {
if (this.canHideModal) {
return true;
} else {
alert('You have unsaved changes');
this.canHideModal = true;
return false;
}
}
};
this.modalService.open(content, options).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
});
}
...
}
Related
I'm getting this error when I try to press the 'Add' button on a web application I'm building.
core.js:6210 ERROR TypeError: this.service.addDepartment is not a function
at AddEditDepComponent.addDepartment (add-edit-dep.component.ts:25)
at AddEditDepComponent_button_5_Template_button_click_0_listener (add-edit-dep.component.html:10)
at executeListenerWithErrorHandling (core.js:15265)
at wrapListenerIn_markDirtyAndPreventDefault (core.js:15303)
at HTMLButtonElement.<anonymous> (platform-browser.js:582)
at ZoneDelegate.invokeTask (zone-evergreen.js:406)
at Object.onInvokeTask (core.js:28540)
at ZoneDelegate.invokeTask (zone-evergreen.js:405)
at Zone.runTask (zone-evergreen.js:178)
at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:487)
I don't quite know what the problem is. The function addDepartment looks fine to me, but the error is definitely there.
add-edit-dep.component.ts
import { Component, OnInit, Input } from '#angular/core';
import { SharedService } from 'src/app/shared.service';
#Component({
selector: 'app-add-edit-dep',
templateUrl: './add-edit-dep.component.html',
styleUrls: ['./add-edit-dep.component.css']
})
export class AddEditDepComponent implements OnInit {
constructor(private service : SharedService) { }
#Input() dep: any;
DepartmentId!:string;
DepartmentName!:string;
ngOnInit(): void {
this.DepartmentId = this.dep.DepartmentId;
this.DepartmentName = this.dep.DepartmentName;
}
addDepartment() {
var val = {DepartmentId:this.DepartmentId,
DepartmentName:this.DepartmentName};
this.service.addDepartment(val).subscribe((res: { toString: () => any; })=>{
alert(res.toString());
});
}
updateDepartment() {
var val = {DepartmentId:this.DepartmentId,
DepartmentName:this.DepartmentName};
this.service.updateDepartment(val).subscribe((res: { toString: () => any; })=>{
alert(res.toString());
});
}
}
I would like to think it has something to do with 'res' because I was getting an error (res is an implicit any type) and used QuickFix in VS Code to fix it. But the updateDepartment function is basically identical and it is working properly, so I don't know what the problem is.
I've included all the files I worked on today. I'd appreciate any help.
show-dep.component.html
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary float-right m-2"
data-bs-toggle="modal" data-bs-target="#exampleModal"
(click)="addClick()"
data-backdrop="static" data-keyboard="false">
Add Department
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">{{ModalTitle}}</h5>
<button type="button" class="btn-close"
data-bs-dismiss="modal" aria-label="Close"
(click)="closeClick()" >
</button>
</div>
<div class="modal-body">
<app-add-edit-dep
[dep]="dep" *ngIf="ActivateAddEditDepComp">
</app-add-edit-dep>
</div>
</div>
</div>
</div>
<table class = "table table-striped">
<thead>
<tr>
<th>Department ID</th>
<th>Department Name</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<tr *ngFor = "let dataItem of DepartmentList">
<td>{{dataItem.DepartmentId}}</td>
<td>{{dataItem.DepartmentName}}</td>
<td>
<button type="button" class = "btn btn-light mr-1"
data-bs-toggle="modal" data-bs-target="#exampleModal"
(click)="editClick(dataItem)"
data-backdrop="static" data-keyboard="false">
Edit
</button>
<button type="button" class = "btn btn-light mr-1">
Delete
</button>
</td>
</tr>
</tbody>
</table>
add.edit-dep.component.html
<div class = "form-froup row">
<label class = "col-sm-2 col-form-label"> Department Name </label>
<div class = "col-sm-10">
<input type = "text" class = "form-control" [(ngModel)] = "DepartmentName"
placeholder = "Enter department name">
</div>
</div>
<button (click) = "addDepartment()" *ngIf = "dep.DepartmentId == 0" class = "btn btn-primary">
Add
</button>
<button (click) = "updateDepartment()" *ngIf = "dep.DepartmentId != 0" class = "btn btn-primary">
Update
</button>
shared.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SharedService {
[x: string]: any;
readonly APIUrl = "http://localhost:59281/api";
readonly PhotoUrl = "http://localhost:59281/Photos";
constructor(private http:HttpClient) { }
getDepList():Observable<any[]> {
return this.http.get<any>(this.APIUrl + '/Department');
}
getDepartment(val:any) {
return this.http.post(this.APIUrl + '/Department', val);
}
updateDepartment(val:any) {
return this.http.put(this.APIUrl + '/Department', val);
}
deleteDepartment(val:any) {
return this.http.delete(this.APIUrl + '/Department/' + val);
}
getEmpList():Observable<any[]> {
return this.http.get<any>(this.APIUrl + '/Employee');
}
getEmployee(val:any) {
return this.http.post(this.APIUrl + '/Employee', val);
}
updateEmployee(val:any) {
return this.http.put(this.APIUrl + '/Employee', val);
}
deleteEmployee(val:any) {
return this.http.delete(this.APIUrl + '/Employee/' + val);
}
UploadPhoto(val:any) {
return this.http.post(this.APIUrl + 'Employee/SaveFile', val);
}
getAllDepartmentNames():Observable<any[]> {
return this.http.get<any[]>(this.APIUrl + '/Employee/GetAllDepartmentNames');
}
}
There is no addDepartment(val:any) function in your SharedService class, this is one of those cases where error messages point to exactly the right place.
You're calling this.service.addDepartment(val) in the addDepartment() method of the AddEditDepComponent
I have created a modal with css and pure html for my angular application, I try to show it and hide it with the ng-if property and with #input.
but the problem is that I can only open it once and close it once, after closing it it won't open again.
and I don't know what the problem is in my logic
This is my component modal HTML.
<div class="card mymodal z-depth-5" *ngIf="noDisplay">
<form class="form center" (ngSubmit)="enviar(f)" #f="ngForm">
<div>
<h3 class="center">Crear Usuario</h3>
</div>
<!-- nombre -->
<div class="row">
<div class="input-field col s6">
<input id="name" type="text" name="nombre" [(ngModel)]="usuario.nombre" >
<label for="first_name">Nombre</label>
</div>
<!-- nombre -->
<!-- Apellido -->
<div class="input-field col s6">
<input id="name" type="text" [(ngModel)]="usuario.apellido" name="apellido">
<label for="first_name">Apellido</label>
</div>
</div>
<!-- Apellido -->
<!-- Usuario -->
<div class="row">
<div class="input-field col s6">
<input id="name" type="text" [(ngModel)]="usuario.usuario" name="usuario">
<label for="first_name">Nombre de Usuario</label>
</div>
<!-- Usuario -->
<!-- Email -->
<div class="input-field col s6">
<input id="email" type="email" [(ngModel)]="usuario.email" name="email">
<label for="email">Email</label>
</div>
</div>
<!-- Email -->
<!-- Passwor -->
<div class="row">
<div class="input-field col s6">
<input id="password" type="password" [(ngModel)]="usuario.password" name="password">
<label for="password">Password</label>
</div>
<!-- Passwor -->
</div>
<div class="row">
<div class="row">
<div class="input-field col s12">
<button (click)="cerrar()" class="btn waves-effect waves-light right ml-1 mt-2 grey darken-1"
type="submit" name="action">Cancelar
</button>
<button class="btn waves-effect gradient-45deg-light-blue-cyan right mt-2" type="submit" name="action">Guardar
<i class="material-icons right">save</i>
</button>
</div>
</div>
</div>
</form>
</div>
CSS
.mymodal{
position: fixed;
z-index: 2000;
height: 500px;
width: 70%;
background-color: aliceblue;
}
.form{
padding-left: 10%;
padding-right: 10%;
}
TS file
import { Component, OnInit, Output, Input } from '#angular/core';
import { EventEmitter } from 'protractor';
import { AuthService } from 'src/app/services/services.index';
import { NgForm } from '#angular/forms';
import { NuevoUsuario } from 'src/app/models/nuevosUsuario';
#Component({
selector: 'app-modal-form',
templateUrl: './modal-form.component.html',
styleUrls: ['./modal-form.component.css']
})
export class ModalFormComponent implements OnInit {
usuario: NuevoUsuario;
errorMessage: boolean;
#Input() noDisplay: boolean;
constructor(private register: AuthService) { }
ngOnInit(): void {
this.usuario = new NuevoUsuario();
}
enviar(form: NgForm) {
if (form.invalid) {
console.log('Formulario Invalido');
return;
}
this.register.createUser(this.usuario).subscribe(resp => {
console.log('usuario registrado');
}, (err) => {
this.errorMessage = true;
console.log(err.message);
console.log('ocurrio un error');
}
);
}
// close the modal
cerrar() {
this.noDisplay = false;
}
}
and this is where I try to use it
<app-modal-form [noDisplay]="modal" ></app-modal-form>
<div class="row">
<button class="btn gradient-45deg-light-blue-cyan ml-5" (click)="activarModal()">Nuevo
<i class="material-icons right">person_add</i>
</button>
</div>
import { Component, OnInit } from '#angular/core';
import { UsuariosService } from '../../services/services.index';
import { Usuario } from 'src/app/models/usuario';
#Component({
selector: 'app-admin-panel',
templateUrl: './admin-panel.component.html',
styleUrls: ['./admin-panel.component.css']
})
export class AdminPanelComponent implements OnInit {
modal: boolean;
usuariosData: Usuario[] = [];
total: number;
// total_activos: Usuario[] = [];
constructor(private usuario: UsuariosService) { }
ngOnInit(): void {
this.getAllusers();
}
// optiene todos los usuarios
getAllusers() {
this.usuario.getAllUsers().subscribe((data: any) => {
this.usuariosData = data.usuarios;
this.total = this.usuariosData.length;
});
}
// Open the modal
activarModal() {
this.modal = true;
}
}
in your parent component
export class AdminPanelComponent implements OnInit {
// open modal
activarModal() {
this.noDisplay = true;
}
// close modal
onHidePopup() {
this.noDisplay = false;
}
in your popup component
export class ModalFormComponent implements OnInit {
#Input() noDisplay;
// use output to pass event
#Output() onHidePopup = new EventEmitter();
cerrar() {
this.onHidePopup.emit();
}
and in html admin you should pass noDisplay for showing popup and onHidePopup to back the parent
<app-modal-form [noDisplay]="noDisplay" (onHidePopup)="onHidePopup()"></app-modal-form>
After looking at your code again it appears the problem is that you're setting noDisplay to equal the value of modal. But modal is only false when the parent component first loads. You set it to true when you trigger this function:
activarModal() {
this.modal = true;
}
Now, that's going to be leave this.modal as true until the parent component re-mounts. So, logically, that's why you can only open the modal once with your current implementation.
To resolve this, use #Output() someEvent = new EventEmitter(); to pass details back to the parent from the child component, and re-set this.modal to false when that event fires. The event you want to trigger on for the #Output() is this one in your modal component:
// close the modal
cerrar() {
this.noDisplay = false;
}
See this answer for more details on #Output(): Pass Event from child component to parent component Angular 5
I am trying assign unique id to my bootstrap modal as it's being using ngFor. Following is my code.
<div class="container shadow lead-container" data-toggle="modal" [attr.data-target]="customId"> -------------------------> . Data-target is set to customId
<div class="row text-left">
----------------------> Other Content goes here
</div>
</div>
<!--Lead Popup-->
<div class="modal fade" [attr.id]="customId" tabindex="-1" role="dialog" aria-labelledby="exampleModalLongTitle" aria-hidden="true"> -----------> [attr.id] is set to customid
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="container popup-container">
<div class="row text-left">
-------------------------------------> Modal Content Goes Here
</div>
</div>
</div>
</div>
Following is my component.ts:
import { Lead } from './../models/lead';
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-lead',
templateUrl: './lead.component.html',
styleUrls: ['./lead.component.css']
})
export class LeadComponent implements OnInit {
#Input() public lead: Lead;
#Input() public index;
public customId: string;
constructor() {
}
ngOnInit() {
this.customId = "custom".concat(this.index);
}
}
When i click on div. The modal doesnt popup for some reason. Any help would be much appreciated
add "#" to the id.
[attr.data-target]="'#'+customId"
I want to load all companies via AJAX request into a state property when the user clicks on the select box.
This is the code:
import React, { Component } from 'react';
import SelectOption from './SelectOption';
class CreateFreightEntryModal extends Component {
constructor(props) {
super(props);
this.state = {
freights: props.freights,
onClose: props.onClose,
onClick: props.onClick,
companies: [],
};
}
loadCompanies(event) {
$.ajax({
type: "POST",
context:this,
dataType: "json",
async: true,
url: "../data/get/json/companies",
data: ({
_token : window.Laravel.csrfToken,
}),
success: function (data) {
var arr = $.map(data, function(el) { return el; });
this.setState({
companies: arr
})
}
});
}
render() {
return (
<div className="modal fade" id="createFreightEntryModal" tabIndex="-1" role="dialog">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">New freight entry</h4>
</div>
<div className="modal-body">
<div>
<div>
<form onSubmit={this.add.bind(this)}>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<strong>Create a new freight entry:</strong>
</div>
</div>
<div className="row">
<div className="col-xs-4 col-sm-4 col-md-4 col-lg-4">
Company
</div>
<div className="col-xs-8 col-sm-8 col-md-8 col-lg-8">
<div className="form-group" onClick={this.loadCompanies.bind(this)}>
<select className="selectpicker show-tick form-control" data-live-search="true" data-title="Please select" ref="Firma" required>
{
this.state.companies.map((company)=> {
return (
<SelectOption value={company.Nummer} displayValue={company.Bezeichnung} key={company.id} />
);
})
}
</select>
</div>
</div>
</div>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div className="form-group">
<button type="submit" className="btn btn-success"><span className="glyphicon glyphicon-floppy-disk"></span> Save </button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
);
}
}
export default CreateFreightEntryModal
When I add the componentWillReceiveProps(nextProps) method, I get this error.
This error occurs when the page is loaded and not when I click on the select box!
componentWillReceiveProps(nextProps) {
this.setState({
companies: nextProps.companies,
});
}
TypeError: this.state.companies is undefined
This is the part where the error occurs:
...
this.state.companies.map((company)=> {
...
How can I solve this issue? Thanks for your help in advance.
Using this construct:
componentWillReceiveProps(nextProps) {
this.setState({
companies: nextProps.companies,
});
}
you update state.companies every time the component receives ANY props, even when there are no companies in the props. And when the nextProps don't have companies it is set to undefined.
Let's illustrate it this way:
{
let props = { freights : [ 'a', 'b', 'c' ] }
this.setState({ companies : props.companies })
/* state.companies are now undefined, because props.companies are undefined */
}
Fix:
componentWillReceiveProps(nextProps) {
if( nextProps.companies ){
this.setState({
companies: nextProps.companies,
});
}
}
BTW the problem with success callback scope I have mentioned in a comment may still apply.
I have 2 components:
Vue.component('repo-button', {
props:["check_in_id", "repo_id"],
template: '#repo-button',
methods: {
fetchRepo: function() {
url = window.location.href.split("#")[0] + "/check_ins/" + this.check_in_id + "/repositionings/" + this.repo_id + ".json"
cl(url)
cl(this)
var that;
that = this;
$.ajax({
url: url,
success: function(data) {
cl(data)
that.showRepo();
}
})
},
showRepo: function() {
// what do I put here to display the modal
}
},
data: function() {
var that = this;
return {
}
}
});
Vue.component('repo-modal', {
template: "#repo-modal",
data: function() {
return {
status: 'none'
}
}
});
var repositionings = new Vue({
el: "#repo-vue"
});
...and my view consists of a button and a modal. I'd like the button to call fetchRepo on the repo-button component and display the modal (change its status property from none to block.
<script type="text/x-template" id="repo-button">
<div class='socialCircle-item success'>
<i class='fa fa-comment'
#click="fetchRepo"
:data-check_in='check_in_id'
:data-repo='repo_id'>
</i>
</div>
</script>
<script type="text/x-template" id="repo-modal">
<div v-bind:style="{ display: status }" class="modal" id="vue-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-client_id="<%= #client.id %>">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-danger btn-simple" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</script>
<div id="repo-vue">
<div is="repo-modal"></div>
<div is="repo-button" repo_id="<%= ci.repositioning.id %>" check_in_id="<%= ci.id %>"></div>
</div>
Props down, events up
In Vue.js, the parent-child component relationship can be summarized
as props down, events up. The parent passes data down to the child via
props, and the child sends messages to the parent via events.
In particular, if the state of a component needs to be controlled externally (by a parent or sibling), that state should be passed in as a prop from the parent. Events indicate to the parent that the state should be changed.
Your modal's state is controlled by events in itself and in a sibling component. So the state lives in the parent, and is passed to the modal as a prop. Clicking the modal Close button emits a (custom) hidemodal event; clicking the sibling component's comment icon emits a showmodal event. The parent handles those events by setting its showRepoModal data item accordingly.
Vue.component('repo-button', {
template: '#repo-button',
methods: {
showRepo: function() {
this.$emit('showmodal');
}
}
});
Vue.component('repo-modal', {
template: "#repo-modal",
props: ["show"],
computed: {
status() {
return this.show ? 'block' : 'none'
}
},
methods: {
hideRepo() {
this.$emit('hidemodal');
}
}
});
var repositionings = new Vue({
el: "#repo-vue",
data: {
showRepoModal: false
}
});
.socialCircle-item i {
cursor: pointer;
}
<link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<template id="repo-button">
<div class='socialCircle-item success'>
<i class='fa fa-comment'
#click="showRepo"
>
</i>
</div>
</template>
<template id="repo-modal">
<div v-bind:style="{ display: status }" class="modal" id="vue-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" >
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"></h4>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" #click="hideRepo" class="btn btn-danger btn-simple" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</template>
<div id="repo-vue">
<div is="repo-modal" :show="showRepoModal" #hidemodal="showRepoModal = false"></div>
<div is="repo-button" #showmodal="showRepoModal = true"></div>
</div>