Aurelia Dialog runs 'then' before dialog returns - aurelia-dialog

I have a view that launches a confirmation dialog but instead of waiting for the dialog to return with a result, the code jumps straight to the 'then' portion of the promise. See code below:
ConfirmDialog.ts
import { inject } from 'aurelia-framework';
import { DialogController } from 'aurelia-dialog';
#inject(DialogController)
export class ConfirmDialog {
private message: string;
private controller: DialogController;
constructor(controller: DialogController) {
this.controller = controller;
}
public activate(message: string) {
this.message = message;
}
}
ConfirmDialog.html
<template>
<div tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" click.trigger="controller.cancel()" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Confirmation</h4>
</div>
<div class="modal-body">
${message}?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" click.trigger="controller.cancel()">No!</button>
<button type="button" class="btn btn-danger" click.trigger="controller.ok()">Yes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</template>
SomeView.ts
import * as moment from 'moment';
import { inject, singleton } from 'aurelia-framework';
import { DialogService } from 'aurelia-dialog';
import { ConfirmDialog } from '../components/modal/confirmDialog';
import { InfoDialog } from '../components/modal/infoDialog';
import { StateStore } from '../common/StateStore';
import { Routing } from '../common/routing';
#singleton()
#inject(Routing, StateStore, DialogService)
export class SomeView {
private routing: Routing;
private commonState: StateStore;
private dialogService: DialogService;
constructor(routing: Routing, stateStore: StateStore, dialogService: DialogService) {
this.routing = routing;
this.commonState = stateStore;
this.dialogService = dialogService;
}
public someButtonClickHandler(someArg: SomeType) {
if (!this.routing.auth.authenticated) {
this.routing.router.navigate('#/login');
}
this.dialogService.open({
viewModel: ConfirmDialog,
model:
'Do you wish to continue'
}).then((response) => {
if (response.wasCancelled) {
return;
}
this.dialogService.open({
viewModel: InfoDialog,
model: 'Why is this happening..'
});
});
}
}
I have omitted the html for the view since it works and all bindings are firing correctly. This used to work, I updated the aurelia-bundler which caused a runtime error so I reverted back to a previous version of the bundler. The runtime error stopped but now it seems that Promise is being short circuited. I even tried checking out the project from version control and this keeps happening. Tried clearing the browser cache in case something was wrong there but no matter what I do, the "why is this happening..." always shows before any interaction can happen with the confirmation dialog. When I click 'ok' on the InfoDialog, the confirmation dialog is there underneath and clicking cancel or OK subsequently does nothing.
Any help would be appreciated.

This is most likely because of breaking changes in the aurelia-dialog between beta and RC.
Try changing this.dialogService.open({...}).then(...) to this.dialogService.open({...}).whenClosed().then(...).
See the release notes for RC-1: https://github.com/aurelia/dialog/releases/tag/1.0.0-rc.1.
There's also a blog post in Aurelia blog: http://blog.aurelia.io/2017/04/27/aurelia-dialog-release-candidate/

Related

Toggle Modal From Another Modal with Javascript

Issue
Toggling a modal with Javascript from another modal creates a conflict with the .modal-backdrop. Solutions I have thought of require me to make some functions to count .modal-backdrop but it breaks Bootstrap's native modal-dismiss behavior. I think maybe there could be better way that incorporates Bootstrap's native CSS (fade, etc.) without additional functions?
Here is my current working example
Ultimate Goal
Create a modal that can toggle another modal while retaining Bootstrap's default CSS. Also eliminate .modal-backdrop from creating more than one instance.
Background
I am creating a "parent" form that can be built dynamically (with JQuery) based on user input. Two sections of the form, "Add System" and "Add Circuit", will require an additional "child" form that will be placed inside a modal, outside the scope of the parent form (This helps avoid nesting one form inside the parent form). The reason I want to separate the user inputs from the parent and child forms is because the child form inputs may have different JQuery validation rules than the parent form (i.e., the data is not required to create a CSD, but if you want to add a system/circuit then I want to make sure you give me all the necessary data to create those objects). If a user wants to Add a system or a circuit:
they click the "Add System/Circuit" button where the 1st modal appears.
The user is prompted with a form to search for an existing one or create a new one.
If the user decides to "Add New System/Circuit", then a different modal appears.
The user is prompted with a form to enter the relevant data.
Here is my javascript function creating the modals. I am generating the modal dynamically based on the option the user chooses. One thing to note, since I am creating these modals in the same function, I want to make sure my function deletes the existing modal content too. (That's why I added the line
if (modalWrap !== null) {
modalWrap.remove()
}
at the beginning of my function)
var modalWrap = null
function create_dynamic_csd_modal(modal_type, section_type) {
if (modalWrap !== null) {
modalWrap.remove()
}
modalWrap = document.createElement("div")
if (modal_type == "new") {
if (section_type == "system") {
var modalContent = `
<div class="modal-header">
<h1 class="modal-title">Add New System</h1>
</div>
<div class="modal-body" style="height: 50vh;">
[...]
</div>
`
} else if (section_type == "circuit") {
var modalContent = `
<div class="modal-header">
<h1 class="modal-title">Add New Circuit</h1>
</div>
<div class="modal-body" style="height: 50vh;">
[...]
</div>
`
}
modalWrap.innerHTML = `
<div class="modal fade csd-add-new-${section_type}" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-xl">
<div class="modal-content">
<form
action='/ajax/csd_add_new_${section_type}/'
method='POST'
class="csd-add-new-${section_type}"
novalidate
>
${modalContent}
<div class="modal-footer">
<button
class="btn btn-warning cancel"
type="reset"
data-bs-dismiss="modal"
>
Cancel
</button>
<button
class="btn btn-secondary previous"
type="button"
>
Previous
</button>
<button
class="btn btn-danger"
type="button"
>
Add
</button>
</div>
</form>
</div>
</div>
</div>
`
} else {
if (section_type == "system") {
var modalContent = `
<div class="modal-body mt-4">
[...]
</div>
`
} else if (section_type == "circuit") {
var modalContent = `
<div class="modal-body mt-4">
[...]
</div>
`
}
modalWrap.innerHTML = `
<div class="modal fade csd-add-existing-${section_type}" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<form
action='/ajax/csd_add_existing_${section_type}/'
method='POST'
class="csd-add-existing-${section_type}"
novalidate
>
${modalContent}
<div class="modal-footer">
<button
class="btn btn-warning cancel"
type="reset"
data-bs-dismiss="modal"
>
Cancel
</button>
<button
class="btn btn-danger"
type="submit"
>
Add
</button>
</div>
</form>
</div>
</div>
</div>
`
}
document.body.append(modalWrap)
var modal = new bootstrap.Modal(modalWrap.querySelector(".modal"))
modal.show()
if ($(".modal-backdrop").length >= 1) {
$(".modal-backdrop").not(":first").remove()
}
}
$(document).on(
"click",
"form.create-csd-series button.add-system, form.create-csd-series button.add-circuit, form.csd-add-existing-system button.add-system, form.csd-add-existing-circuit button.add-circuit",
function(e) {
var button = this
var modal_type = null
if (button.classList.contains("add-system")) {
var section_type = "system"
} else if (button.classList.contains("add-circuit")) {
var section_type = "circuit"
}
if (button.closest(".modal") !== null) {
modal_type = "new"
}
create_dynamic_csd_modal(modal_type, section_type)
},
)
The idea is once the data inside the "child" form is entered and the user clicks "Add" in the modal - the data from this form will populate the parent form in a consolidated view. All the inputs' attributes (like name='some_input_name', id='some_input_id', etc.) from the child form will be transposed into the parent form that match validation rules for it. The user could then edit/delete that data before submitting the parent form to the server. (something like this).

Triggering A modal from within a countdown function

I am trying to get a bootstrap modal to be triggered when my jsonwebtoken is close to expiring.
I am able to get the modal to fire via a button on the nav bar, but I cannot get the modal to trigger from the function.
when i try to trigger the modal using this.openRenew(renew); i get an cannot find name renew error,
**** navbar.html
<!-- Renew Token -->
<ng-template #renew let-modal>
<div class="modal-header">
<h4 class="modal-title" id="renewModal">Renew Log In</h4>
<button type="button" class="close" aria-label="Close
(click)="modal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
Your Session In About To Expire.
For Security Please Confirm Your Password To Continue.
<form>
<div class="form-group">
<label for="password">Password</label>
<input id="password" class="form-control"
placeholder="Password" name="password">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark"
(click)="modal.close('Save click')">Log Back In</button>
</div>
</ng-template>
**** navbar.ts
constructor(
public _auth:AuthService,
public _router:Router,
public modalService: NgbModal) {
this._auth.time.subscribe((now: Date) => {
this.currentTime = now.valueOf();
if(!!this._auth.emToken){
if (!this.timeToRenew || this._auth.emExpTime==null){
console.log('Checking Time to renew',
this._auth.emExpTime*1000-this.currentTime );
if((this._auth.emExpTime*1000)-45000<this.currentTime){
this.timeToRenew = true;
console.log('Time to Log Back In');
/ * Need to trigger openRenew() here *
}
}
}
});
}
openRenew(renew) {
this.modalService.open(renew, {ariaLabelledBy:
'renewModal'}).result.then(
(result) => {
console.log(result);
// validate password
});
}
I've put together a StackBlitz demo to show this working. It should automatically display the modal after around 10 seconds.
There's a couple of changes you'll need to make to your code to get this to work:
i. Make sure you can get a reference to the modal template in your TS file by using the following code to declare the template as a class variable and using #ViewChild to get a reference to it in the HTML:
#ViewChild('renew')
private renew: TemplateRef<any>;
I've modified the logic to make it simpler for the demo - in this example the AuthService fires the time every 5 seconds. The component listens to this and if the timestamp emitted by the AuthService is greater than 10 seconds after the component was created, it displays the modal.
ii. You will need assign the subscription to a variable subscription so that you can then unsubscribe when you open the modal:
// 1. Maintain a reference to the subscription
const subscription: Subscription = this._auth.time.subscribe((now: Date) => {
...
if (/*should renew*/) {
this.openRenew(this.renew);
subscription.unsubscribe(); // 2. Unsubscribe here after opening modal
}
});
This prevents additional modals being displayed on top of the original one every time the AuthService emits a timestamp.

VueJS error interactivity on class and styles

I meet an error on class reactivity and dot not find solution despite a lot of tests.
I modify data with local methods and it work but, application on this DOM (reactivity), does not exceed one. After one modification, the DOM is figed.
I use an Vue extend, i use $set to define value of attribute (because the extend of vue use an return object) or direct access, but the result always the same.
Do you have an idea ?
Thanks in advance.
Jérémy
It's an exemple of code (i use webpack to build my vue app, javascript script separate to html).
<div id="modalMemberEdit" class="modal fade modalmember" v-bind:class="{ 'in':showmodal }" role="dialog" v-bind:style="{'display':displaymodal,'border':bordermodal}">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" v-on:click="hideModal">×</button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-rouge" v-on:click="hideModal" ></button>
</div>
</div>
</div>
</div>
const modalMemberEdit = Vue.extend({
name: "modalMember",
data: function () {
return {
displaymodal: "none",
bordermodal: "15px solid white",
showmodal: false,
}
},
methods: {
showEditModal: function( id ) {
this.$set(this._data,"showmodal",true);
this.$set(this._data,"displaymodal","block");
},
hideModal: function() {
this.$set(this._data,"showmodal",false);
this.$set(this._data,"displaymodal","none");
}
},
mounted: function() {
this.$set(this._data,"showmodal",true);
this.$set(this._data,"displaymodal","block");
}
});

select2 4.0 - TypeError: $(...).select2(...).select2(...) is undefined

Trying to finally change over to the 4.0 version of select2 and running into a problem.
All of this works perfectly fine on 3.5. On button click I open a bootstrap modal and load a remote page into it. I have tested all everything on a normal page (not in a modal) and it works correctly. When loaded in a modal as below the modal opens and everything like it always had for 3.5, but select2 is returning an error. I believe the issue here is the select2 is being loaded from a remote page... thing is this same remote loading method always worked flawlessly with 3.5.
TypeError: $(...).select2(...).select2(...) is undefined
js:
// show edit modal
$('#datatable2').on('click', '.dtEdit', function () {
var data = {
'filter_id': $(this).parents('tr').attr('id').replace('dtrow_', '')
};
$('#modal-ajax').load(
'/modals/m_filtering_applications_filters.php',
data,
function() {
$(this).modal('show');
changeSettings();
hourSelection();
}
);
});
// change filter modal confirmation
var changeSettings = function() {
// get the default filter
var default_filter = $("#filter_default").val();
//app list
$("#vedit-filter").select2({
placeholder: {
id: default_filter, // or whatever the placeholder value is
text: default_filter // the text to display as the placeholder
},
allowClear: true,
multiple: false,
tags: true,
createTag: function (query) {
return {
id: query.term,
text: query.term,
tag: true
}
},
ajax: {
dataType: 'json',
delay: 500,
type: 'post',
url: '/process/get_application_list.php',
data: function (params) {
return {
term: params.term, // search term
page: params.page, //page number
page_limit: 25, // page size
};
},
results: function (data, page) {
var more = (page * 25) < data.total; // whether or not there are more results available
return {
results: data.results,
more: more
};
}
}
}).select2('val', [default_filter]).on('change', function() {
$(this).valid();
});
}
m_filtering_applications_filters.php :
Just the contents of the modal which is loaded in :
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"></button>
<h3 class="modal-title">Change these settings?</h3>
</div>
<form id="application-filters-edit">
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<div class="row">
<div class="col-md-12 margin-bottom-30 form-group">
<div class="input-modal-group">
<label for="vedit-filter" class="f-14"><b>Application to filter :</b></label>
<select id="vedit-filter" name="settings[filter]" class="form-control select2">
<option value="<?php echo htmlspecialchars($result['filter'], ENT_QUOTES, 'UTF-8'); ?>" selected=""><?php echo htmlspecialchars($result['filter'], ENT_QUOTES, 'UTF-8'); ?></option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<input type="hidden" name="settings[users][]" value="<?php echo $result['user_id']; ?>"/>
<input id="filter_default" type="hidden" name="settings[original]" value="<?php echo htmlspecialchars($result['filter'], ENT_QUOTES, 'UTF-8'); ?>"/>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<?php
if (!$result)
{
//disable the button
echo '<button type="button" class="btn btn-primary disabled" data-dismiss="modal"><i class="fa fa-check-circle"></i> Save Settings</button>';
}
else
{
// show the button
echo '<button class="btn btn-primary" type="submit"><i class="fa fa-check-circle"></i> Save Settings</button>';
}
?>
</div>
</form>
</div>
</div>
UPDATE:
Okay, the error is coming from :
}).select2('val', [default_filter]).on('change', function() {
$(this).valid();
});
attached to the end... this is for using the jquery validation script (I did not include this in the jS here), and again, worked fine in 3.5. Can you not add on to the select2() anymore with 4.0 or something?
When removing this line the error goes away, but the display of the select2 is very small in width and I cannot gain focus on the search box to enter any values so it is still unusable within the modal.
UPDATE2:
Realized the events changed with 4.0 so this seems to work :
}).on("change", function (e) {
$(this).valid();
});
Still cannot enter anything into the search box though. Also, I notice if I click on anything other than the arrow in the input box to show the dropdown it acts as if the mouse is being held down - it highlights all the text/content within the modal.
Solution : All the issues I was having with the select2 in my bs3 modal were solved by adding the following in my js :
$.fn.modal.Constructor.prototype.enforceFocus = $.noop;
The highlighting of content/text is gone. The focus on the search box is gone. For now everything seems to work great. I do not have any information as to what this line actually does at the moment, but will be researching this to find out and will add it back here for future reference for anyone.

grails angularjs - modal not working as expected

I want to show modal(encouragementModal) only when user clicks on Encourage button.
Configuration
GSP View _encourage.gsp is :
<div ng-controller="EncouragementController">
<g:if test="${notEncouraged}">
<button class="btn" ng-click="openEncouragementModal()">Encourage</button>
<div modal="encouragementModal" close="closeEncouragementModal()" options="opts">
<div class="modal-header">
<h3>Encourage!</h3>
</div>
<div class="modal-body">
You are about to encourage with {{amount}}.
</div>
<div class="modal-footer">
<button class="btn btn-info cancel" ng-click="encourage()">Confirm</button>
<button class="btn btn-warning cancel" ng-click="close()">Cancel</button>
</div>
</div>
</g:if>
<g:else>
Thank you for encouraging.
</g:else>
The EncouragementController.js is
function EncouragementController($scope, $http) {
/**
* open payment dialog
*/
$scope.openEncouragementModal = function (amount) {
$scope.encouragementModal = true;
};
$scope.closeEncouragementModal = function () {
$scope.closeMsg = 'You canceled the encouragement’;
$scope.encouragementModal = false;
};
$scope.opts = {
backdropFade: true,
dialogFade:true
};
}
EncouragementController.$inject = [ '$scope', '$http'];
Question
Only the Encourage button should have been visible for the first time, but what I see is button along with the encouragementModal and it's buttons.
So, how do I do that?
References I followed
[1] Modal (ui.bootstrap.modal)
[2] Simple Grails + AngularJS Example
You need to hide it at first and only display it when scope.encouragementModal value is true.
<div modal="encouragementModal" ng-show="encouragementModal" options="opts">

Resources