Using X.PagedList on a modal pop-up - javascript

I've got a modal pop up on a page:
...
<div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="companySearchModal" aria-hidden="true" id="companySearchModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div id="companySearchModalContent"></div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
...
That I pop up:
...
$('#companySearchModalContent').html(data);
$('#companySearchModal').modal(options);
$('#companySearchModal').modal('show');
...
On that modal dialog I display a list of companies with this PagedListPager on the bottom setup like this:
<div>
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
<div id="companySearchPager">
#Html.PagedListPager(
Model,
page => Url.Action("CompanySearch",
"Admin",
new
{
sortOrder = ViewBag.CurrentSort,
currentFilter = ViewBag.CurrentFilter,
page = page
}),
PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(
new AjaxOptions
{
HttpMethod = "GET",
UpdateTargetId = "companySearchModalContent"
}
)
)
</div>
</div>
When I click on a given page element rendered by the PagedListPager control it does call the the Action "CompanySearch" from the Controller "Admin" as specified in the Url.Action but it renders the PartialView that is returned all by itself on the whole page instead of injecting the partial view into the "#companySearchModalContent" Div I've set as the UpdateTargetId in the AjaxOptions of the PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing call.
I figured the PagedListPager would do this. I added some jQuery code to call the appropriate ajax injection "$('#companySearchModalContent').html(data);" but I don't have a way to get the page number, search and sort parameters to come along with that the user clicked on from the pager control and don't know how to set the url and data appropriately in the .ajax code block.
$('#companySearchPager').click(function (e) {
e.preventDefault();
$.ajax({
type: 'GET',
// How to get the page value the user clicked on?
// data: {"page": #},
// How to get the url? This would work if I could get the page #.
// url: '#Url.Action("CompanySearch", "Admin")',
success: function (data) {
debugger;
$('#companySearchModalContent').html(data);
},
error: function () {
showAlert("Employer content load failed.", "warning", 5000);
}
});
return false;
});
I would expect the PageListPager to make that "$('#companySearchModalContent').html(data);" call for me given that I've set the UpdateTargetId in the AjaxOptions of the PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing call.
Thanks for any help...

Fixed the newel post!
Added
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
at the top of the page.

Related

Django, how to handle fetch request

I'm working on a Django project that uses a flatbed scanner. Since scanning takes a long time I must work around getting a timeout error. After searching and trying multiple things I ended up with threading an a fetch call.
How do I alter the fetch call to do what I want? I currently get send to a blank page that shows the dictionary that was returned by free_scan_crop. Please note that I am new to JavaScript. I just copied this bit of JS.
What I would like to happen:
A modal shows up when the form is submitted
When the scanner is done: send user to home page and show message
scan.html
<div class="modal fade" id="scanDocument" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Scanning</h5>
</div>
<div class="modal-body">
Please wait...
</div>
</div>
</div>
</div>
<script>
formElem.onsubmit = async (e) => {
e.preventDefault();
let response = await fetch("{% url 'core:free_scan' %}", {
method: 'GET',
body: new FormData(formElem)
});
let result = await response.json();
alert(result.message);
};
</script>
views.py
def free_scan_crop(request):
form = FreeScanForm(request.GET)
if form.is_valid():
file_name = form.cleaned_data['file_name']
# Grab the rest of the fields from the form...
x = threading.Thread(
target=scan_crop,
args=(request, file_name, top_left_x, top_left_y, bottom_right_x, bottom_right_y, dpi),
)
return x.start() # Questions: is this correct?
return JsonResponse({"scanning": True})
# invalid form
return JsonResponse({"scanning": False})
def scan_crop(request, file_name, top_left_x, top_left_y, bottom_right_x, bottom_right_y, dpi):
# This method runs for a long time
image = ScannerServiceConnection().scan_roi(
top_left_x,
top_left_y,
bottom_right_x,
bottom_right_y,
dpi
)
if image is None:
# No connection to the scanner
messages.error(request, 'Check scanner service status')
else:
# store image
image.save(<file_path>)
messages.success(request, 'saved image')
# Please note that I want to send a message to the user to inform them on the status of the scan
return render(request, 'home.html')
A special thanks to vlaz for helping out via the JS chat.
The fetch call is now working like it should. I couldn't get fetch to show the modal so made a little function to do that.
The scanner is running as a service via rpyc. I had to disable the timeout setting to keep it from throwing timeout errors. Note, this Django application runs offline on the user's system.
rpyc.connect(
host="localhost",
port=18861,
config={'sync_request_timeout': None},
)
scan.html
<form class="form" action="" method="post" id="free_scan_from">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" id="save_button" class="btn btn-primary" onclick="modalFunction()" name="action"
value="save_crop">
<i class="fa fa-save"></i> Scan & Save
</button>
{% endbuttons %}
</form>
<script>
// show the modal when the file_name field has a value
function modalFunction() {
if (document.getElementById("id_file_name").value !== "") {
$('#scanBanknote').modal('show');
}
}
</script>
<script>
const formElem = document.querySelector("free_scan_from")
formElem.onsubmit = async (e) => {
e.preventDefault();
let response = await fetch("{% url 'core:free_scan' %}", {
//cannot have a body with GET
method: 'POST',
body: new FormData(formElem)
});
let result = await response.text();
alert(result);
};
</script>
views.py
def free_scan_crop(request):
form = FreeScanForm(request.POST)
if form.is_valid():
file_name = form.cleaned_data['file_name']
# grab the rest of the fields
image = ScannerServiceConnection().scan_roi(
top_left_x,
top_left_y,
bottom_right_x,
bottom_right_y,
dpi
)
if image is None:
messages.error(request, 'Check scanner service status')
else:
# store image
image.save(<file_path>)
messages.success(request, 'image saved')
return render(request, 'free_scan_preview.html')
# invalid form
context = {'form': form}
return render(request, "free_scan_crop.html", context)

ASP.NET MVC Core - JavaScript event handlers with dynamically loaded PartialViews [duplicate]

This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 4 years ago.
I have this script in my view file, the purpose of it is to populate a section of the same view, but only a small section of it, which is a HTML div with Bootstrap panel classes:
<script type="text/javascript">
function GetVehicles() {
$.get('#Context.Request.Scheme://#hostname/#controller/GetVehicles', {id: #Model.Id}, function (response) {
$("#tabOutVehicles").html(response);
});
}
function GetMedInfo() {
$.get('#Context.Request.Scheme://#hostname/#controller/GetMedInfo', {id: #Model.Id}, function (response) {
$("#tabOutMedInfo").html(response);
});
}
</script>
My complete view, which will display the output generated by the script above:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="~/js/site.js"></script>
<div class="panel panel-primary">
<div class="panel-heading">
Vehicles
</div>
<div id="collapseVehicles" class="panel-collapse collapse">
<div id="tabOutVehicles">
</div>
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<strong style="color:white">Medical Info</strong>
</div>
<div id="collapseMedInfo" class="panel-collapse collapse">
<div id="tabOutMedInfo">
</div>
</div>
</div>
<script type="text/javascript">
function GetVehicles() {
$.get('#Context.Request.Scheme://#hostname/#controller/GetVehicles', {id: #Model.Id}, function (response) {
$("#tabOutVehicles").html(response);
});
}
function GetMedInfo() {
$.get('#Context.Request.Scheme://#hostname/#controller/GetMedInfo', {id: #Model.Id}, function (response) {
$("#tabOutMedInfo").html(response);
});
}
</script>
By clicking on the hyperlinks inside the Bootstrap panel divs, the jQuery method hits my controller action, returns the response, and then puts the response in the applicable div (#tabOutMedInfo / #tabOutVehicles). Both these actions return partial views. Here is what my partial view looks like, both look the same except for the model properties that differ:
#model MyViewModel
<div class="panel-body">
<a data-url="#Url.Action("EditMedInfo", "Controller", new { id = Model.Id })" data-toggle="ajax-modal" data-target="#EditMedInfo" class="btn btn-default">Edit</a>
<div class="form-horizontal">
<div class="">
<div class="form-group">
<div class="col-md-5 col-sm-5 col-xs-5"><label asp-for="Variable" class="control-label"></label></div>
<div class="col-md-7 col-sm-7 col-xs-7">
#Html.DisplayFor(item => item.Variable)
</div>
</div>
</div>
</div>
</div>
When the above hyperlink is clicked, it is supposed to execute JavaScript code that loads a modal for editing, which is not happening, instead it does not open the modal. The JavaScript code is located in my site.js file, which is being imported in my main view.
What I've tried:
I moved the script import tag to both my partial views and then removed it from my view, it then causes the JavaScript code to run and display my modal, but this solution only worked for a while.
New problem:
Then a new problem started occurring, if the first partial view is loaded, and you were to load the second partial view without closing the web page, it causes site.js to be loaded twice into the view, once by the first partial view, and a second time by the second partial view. With site.js loaded twice, it somehow causes my post action to be hit twice, resulting in data being inserted twice for one post action.
I then decided to move site.js to my _Layout.cshtml (ideally how it should be) and tried again, this way around caused the partial views to render like normal, but once again, the modals didn't show when clicking on the hyperlinks found in the partial views.
My theory:
The way I understand it, when the jQuery get methods loads the partial views, it prevents the partial view from seeing site.js, even though it was loaded in by my _Layout.cshtml.
What I preferrably don't want to change:
I don't want to get rid of my small get actions, I built it like this to keep my users as much as possible on one page, and calling the get actions seperately reduces their data usage.
Am I loading site.js correctly? Why doesn't my partial views see site.js?
Edit:
This is my _Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
<environment names="Development">
#*Other scripts omitted *#
<script src="~/js/site.js" asp-append-version="true" defer></script>
</environment>
<environment names="Staging,Production">
#*Other scripts omitted *#
<script src="~/js/site.js" asp-append-version="true" defer></script>
</environment>
</head>
<body>
<div id="modal-placeholder"></div>
#RenderBody()
#RenderSection("scripts", required: false)
</body>
</html>
And here is the JavaScript code located inside site.js:
$(function () {
var placeholderElement = $('#modal-placeholder');
var redirectUrl = "";
$('a[data-toggle="ajax-modal"]').click(function (event) {
var url = $(this).data('url');
redirectUrl = window.location.href;
$.get(url).done(function (data) {
placeholderElement.html(data);
placeholderElement.find('.modal').modal('show');
});
});
placeholderElement.on('click', '[data-save="modal"]', function (event) {
event.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
var newBody = $('.modal-body', data);
placeholderElement.find('.modal-body').replaceWith(newBody);
var isValid = newBody.find('[name="IsValid"]').val() == 'True';
if (isValid) {
placeholderElement.find('.modal').modal('hide');
window.location.assign(redirectUrl);
}
}
});
});
});
The JavaScript was written like above to handle server side validation on modals, see the brilliant article here: https://softdevpractice.com/blog/asp-net-core-mvc-ajax-modals/
When you 'paste' the HTML for the panel into the page once returned from the ajax call, it breaks all existing event handlers. So you need to re-bind the handler anchor elements. I would wrap your event handler inside a function, and call that function both on initial page load and also in the ajax success handler (after you've pasted the HTML). Assuming you're using jQuery, it would look something like this:
function bindAjaxModalButtons() {
$('[data-toggle="ajax-modal"]').click(function() {
// ... your event handler code here
});
}
$(function() {
bindAjaxModalButtons(); // attaches event handlers on initial page load
});
Then change your ajax functions like so:
function GetMedInfo() {
$.get('#Context.Request.Scheme://#hostname/#controller/GetMedInfo', {id: #Model.Id}, function (response) {
$("#tabOutMedInfo").html(response);
bindAjaxModalButtons(); // <-- this will attach the event handler to the new buttons
});
}
EDIT: now that I can see your JS file, here is what it should look like:
$(function () {
var placeholderElement = $('#modal-placeholder');
var redirectUrl = "";
bindAjaxModalButtons();
placeholderElement.on('click', '[data-save="modal"]', function (event) {
event.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
var newBody = $('.modal-body', data);
placeholderElement.find('.modal-body').replaceWith(newBody);
var isValid = newBody.find('[name="IsValid"]').val() == 'True';
if (isValid) {
placeholderElement.find('.modal').modal('hide');
window.location.assign(redirectUrl);
}
}
});
});
});
function bindAjaxModalButtons() {
var btns = $('a[data-toggle="ajax-modal"]');
btns.unbind(); // <-- so that existing buttons don't get double-bound
btns.click(function (event) {
var url = $(this).data('url');
redirectUrl = window.location.href;
var placeholderElement = $('#modal-placeholder');
$.get(url).done(function (data) {
placeholderElement.html(data);
placeholderElement.find('.modal').modal('show');
});
});
}

Prestashop 1.6.1.16 - My js file is loaded, but my functions aren't seen

I am new in Prestashop (1.6.1.16).
I work in default prestashop theme (default-bootstrap).
What I did:
I put content in /themes/default-bootstrap/product.tpl:
right after top comments (those about LICENSE and others):
<script type="text/javascript" src="modules/ask_bid/js/ask.js">
</script>
<button onclick="take_asks({$product->id})">See asks</button>
<input type="hidden" id="product-id" value="{$product->id}" />
<input type="hidden" id="customer-id" value="{$id_customer}" />
<!-- Modal -->
<div id="modal" class="modal fade">
<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">Modal Header</h4>
</div>
<div class="modal-body">
<p>Some text in the modal.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Where {$product->id} comes from
/controllers/front/ProductController.php<br>
and {$id_customer} comes from
/override/controllers/front/ProductController.php
I created /modules/ask_bid/js/ask.js where I put next content:
function isJSON(data) {
var ret = true;
try {
JSON.parse(data);
}catch(e) {
ret = false;
}
return ret;
}
function take_asks (id_product) {
$.ajax({
type: 'POST',
url: baseDir + 'modules/ask_bid/ajax.php',
data: 'method=take_asks&id_product='+id_product,
dataType: 'text',
success: function(json) {
if(isJSON(json)) {
var json = JSON.parse(json);
//alert("json: " + json[0].comment);
}
},
error: function() {
alert("error");
}
});
}
Also the modal doesn't act like one
My modal is displayed (not hidden) and that is not normal.
It is right after button instead being "in the air" (I hope you know what I mean).
And I have js error:
The /modules/ask_bid/js/ask.js is loaded (I see this also in INSPECT->f12/Network), but the take_asks() is not seen.
I get next console error (when I press 'Take asks' button):
Uncaught ReferenceError: take_asks is not defined
at HTMLButtonElement.onclick (index.php?
id_product=6&controller=product&id_lang=1:413)
What I tried
-I deleted class_index.php
-I deleted cache (with CTRL-f5)
-I tried to add js file from /override/controllers/front/ProductController.php
but doesn't work and i also don't get errors:
public function setMedia()
{
$this->addJS('modules/ask_bid/js/ask.js');
parent::setMedia();
}
...or...
function init () {
$this->context->controller->addJS('modules/ask_bid/js/ask.js');
parent::init()
}
What do you think I can do?
You should create a hookheader in your module:
public function hookHeader($params)
{
$this->context->controller->addJS(($this->_path).'js/ask.js');
}
You can add it only on product pages with:
public function hookHeader($params)
{
if (!isset($this->context->controller->php_self) || $this->context->controller->php_self != 'product')
return;
$this->context->controller->addJS(($this->_path).'js/ask.js');
}
and in your module install hook to header with
$this->registerHook('header'))
To add content to the product page without changing the themes tpls (as mentioned in comments) you can use the displayFooterProduct that "Add new blocks under the product description.".
public function hookDisplayFooterProduct($params)
{
return "code you want to insert";
}
Inside this hook you can access the following parameters:
$params = array('product' => Product, 'category' => Category)
Also, remember to hook it on install with $this->registerHook('displayFooterProduct')) and if the module is already installed, reset it or manually hook it.

How should i handle calling modal widow from controller under certain conditions

I am trying to create a modal window that will open under some conditions.
I did create a modal window that is opening on button click. What should i change?
My view:
<button type="button" data-toggle="modal" data-target="#popupModal">open</button>
<div id="popupModal"
class="modal hide fade"
tabindex="-1"
role="dialog"
aria-labelledby="popupModalLabel"
aria-hidden="true">
...some html
</div>
</div>
my controller:
if (some conditions)
{
//here i want to open my modal window somehow
}
You can check these conditions in the button click event,
$(button).click(function(){
if (some conditions)
{
$("#popupModal").modal("show");
//$("#popupModal").show();
}
});
I've created jsFiddle with an example for you: JsFIddle
From your controller Send your option in data array when you load your view.
for example, your code should be Something like this..
Controller:
$data = array();
if (some conditions)
{
$data['option1'] = 'modal1';
}
else{
$data['option2'] = 'modal2';
}
$this->load->view('your view file', $data);
View:
if($option1){
// modal 1 html
}
else{
// modal 2 html
}
Try this. i hope you get an idea.

How to pass data from one view to another using RenderAction

I am trying to call a view as modal dialog using RenderAction method. I have to pass some data from the view to the modal dialog's View. How can I achive this?
Below is my code (trimmed as per required) so far.
<table class="table">
<tr>
<th>Project No</th>
<th>Drawing No</th>
<th>Revision</th>
<th>Fabrication</th>
</tr>
#foreach (var irn in Model)
{
<tr>
<td class="projno">#irn.PROJECTNO</td>
<td class="drawingno">#irn.DRAWINGNO</td>
<td class="revno">#irn.REVNO</td>
<td>
<button class="button" type="button" class="btn btn-sm" data-toggle="modal">Add</button>
</td>
</tr>
}
Here is the modal dialog using RenderAction to call another view
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<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="myModalLabel">Enter Fabrication Information</h4>
</div>
<div class="modal-body">
<div>
#{Html.RenderAction("Create", "Fabrication");}
</div>
</div>
</div>
</div>
Here are two ways I have tried to invoke the modal dialog
<script type="text/jscript">
$(document).ready(function ()
{
$('button').click(function ()
{
var $row = $(this).closest("tr") // Finds the closest row <tr>
var $projectNo = $row.find(".projno") // Gets a descendent with class="nr"
.text(); // Retrieves the text within <td>
var link = '#Url.Action("Create", "Fabrication")'
// Method 1 -
$('#myModal').modal('show');
//Method 2
$.ajax({
type: "GET",
url: link,
error: function (data)
{ },
success: function (data)
{
$("#myModal.modal-body").html(data);
$('#myModal').modal('show');
},
// in method 2, when I close the dialog, the screen becomes tinted and freezes while it works ok in method 1. why is that?
});
});
});
Here is the Fabrication/Create Conroller method
public ActionResult Create(string projectNo, string drawingNo, string revisionNo)
{
ViewBag.ProjectNo = projectNo;
ViewBag.DrawingNo = drawingNo;
ViewBag.RevisionNo = revisionNo;
return PartialView("Create");
}
When user click Add button, modal dialog should appear carrying ProjectNo information from parent View.
You need to pass the data when invoking controller action.
via JavaScript
When you're sending AJAX request via jQuery, you can use data option property, like in the example below.
If you're sending GET requst, jQuery will automagically append this object to the URL, like so: /Fabrication/Create?projectNo=123&drawingNo=456&revisionNo=789.
Hovewer, if you're sending POST request, URL will not be changed and the data object will be passed inside a request body.
$.ajax({
type: "GET",
url: link,
data: {
projectNo: 123,
drawingNo: 456,
revisionNo: 789
}
error: function (data)
{ },
success: function (data)
{
$("#myModal .modal-body").html(data); // Note that you missed a space between selectors
$('#myModal').modal('show');
},
// in method 2, when I close the dialog, the screen becomes tinted and freezes while it works ok in method 1. why is that?
});
via Razor
You can also use one of the parameter of Html.RenderAction or Url.Action to pass any additional data using anonymous object. This object is always the last function argument, no matter how many arguments you pass before (controller and area names are optional). Note that it's more of a fun fact, because you can't access JavaScript variables directly when using Server-Side methods. It'd be good when rendering default state of your form.
#* Pass custom parameters to controller's action and render it *#
#Html.RenderAction("Create", "Fabrication", new {
projectNo = 123,
drawingNo = 456,
revisionNo = 789
})
#* Create an URL to a controller's action with custom parameters *#
#Url.Action("Create", "Fabrication", new {
projectNo = 123,
drawingNo = 456,
revisionNo = 789
})

Categories