jQuery - dynamically loading ASP.NET partial view into modal - javascript

Question background:
I have an MVC project where I am attempting to create a partial view modal content that is then concatenated to the rest of its respective modal markup and then finally appending to a 'main' modal div.
The code:
Main modal div:
<div class="modal fade"
id="basicModal"
tabindex="-1"
role="dialog"
aria-labelledby="basicModal"
aria-hidden="true">
</div>
JQuery to trigger the modal popup along with the AddModal method to build the modal content:
<script>
$(document).ready(function () {
$(".btn").click(function () {
AddModal();
$("#basicModal").modal("show");
});
});
</script>
AddModal method to build the modal:
AddModal = function() {
var partialHtml = $(#Html.Partial("../Modal/Index", new { id = 1 }))
$html = $('<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">X</button>' +
'<h4 class="modal-title" id="myModalLabel">Modal title</h4>' +
'</div>' +
'<div class="modal-body">'+partialHtml+'</div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>' +
'<button type="button" class="btn btn-primary">Save changes</button>' +
'</div>' +
'</div>' +
'</div>');
$("#basicModal").append($html);
};
Partial View:
<h4>Test Partial view</h4>
The issue:
I am running into an error of Uncaught SyntaxError: Unexpected token < which is being caused by the HTML Markup of the partial view as shown:
var partialHtml = $(<h4>Test Partial view</h4>)
How can I successfully escape this forward slash so that the HTML from my partial view is correctly added to the rest of the dynamically added markup?

Instead of using Html.Partial() and storing that in a JavaScript string, consider using this technique: Render Partial View Using jQuery in ASP.NET MVC
Basically, in your AddModal() method, fire off a GET request that hits an action that returns the partial view as HTML. Then, just replace the contents of #basicModal with the returned HTML:
AddModal = function() {
var partialHtml;
var url = '../Modal/Index?id=1';
$.get(url, function(data) {
$("#basicModal").html(data);
});
};
I've used this technique to load modals before and it works well. However, one problem with doing it this way is that $.get() is an async call, so .modal("show") is probably going to fire before the data has been fetched. We can solve this by having AddModal return the promise generated by $.get():
AddModal = function() {
var partialHtml;
var url = '../Modal/Index?id=1';
return $.get(url, function(data) {
$("#basicModal").html(data);
});
};
Then you would change your calling line to:
AddModal().done(function() {
$("#basicModal").modal("show");
});

Related

How to append big HTML snippets into DOM with Javascript?

I have an app which uses EJS templating to populate data.
In order to add new projects I have made a simple input form which takes all required parameters.
After an input completed an Ajax request being sent, on success I want to inject this snippet into DOM.
In simple words - After new project added I want to display play instantly by injecting into DOM without reloading the page
Is there an elegant way of inserting this div ladder as a template into DOM? It works,
<div class="projects">
<div class="projectHeader">
<div class="projectTitle">
<span>
<a data-toggle="modal" data-target="#editDeadLineModal">
<i data-id="<%=project.id%>" class="projectDeadline far fa-calendar-alt fa-2x" data-toggle="tooltip" data-placement="top" title="Set Deadline"></i>
</a>
</span>
<h5 class="projectName <%=project.id%>" data-toggle="tooltip" data-placement="top" title="Deadline <%=deadline%>" style="align-items: center;">
<%=project.name%>
</h5>
<%}%>
<div class="projectButtons">
<span data-toggle="tooltip" data-placement="top" title="Edit Project Title">
<a data-toggle="modal" data-target="#editProjectTitleModal">
<i id="editProjectName" class="editProject fas fa-pencil-alt" data-name="<%=project.name%>" data-id="<%=project.id%>"></i>
</a>
</span>
<i class="separatorDash fas fa-minus"></i>
<span data-toggle="tooltip" data-placement="top" title="Delete Project">
<a data-toggle="modal" data-target="#deleteProjectModal">
<i id="deleteProject>" class="deleteProject far fa-trash-alt" data-id="<%=project.id%>"></i>
</a>
</span>
</div>
</div>
</div>
</div>
What I have tried is recreating the entire div ladder in string and append it to parent node.
Like this:
// Add new Project
$('#addNewProjectBtn').on("click", function() {
$("#newProjectModal").on('show.bs.modal', function() {
$(".confirmNewList").on("click", function(event) {
var url = '/addNewList';
var newListTitle = $("#newListNameInput").val()
event.preventDefault();
$.post({
url: url,
data: {
listName: newListTitle
},
success: function(result) {
$("#newProjectModal").modal('hide')
$("#newListNameInput").val('');
var id = result.data.id
var name = result.data.name
//Append new project
$(".projects").append("<div class='project col-4' id='project" + id + "'> <div class='projectHeader'> <div class='projectTitle'> ...and so on until the end")
},
error: function(err) {
console.log(err);
}
})
}
}
})
})
})
In simple words - After new project added I want to display play instantly by injecting into DOM without reloading the page
Is there an more elegant and specially efficient way of inserting this div ladder as a template into DOM?
The method which I have tried above - works, But on attempt to interact with it by calling modals - modals do not get it's data-*, as well the bootstrap tooltips don't work.
you can try create new html file instead, and append like this in your page
$.get("yourfile.html", function (data) {
$("#appendToThis").append(data); // or use .html();
});
OR you can directly pass this HTML structure from your backend, so you can directly use append function.
After some research I discovered that the event handler can be delegated.
Since the html snippet I am trying to append to projects is added after document.ready (document is loaded and handlers are bind) the only thing required to make the new snippet be active on event is to delegate the event handler to the parent of element that is appended.
Like this :
$("body").delegate("#addNewProjectBtn", "click", function() {
$("#newProjectModal").on('show.bs.modal', function() {
$(".confirmNewList").on("click", function(event) {
var url = '/addNewList';
var newListTitle = $("#newListNameInput").val()
event.preventDefault();
$.post({
url: url,
data: {
listName: newListTitle
},
success: function(result) {
$("#newProjectModal").modal('hide')
$("#newListNameInput").val('');
var id = result.data.id
var name = result.data.name
//Append new project
$(".projects").append("<div class='project col-4' id='project" + id + "'> <div class='projectHeader'> <div class='projectTitle'> ...and so on until the end")
},
error: function(err) {
console.log(err);
}
})
}
}
})
})
})
By delegating the event handler to it's parent - the event handler is preserved in the parent.

FullCalendar and JQuery events

I made a Fullcalendar in my Laravel app. The render is exactly what I want : a calendar on the left and when users clic on a day, this day become "red", and the list of hours for meetings is create on the right.
See the result here (I have just blur the coach name ;) :
I create the list with this code in my calendar :
dateClick: function (info) {
//Colorize the select day in red
$('*').removeClass('activeday');
$('[data-date=' + info.dateStr + ']').addClass('activeday');
// Ajax for recover all events 'Disponible"
let qlq;
$.ajax({
url: 'events/get-disponibility',
method: 'GET',
dataType: 'json',
async: false,
data: {
date: info.dateStr,
},
error: function (request, status, error) {
console.log(request.responseText);
console.log(status);
console.log(error);
},
success: function (data) {
qlq = data;
}
});
let html = "<h3>Horaires et Coachs disponibles : </h3> <br>";
if (qlq.length) {
qlq.forEach(function (row) {
html = html + '<div class="container d-flex mb-3">\
<div class="col-6">\
<span id="puce">•</span>\
' + row.admin_prenom + ' ' + row.admin_nom + ' </div> \
<div class="col-6 justify-content-around">\
<span class="badge badge-pink">' + row.start_date.substring(11, 16) + '</span>\
<a href="#' + row.id + '" class="get-modal-event"\
data-idEvent=' + row.id + '>\
<span class="badge badge-dark">\
<i class="fas fa-arrow-right"></i>\
</span>\
</a>\
</div>\
</div>';
});
$("#freeCoach").empty()
.append(html);
} else {
$("#freeCoach").empty()
.append('<div class="container d-flex mb-3">\
<div class="col-12">\
<span id="puce">•</span>\
Pas de coach disponible à cette date. <br>\
<br>\
Seul les dates comportant un fond coloré comporte des disponibilités</div> \
</div>');
}
},
Now I just want to popup a bootstrap modal, when users click on the black arrow (a link whith the class "get-modal-event").
So I use, after my calendar render, this JQuery code :
$('a.get-modal-event').each(() => {
$(this).click(function (e) {
e.preventDefault();
alert('get modal !!!!!');
$('#modal-event').modal('show');
})
I have create an alert box to see that function work, but no alert is showing and no modal appears...
I tried to create a link outside of the calendar (with is own class for a test) and both alert and modal appear !
I also tried to put this code in a different file and build it, but the result is the same.
Any idea ?
The click() binding you're using is called a "direct" binding which will only attach the handler to elements that already exist. It won't get bound for dynamically created elements. you'll have to create a "delegated" binding by using [on()].
Here you'll need to use:
$('body').on('click', 'a.get-modal-event', function(e) {
e.preventDefault();
alert('get modal !!!!!');
$('#modal-event').modal('show');
});

yii2 assets missing when creating modal

I know there are a lot of similar questions I have tried all of them nothing working. In my modal there are assets missing, and I don't know how to, where to reload them.
EDIT: this is my start-form.php:
<?php $this->registerJsFile('js/voti.js'); ?>
<div class="box-footer">
<center><button id="ladeauswertung" class="btn btn-default">Auswertung Laden</button></center>
</div>
<div id ='auswertungdetail' name = 'auswertungdetail'>
<?= $auswertungdetail ?>
</div>
When the button "ladeauswertung" is clicked the followin JS code will be executed you can see here script-file.js:
$(document).on('click', '#ladeauswertung', function ()
{
var ausgewaehlterstandort = document.getElementById("standorte").value;
var datum = document.getElementById("datum").value;
$.get("index.php?r=voti/ladeauswertung&standort=" + ausgewaehlterstandort + "&datum=" + datum,
function (jsondata)
{
document.getElementById("auswertungdetail").innerHTML = jsondata;
}
);
});
and this code Part, which is in my controller:
$.get("index.php?r=voti/ladeauswertung&standort=" + ausgewaehlterstandort + "&datum=" + datum,
doing the following:
return $this->renderAjax('auswertungdetail', ["auswertung" => $auswertung, "gesamtauswertung" => $gesamtauswertung]);
so the modal appears after the button is clicked in my form, and in my modal there is a daterangepicker and a chart widget include. These widgets work great in every form BUT not in the modal, so I'm thinking that the assets are missing, but where do I load them?
Please help I'm searching since a couple of days.
Your HTML Code:
<div id ='auswertungdetail' name = 'auswertungdetail'>
</div>
Your Javascript Code:
<script>
$(document).on('click', '#ladeauswertung', function ()
{
var ausgewaehlterstandort = document.getElementById("standorte").value;
var datum = document.getElementById("datum").value;
$('#auswertungdetail').load("index.php?r=voti/ladeauswertung&standort="+ausgewaehlterstandort+"&datum="+datum").fadeIn("slow");
}
);
</script>
You just have to do coding in the controller as you are rendering the normal view.

Build input dynamically from View Model in an ajax response

I have a controller action that sends down a collection of ApiViewModel Types. Each view model represents a different API that can be executed server-side, and output the response in the browser through an ajax call using jquery. The server generates the HTML so all I have to do is insert the server-side HTML into the current page.
Some of the APIs can only execute if they are given some parameters. I'm trying to do this in a generic fashion. When the user clicks the run button, I display a model Bootstrap dialog. Within this dialog I'd like to provide the input options for the parameters on the API selected.
This is my HTML for the modal dialog
<div class="modal fade"
id="appParameters"
role="dialog"
aria-labelledby="appParametersLabel">
<div class="modal-dialog"
role="document">
<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" id="appParametersLabel"></h4>
</div>
<div class="modal-body" id="appDialogBody">
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
i have enough information to send to the server, letting the server know what API is going to be executed, and what View Model needs to go with it. What I'm not sure of though is how should I put together the HTML on the server side, so that I can send the HTML to the client and have the MVC validation attributes still work for client-side validation?
The javascript I'm using to send the data to the server, and add the servers HTML to the DOM is this. If no View Model is required, I just make a request to the server to execute the app and output the server-side response HTML. I think I don't need to do anything in regards to my Java Script below to handle the validation stuff; not sure though.
$('.btn-success').click(function () {
var button = $(this);
var appId = $(this).data("app");
var vmRequired = $(this).data("vm-required");
if (!vmRequired) {
var url = "/Home/RunApp?appId=" + appId;
$.get(url, function (data) {
$("div[data-app='" + appId + "']").html(data);
var buttonColumn = button.parent();
var appRow = buttonColumn.parent();
var hiddenRow = appRow.next()
hiddenRow.removeClass("hidden");
appRow.click(function () {
var hiddenColumn = hiddenRow.children().first();
var resultsDiv = hiddenColumn.children().first();
resultsDiv.empty();
hiddenRow.addClass("hidden");
$(this).off();
hiddenRow.off();
})
hiddenRow.click(function () {
var hiddenColumn = $(this).children().first();
var resultsDiv = hiddenColumn.children().first();
resultsDiv.empty();
$(this).addClass("hidden");
appRow.off();
$(this).off();
})
});
return;
}
var appName = $(this).data("app-name");
$('#appParametersLabel').html(appName);
$('#appParameters').modal({
keyboard: true,
backdrop: "static",
show: false,
}).on('show', function () {
$.get(url, function (data) {
$('#appDialogBody').html(data);
})
});
});
Do I just generate the HTML on the server side, like I would normally in the view? When the HTML is inserted into the DOM, will the validation all work correctly while using unobtrusive jquery validation?

Load in new view without removing previous one

My index.html has the following div
<div ng-view></div>
And I have my app declared as follows :
angular.module('app', [])
.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/listen', {
templateUrl : 'partials/listen.html',
controller : PlaylistCtrl
})
.when('/settings', {
templateUrl : 'partials/settings.html',
controller : SettingsCtrl
})
.otherwise({
redirectTo : '/listen'
})
}
])
;
Now, when I'm at the homepage (i.e. /#/listen), and click on a link to /#/settings, it replaces the contents of the page with the contents from partials/settings.html. How can I modify it so that the contents aren't replaced, but just added on? My goal is to have settings show up in a modal dialog, so I don't want the previous contents of the page to get cleared out.
ng-view is not going to help you here.
Instead you should combine ng-include with ng-switch or ng-show.
<div><ng-include src="listen.html"/></div>
<div ng-show="isOnSettingsUrl()"><ng-include src="settings.html"/></div>
Or something along those lines.
In the parent controller you need to read the $routeParams so that you can implement isOnSettingsUrl().
That's not possible with ng-view. You need to create an own directive and include it in your index.html:
<modal></modal>
Angular-ui has an implementation. Maybe you should check it out.
Edit:
In the past I've made my own modal (when testing out angular). I just started to learn angular, so it has lots of room for improvement (read now i would do it differently). However, it could give you an example:
app.directive('modal', function($compile, $http) {
return {
restrict: 'E',
replace: true,
compile: function(elm, attrs) {
var htmlText =
'<div id="' + attrs.id + '" class="modal hide fade" aria-hidden="true">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<p> </p>' +
'</div>' +
'<div class="modal-body">' +
'<div>to be replaced</div>' +
'</div>' +
'<div class="modal-footer">' +
'<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>' +
'</div>' +
'</div>';
elm.replaceWith(htmlText);
return function postLink(scope, elm, attrs) {
var modal = $('#' + attrs.id);
modal.css({
width: '60%',
left: '20%',
margin: 'auto auto auto auto',
top: '10%'
});
var modalBody = modal.children('.modal-body');
modalBody.css({
maxHeight: '800px'
});
var replaceDiv = modalBody.children();
$http.get(attrs.src).success(function(data) {
var childScope = scope.$new();
childScope.modalMode = true;
var element = $compile(data)(childScope);
angular.element(replaceDiv).replaceWith(element);
});
};
}
};
});
Html:
<a class="btn" data-target="#myId" data-toggle="modal" data-backdrop="static">Open modal</a>
<modal id="myId" src="path/to/partial" ></modal>
ng-view directly updates itself with the content came from the routeProvider, not only useful, but also increases performance as it unloads the controller and the watchers which you won't be using (you are on a different page).
Also a change in the url should change the view, not append anything. What would happen if I go directly to the page? That definitely won't be intuitive.
It is expected to have a index.html page which includes layout, ng-view which will be the changing page. For other things, you can use ng-include.
In your case, I assume that you want to show a partial page in which user can update their settings.
You don't need to this with routing, you can have the settings partial within the play template and use ng-show on the partial. You then just put a link to show the partial page.
If however, you want something like grooveshark; then you do need to use ng-view with routing to settings, then you should put the play template (and its controller) outside of the ng-view as you expect it to show up in every page.

Categories

Resources