Closing Bootstrap's modal from Knockout - javascript

I wrote a function (basing on some other I found on the Internet), which aids in displaying dynamically-generated modals without need to create ones in page's HTML.
To simplify code analysis, briefly:
I add new binding handler to Knockout to allow disabling binding on specific element
Modal is generated from a template using Mustache
Mustache fills in relevant parts of the modal (title, body, buttons)
Modal is wrapped in div, which stops data binding (modalWrapper)
... so that I can apply custom viewmodel to the modal with ko.applyBindings
Buttons are generated automatically basing on description, like:
{
label: "OK",
cssClass: "default",
handler: "handleClick", // sets data-bind="click: handleClick"
autoClose: true // adds data-dismiss="modal"
}
Relevant parts of the code follows:
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
var modalTemplate = '<div class="modal fade">\
<div class="modal-dialog {{size}}">\
<div class="modal-content">\
<div class="modal-header">\
<h5 class="modal-title">{{title}}</h5>\
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>\
</div>\
<div class="modal-body">{{&body}}</div>\
<div class="modal-footer">{{&buttons}}</div>\
</div>\
</div>\
</div>';
/**
* Displays modal on the screen.
* #param {Object} options Options
* #param {string} options.title Title of the modal
* #param {html} options.body Body of the modal
* #param {string} options.size Size of the modal. Can be small, default, large or xlarge.
* #param {Object} options.actions Describes buttons to display on the modal. For each, specify label, cssClass, handler and optionally autoClose.
*/
var showModal = function(options) {
options = options || {};
options = $.extend({
title: '',
body: '',
size: false,
actions: false,
viewModel: {}
}, options);
var modalClass = {
small: "modal-sm",
default: "",
large: "modal-lg",
xlarge: "modal-xl"
};
var modalWrapper = $('<div data-bind="stopBinding: true"></div>').appendTo('body');
var buttons;
if (options.actions === false) {
buttons = '<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>';
} else {
buttons = "";
for (var i = 0, len = options.actions.length; i < len; i++) {
var currentButton = $.extend({
label: '<No label>',
cssClass: 'default',
handler: '',
autoClose: true
}, options.actions[i]);
var btn = '<button type="button" class="btn ' +
options.actions[i].cssClass +
'" data-bind="click: ' +
options.actions[i].handler +
(options.actions[i].autoClose === true ? '" data-dismiss="modal"' : '')
+ '>'
+ options.actions[i].label
+ '</button>';
buttons += btn;
}
}
var templateData = {
title: options.title,
body: options.body,
size: modalClass[options.size],
buttons: buttons
};
var modalHtml = Mustache.render(modalTemplate, templateData);
var $modal = $(modalHtml).appendTo(modalWrapper);
$modal.on('hidden.bs.modal', function (e) {
modalWrapper.remove();
});
ko.applyBindings(options.viewModel, $modal.get()[0]);
$modal.modal(options);
};
I have a problem with this autoclosing feature. If it is on, viewmodel processes the click, modal is closed by Bootstrap mechanisms and then removed from DOM after hiding.
But when I want a button to be non-auto-closing, I have no means to close modal from the viewmodel. The solution I thought of was to inject a method to viewmodel like:
viewmodel['close'] = function() { $modal.modal('hide'); };
However this seems to be a hackish solution (even for Javascript ;)). Similarly, I may inject the $modal itself to the viewmodel, but that would be even more ugly.
What would be then the best way to close the modal from within modal's viewmodel?

I solved a similar requirement by assigning an (auto-generated) id to the modal div tag.
var modalId = ('modal' + Math.random()).replace('.', '');
var modalTemplate = '<div class="modal fade" id="' + modalId + '">'\ // remaining code here
Then, that id is being passed to viewmodel, which allows it to close the corresponding modal via
$('#' + _self.modalId).modal('hide');
A comparable event subscription to 'hidden.bs.modal' handles the cleanup.

I would create a custom Knockout binding for handling the visibility of the modal.
This binding connects the bound modal element to the supplied model observable. Click handlers in the model now only have to manage this observable to control the modal visibility.
You could apply this binding for example in your modalTemplate variable: var modalTemplate = `<div class="modal fade" data-bind="bootstrapModalVisible: yourObservable">\...
I haven't incorporated the binding in your case, but below is an example of the working of this binding.
the modalVisible observable is initialized to true, so the modal immediately shows
the close button immediately hides the modal by setting the observable to false
the upper-right close button closes the modal because of data-dismiss="modal". The event handlers in the binding's init will make sure the correct state is written to the observable.
the save button only closes the modal when confirmed, showing the possibility to postpone closing the modal
ko.bindingHandlers['bootstrapModalVisible'] = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).on('hidden.bs.modal', function () { valueAccessor()(false); })
$(element).on('shown.bs.modal', function () { valueAccessor()(true); })
},
update: function (element, valueAccessor) {
if (ko.unwrap(valueAccessor())) {
$(element).modal('show');
} else {
$(element).modal('hide');
}
}
};
function Test() {
var self = this;
self.modalVisible = ko.observable(true);
self.showModal = function() {
self.modalVisible(true);
};
self.hideModal = function() {
self.modalVisible(false);
};
self.save = function() {
if (window.confirm('Close modal?')) {
self.modalVisible(false);
}
}
}
ko.applyBindings(new Test());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<button type="button" class="btn btn-primary" data-bind="click: showModal">
Launch demo modal
</button>
<div class="modal fade" data-bind="bootstrapModalVisible: modalVisible">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Example modal</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bind="click: hideModal">Close</button>
<button type="button" class="btn btn-primary" data-bind="click: save">Save changes</button>
</div>
</div>
</div>
</div>

Related

Correct way to create and remove/destroy DOM element using Javascript

Explanation:
I have an ASP.NET application, where I can retrieve Vehicle information with a click of a button. The Vehicle detail is then displayed in a modal screen with the following data: ID, VEHICLENAME,YEAR, and VEHICLE_DOCUMENTATION.
The VEHICLE_DOCUMENTATION is displayed in anchor tags, which is created using JavaScript. The expectation is when I retrieve Vehicle detail, their relevant VEHICLE_DOCUMENTATION would be displayed, and created in an anchor tag. If another Vehicle detail is retrieved, their relevant VEHICLE_DOCUMENTATION would also be created in an anchor tag.
The problem I'm encountering is when anchor tag are created for a Vehicle detail, I can not create anchor tags for the next time I retrieve another Vehicle detail. I have outlined the errors I keep getting below.
I've provided the full working code below, including the error message I encounter. Hopefully someone could assist me.
DetailController:
DetailController:
[HttpPost]
public ActionResult GetDetail(int? id)
{
return _context.Vehicle.Where(v => v.ID == id).Select(x => new VehicleModel(){
ID = x.ID,
VEHICLENAME = x.VEHICLENAME,
YEAR = x.YEAR,
VEHICLE_DOCUMENTATION = x.VEHICLE_DOCUMENTATION
}).FirstOrDefault();
}
Detail Razor Page:
Button
------
<button type="button" data-toggle="modal" data-target="#detailModal" onclick="postData(id)"></button>
Modal window appears when button is clicked
-------------------------------------------
<div class="modal fade" id="detailModal" tabindex="-1" role="dialog" aria-labelledby="detailModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="detailModalTitle">Vehicle Detail</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
ID: <label id="ID"></label><br />
VEHICLE NAME: <label id="VEHICLENAME"></label><br />
YEAR: <label id="YEAR"></label><br />
VEHICLE DOCUMENTATION: <label id="VEHICLE_DOCUMENTATION"></label><br />
</div>
</div>
</div>
</div>
AJAX POST to GetDetail method:
------------------------------
<script>
function postData(id) {
response = '';
$.ajax({
async: true,
type: "POST",
url: "/Detail/GetDetail",
data: { 'id': id},
success: function (xhr) {
response = xhr;
$('#ID').text(response.data.ID);
$('#VEHICLENAME').text(response.data.VEHICLENAME);
$('#YEAR').text(response.data.YEAR);
$('#VEHICLE_DOCUMENTATION').text(response.data.VEHICLE_DOCUMENTATION);
//Create anchor tags
if (response.data.VEHICLE_DOCUMENTATION.length !== 0) {
for (const doc of response.data.VEHICLE_DOCUMENTATION) {
const anchor = document.createElement('a');
const list = document.getElementById('VEHICLE_DOCUMENTATION');
const li = document.createElement('ol');
anchor.href = 'www.vehicledocumentationexample.com';
anchor.innerText = `${doc}`;
//THIS IS WHERE THE ERROR IS BEING FLAGGED:
li.appendChild(anchor);
list.appendChild(li);
}
//Delete/Remove elements
} else if (response.data.VEHICLE_DOCUMENTATION.length == 0) {
response.data.VEHICLE_DOCUMENTATION = [];
document.getElementById("VEHICLE_DOCUMENTATION").outerHTML = "";
//WHAT I'VE TRIED:
//document.getElementById('VEHICLE_DOCUMENTATION').remove();
//document.getElementById('VEHICLE_DOCUMENTATION').outerHTML = "";
}
},
error: function (xhr) {
response = xhr;
}
})
}
</script>
Browser error message:
Uncaught TypeError: Cannot read property 'appendChild' of null
at Object.success (...)
at fire (jquery.js:3240)
at Object.fireWith [as resolveWith] (jquery.js:3370)
at done (jquery.js:9061)
at XMLHttpRequest.<anonymous> (jquery.js:9303)
when you put:
document.getElementById("VEHICLE_DOCUMENTATION").outerHTML = "";
What you are doing is to erase the label element, so when you call it from
const list = document.getElementById('VEHICLE_DOCUMENTATION');
It will return null as the element do not exist anymore
Instead of using "outerHTML" you should use "innerHTML", here it is the difference between them:
console.log(document.getElementById("VEHICLE_DOCUMENTATION").outerHTML);
console.log(document.getElementById("VEHICLE_DOCUMENTATION").innerHTML);
<label id="VEHICLE_DOCUMENTATION">
<p>something</p>
</label>

Symfony - Fullcalendar event description undefined

I have a controller that gets data from database and turns it in events for fullcalendar, when I display a modal clicking in one of this events, it shows that description and email are undefined.
Image of modal undefined fields
But in XHR I can see that im receiving all the data from the controller.
Image of XHR GET
Modal:
<div id="fullCalModal" class="modal fade" style="z-index: 9999;>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 id="modalTitle" class="modal-title"></h4>
</div>
<div id="modalBody" class="modal-body">
<p id="ev_start" class="modal-body"></p>
<p id="ev_end" class="modal-body"></p>
<p id="ev_mail" class="modal-body"></p>
<p id="ev_desc" class="modal-body"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button class="btn btn-primary"><a id="eventUrl" target="_blank">Event Page</a></button>
</div>
</div>
</div>
Javascript:
eventClick: function(info) {
var eventObj = info.event;
alert('Clicked ' + eventObj.title + ' with id: ' + eventObj.id + eventObj.description);
$('#modalTitle').html(eventObj.title);
$('#ev_titulo').html(eventObj.title);
$('#ev_start').html('Fecha inicio: ' + eventObj.start);
$('#ev_end').html('Fecha fin: ' + eventObj.end);
$('#ev_desc').html('Descripcion: ' + eventObj.description);
$('#ev_mail').html('Mail: ' + eventObj.mail);
$('#fullCalModal').modal('show');
},
Controller:
public function loadAction()
{
$em = $this->getDoctrine()->getManager();
$eventos = $em->getRepository('App:Evento')->findAll();
$data = array();
foreach ($eventos as $evento)
{
$events['id'] = $evento->getId();
$events['title'] = $evento->getTitle();
$events['start'] = $evento->getBeginAt()->format('Y-m-d');;
$events['end'] = $evento->getEndAt()->format('Y-m-d');;
$events['color'] = $evento->getColor();
$events['description'] = $evento->getDescription();
$events['mail'] = $evento->getMail();
array_push($data, $events);
}
return $this->json($data);
}
The event parsing documentation states that
Every other non-standard prop will be transferred over to the
extendedProps hash in the Event Object.
Since description and mail are not standard event properties in fullCalendar (as listed in that documentation), then they will be placed under the "extendedProps" object in the final event object which fullCalendar creates based on the data it receives from your server.
Therefore in your eventClick code you should be able to write
$('#ev_desc').html('Descripcion: ' + eventObj.extendedProps.description);
$('#ev_mail').html('Mail: ' + eventObj.extendedProps.mail);
and populate the properties successfully into your modal.
P.S. This behaviour is also mentioned in the event object documentation as well. If you have previously used fullCalendar version 3 or below, then this is a change from how these earlier versions worked.

FullCalendar's eventClick not working after changing view

Supposing that I want to open a modal popup when user clicks on a FullCalendar's event, I wrote some logic on the "eventClick" part of Full Calendar. This works fine until I'm on the default view, which I setted as "month".
If I try to switch from a view to another, though, this doesn't get fired, almost like it died.
What am I doing wrong? This is the code that I have for my Full Calendar. I've also tried to add a viewRender event as per the documentation but it's not working
function initCalendar2(oggetti){
debugger;
$('.calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
//right: 'month,agendaWeek,agendaDay,' +vistaAgenda
right: 'month,agendaWeek,agendaDay,listDay,listWeek'
},
views: {
listDay: { buttonText: 'Lista Giorno'},
listWeek: { buttonText: 'Lista Sett'}
},
eventClick: function(calEvent, jsEvent, view) {
debugger;
//Send 2 values to the modal
$("#dataArrivo").val(calEvent.start.toISOString().split('T')[0]);
$("#dataConsegna").val(calEvent.end.toISOString().split('T')[0]);
//Opens the modal
var targeted_popup_class = $(this).attr('data-popup-open');
$('[data-popup="' + targeted_popup_class + '"]').fadeIn(350);
//e.preventDefault();
var newDateOne;
var newDateTwo;
//This triggers only when the save button of the modal is clicked
setTimeout(function() {
$("#save").click(function() {
var tmp1 = $("#dataArrivo").val();
var tmp2 = $("#dataConsegna").val();
newDateOne = new Date(tmp1);
newDateOne.setDate(newDateOne.getDate());
newDateTwo = new Date(tmp2);
newDateTwo.setDate(newDateTwo.getDate());
var tmp = calEvent.id.split("&");
var idToSend = "";
for (var i = 0; i < tmp.length-1; i++) {
if (i == tmp.length-2) {
idToSend += tmp[i];
} else {
idToSend += tmp[i]+"&";
}
} if (newDateOne !== calEvent.start || newDateTwo !== calEvent.end) {
var actualDate = new Date(calEvent.start);
debugger;
//This ajax call will save the event's new dates in the DB
$.ajax({
url: 'calendariomanagement/listaPraticheFormJSONByIdProgettoCommittenteAndTipoVal/'+ idToSend +'/modDate/' + nuovaDataArrivo.toDateString() + '&' + nuovaDataConsegna.toDateString() + '&' + actualDate.toDateString(),
type:'POST',
async:false,
success: function(data) {
var targeted_popup_class = $(this).attr('data-popup-close');
$('[data-popup="' + targeted_popup_class + '"]').fadeOut(350);
location.reload();
/*calEvent.start = dataUno;
calEvent.end = dataDue;
$('#calendar').fullCalendar('updateEvent', calEvent);*/
}
});
}
}), 350}
);
},
viewRender: function(view, element) {
debugger;
$('#calendar').fullCalendar('rerenderEvents');
},
locale: 'it',
defaultView: 'month',
defaultDate: moment(),
navLinks: true, // can click day/week names to navigate views
editable: false,
eventDurationEditable: false,
//weekends:false,
eventLimit: true, // allow "more" link when too many events
events: oggetti
});
Putting here for anybody that might fall in something similar my problem.
This was the modal popup that I was defining with HTML
<!-- Modal -->
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
*<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Cambio data</h4>
</div>
<div class="modal-body">
<p> Inserire i nuovi valori nel formato anno-mese-giorno (es.: 2018-07-03). Nel
caso non si volesse modificare nessun campo, semplicemente chiudere questo pop-up</p>
<label for="arrivo">Data Arrivo:</label>
<input type="text" name="dataArrivo" id="dataArrivo" class="form-control">
<label for="consegna">Data Riconsegna:</label>
<input type="text" name="dataConsegna" id="dataConsegna" class="form-control">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Chiudi</button>
<button type="button" class="btn btn-default" id="save" data-dismiss="modal">Salva</button>
</div>
</div>
*
After FullCalendar's load, I added this with jQuery
$(".fc-content").attr('data-toggle', 'modal');
$(".fc-content").attr('data-target','#myModal');
But of course, after changing the view, those attributes were lost and there was no chance to reinsert them. As a solution (pointed out by ADyson), the workaround was to define a modal with a bootbox object so that I could have it dynamically

Clicking anchor unexpectedly calls alert()

I have some code that retrieves users from a server (ajax) and I use some <a> tags to display it, and when you click on an <a> tag with a user, it's supposed to add it to an array in_group. The first one works, the second one goes to the alert() function AND also adds the user to the array, which confuses me. The remove button doesn't work either. What am I doing wrong? I want the user to be added to the in_group array only if doesn't exist, and to be deleted when the button is pressed.
var in_group = [];
$("#students-body").on('click', 'a', function() {
var modal = $("#manageGroupMembers");
var student_id = $(this).attr('student-id');
var student_name = $(this).html();
var student = {
id: student_id,
name: student_name
};
console.log(in_group.length);
if (in_group.length > 0)
{
for (i = 0; i < in_group.length; i++)
{
console.log(in_group[i].id);
if (in_group[i].id === student_id)
{
alert('in grp');
return;
}
else
{
in_group.push(student);
}
}
}
else
{
in_group.push(student);
}
RefreshGroup();
//modal.modal('hide');
});
function RefreshGroup()
{
var students_group = $("#students-group");
var html = "";
if (in_group.length > 0)
{
for (i = 0; i < in_group.length; i++)
{
html += "<span>"+in_group[i].name+"</span>";
html += "<button class='btn btn-danger' onclick='event.preventDefault();RemoveFromGroup("+i+")'>x</button>";
}
students_group.append(html);
}
}
function RemoveFromGroup(index) {
in_group.splice(index, 1);
RefreshGroup();
}
Html:
<div class="form-group">
<label for="group_members">Members</label>
<button class="btn btn-primary" style="display: block;" onclick="event.preventDefault()" id="add-student-btn">Add Member</button>
<div id="students-group"></div>
</div>
Modal:
<!-- Modal -->
<div class="modal fade" id="manageGroupMembers" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Add Member to Group</h4>
</div>
<div class="modal-body">
<div id="students-body"></div>
<div id="pagination-students"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
That's because you are inserting the id in a for loop. Change your code:
...your code before this....
var student_id = $(this).attr('student-id');
var student_name = $(this).html();
var student = {
id: student_id,
name: student_name
};
var index = in_group.findIndex(function(std) {
return std.id == student.id;
});
if(index === -1) {
in_group.push(student);
} else {
alert('student was already in array')
}
This way, you check if the student is inside the array. If not (-1) you insert it. Else you alert
In this section of your code, you’re looping the in_group array to find a student entry with a specific ID.
if (in_group.length > 0)
{
for (i = 0; i < in_group.length; i++)
{
console.log(in_group[i].id);
if (in_group[i].id === student_id)
{
alert('in grp');
return;
}
else
{
in_group.push(student);
}
}
}
else
{
in_group.push(student);
}
RefreshGroup();
If you find it, you alert it, and then you stop the loop and the entire function. If you don’t find it, you push it, but you don’t break the loop. You keep looping, the check in_group[i].id === student_id is re-executed and the alert is executed as well.
You could add a return; after calling RefreshGroup, however, why not get rid of the loops and make things easier?
let isInGroup = in_group.find(studentEntry => studentEntry.id === student_id);
if(isInGroup){
alert("in grp");
}
else{
in_group.push(student);
RefreshGroup();
}
This will replace the above section completely and it will work.
As to why the removal doesn’t work:
event isn’t defined. It’ll likely produce a ReferenceError. (Same problem for your other <button>.)
Instead of inline event attributes, look into event delegation and standard event listeners (jQuery has the click method—use it!)
Look into data- attributes or jQuery’s data to hold the value of the index.
Event delegation could look like this:
var students_group = $("#students-group");
students_group.on("click", "button", function(e){
RemoveFromGroup($(this).attr("data-index"));
});
And then, when generating the HTML:
html += "<button class='btn btn-danger' data-index='" + i + "'>x</button>";
I’d also recommend reading about Array.prototype methods, particularly the iteration methods.

Implementing a Modal Factory?

I'be been using modals as a means to communicate to users in my apps for some time now via several different front end frameworks. The logic is usually the same, defining the modal's html then rendering it via some click event.
As my applications grow, so do the number of modals I use for a user prompt or confirmation - these modals can have anything from text inputs to forms to dropdowns and so on.
My current method is to write out each separate modal in a single html file and simply call them by their IDs but I feel this is inefficient as there is plenty of duplicate boilerplate code, so I'm wondering the best way would be to create modals dynamically while keeping the code as light andclean as possible?
I've been thinking of something like a "modal factory" where you pass the content of the modal along with the height, width, styling, etc. would this be a good approach?
Thanks for any input!
Well what I do for Forms/HTML Content loaded from the server - is create a div with an ID - PartialViewDialog at the end of my page -(I load Partial Views inside a Dialog)
This one is Bootstrap 3.* based - (HTML structure based on Frontend framework
So the HTML is like this:
<body>
<!-- Other page content -->
<div class="modal fade" id="PartialViewDialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" data-modal="title"></h4>
</div>
<div class="modal-body" data-modal="content">
</div>
<div class="modal-footer" data-modal="footer">
</div>
</div>
</div>
</div>
</body>
Then in JS, I create a dialog Manager:
var MyApp = MyApp || {};
MyApp.UIHelper = MyApp.UIHelper || {};
MyApp.UIHelper.DialogManager = (function () {
"use strict";
var self = {};
self.divId = null;
self.dialog = null;
self.dialogBody = null;
self.dialogTitle = null;
self.dialogFooter = null;
self.actionUrl = "";
self.modalObject = null;
self.options = {};
function Initilize(divId, options) {
self.options = $.extend({ buttons: [] }, options);
self.divId = divId;
self.dialog = $(self.divId);
self.dialogBody = self.dialog.find('*[data-modal="content"]');
self.dialogTitle = self.dialog.find('*[data-modal="title"]');
self.dialogFooter = self.dialog.find('*[data-modal="footer"]');
self.BootgridObject = null;
};
function OpenPartialViewDialog(url, title, preprocessingFunction, postProcessingFunction) {
// Create the buttons
var options = self.GetPartialViewButtons(url, preprocessingFunction, postProcessingFunction);
// Initialise the PartialViewDialog with Buttons
Initilize('#PartialViewDialog', options);
// Set the URL for Ajax content load and Form Post
self.actionUrl = url;
// Set Dialog Title
self.dialogTitle.html(title);
// Open the PartialViewDialog
self.OpenModel();
};
// This Method creates the buttons for the Form dialog
// e.g Save, Cancel, Ok buttons
self.GetPartialViewButtons = function (url, preprocessingFunction, postProcessingFunction) {
// I only need Save and Cancel buttons always so I create them here
var buttons = {
buttons: {
// I create a save button which Posts back the Form in the Dialog
Save: {
Text: "Save",
css: "btn btn-success",
click: function () {
// Call a function before sending the Ajax request to submit form
if (preprocessingFunction) { preprocessingFunction(); }
$.ajax({
type: "POST",
url: url,
// This Dialog has a Form - which is Post back to server
data: self.dialogBody.find("form").serialize(),
success: function (response) {
// TODO: Check if response is success -
// Apply your own logic here
if (response.hasOwnProperty("IsSuccess")) {
if (response.IsSuccess) {
self.dialogBody.html("");
self.dialog.modal("hide");
// TODO: Show Success Message
// You can call another function if you want
if (postProcessingFunction) {
postProcessingFunction();
}
} else {
// If failure show Error Message
}
}
},
error: function (response) {
// If failure show Error Message
}
});
}
},
Cancel: {
Text: "Cancel",
css: "btn btn-danger",
click: function () {
self.dialogBody.html("");
self.dialogFooter.html("");
self.dialogTitle.html("");
self.dialog.modal("hide");
}
}
}
};
return buttons;
};
// dynamic creating the button objects
self.CreateButtonsHtml = function () {
var htmlButtons = [];
$.each(self.options.buttons, function (name, props) {
var tempBtn = $("<button/>", {
text: props.Text,
id: "btn_" + props.Text,
"class": props.css + "",
click: props.click
}).attr({ "style": "margin-right: 5px;" });
htmlButtons.push(tempBtn);
});
return htmlButtons;
};
// This method will load the content/form from server and assign the modal body - it will assign the buttons to the Modal Footer and Open the Dialog for user
self.OpenModel = function () {
$.ajax({
url: self.actionUrl,
type: "GET",
success: function (response) {
// Handle response from server -
// I send JSON object if there is Error in loading the content - otherwise the result is HTML
if (response.hasOwnProperty("HasErrors")) {
// Means some error has occured loading the content - you will have to apply your own logic
} else {
//Server return HTML - assign to the modal body HTML
self.dialogBody.html(response);
self.modalObject = self.dialog.modal();
// show modal
self.modalObject.show();
}
}
});
// Create the buttons in the Dialog footer
var buttons = self.CreateButtonsHtml();
self.dialogFooter.html('');
for (var i = 0; i < buttons.length; i++) {
self.dialogFooter.append(buttons[i]);
}
};
return {
OpenPartialViewDialog: OpenPartialViewDialog,
};
})();
Then whenever I need to open a dialog from the server I can call it like this:
MyApp.UIHelper.DialogManager
.OpenPartialViewDialog('/Content/Load', "My Title",
function(){alert('pre-process')},
function(){alert('post-process')}
);
Note: The PreProcess + PostProcess are called when the Save button is clicked
Here is a working/demo example which shows what the above JS does - Hope it helps
In the demo I load Dummy HTML from a div id="dummycontent"
Fiddle: https://jsfiddle.net/1L0eLazf/
Button Fiddle: https://jsfiddle.net/1L0eLazf/1/

Categories