Modal Pop-Up Not Closing Properly After Form Submission - asp.net-core

This is a follow-up to the following post:
Modal Pop-Up Displaying Incorrectly When ModelState.IsValid = false Redirect
My Pop-Up validates correctly but after it process the form data it is not getting closed. Once the data gets loaded in the db, I run the following:
TempData["ID"] = status.IssueID;
return RedirectToAction("edit");
Since the Modal doesn't close, the view data gets populated in the modal and not the window.
If I try to use return View("edit"); the underlying page fails because there is no model data on the page.
Here is the current code that I implemented from the post referenced above:
<script>
$('body').on('click', '.modal-link', function () {
var actionUrl = $(this).attr('href');
$.get(actionUrl).done(function (data) {
$('body').find('.modal-content').html(data);
});
$(this).attr('data-target', '#modal-container');
$(this).attr('data-toggle', 'modal');
});
$('body').on('click', '.relative', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
$('body').find('.modal-content').html(data);
});
})
$('body').on('click', '.close', function () {
$('body').find('#modal-container').modal('hide');
});
$('#CancelModal').on('click', function () {
return false;
});
$("form").submit(function () {
if ($('form').valid()) {
$("input").removeAttr("disabled");
}
});
</script>
Here is the code that I run to open the modal:
<div id="modal-container" class="modal fade" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
</div>
</div>
</div>
Add New Status
And here are the actions when I submit data from the modal:
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult CreateEdit(StatusViewModel model)
{
if (ModelState.IsValid)
{
StatusModel status = new StatusModel();
status.IssueID = model.IssueID;
status.StatusDate = DateTime.Today;
status.Status = model.Status;
status.ColorCode = model.ColorCode;
status.NextStep = model.NextStep;
if (model.addedit == "edit")
{
status.UpdatedByNTID = AppHttpContext.Current.Session.GetString("userntid").ToString();
string done = _adoSqlService.UpdateStatus(status);
}
else
{
status.EnteredByNTID = AppHttpContext.Current.Session.GetString("userntid").ToString();
string done = _adoSqlService.InsertStatus(status);
}
TempData["ID"] = status.IssueID;
return RedirectToAction("edit");
}
else
{
return PartialView("_CreateEdit", model);
}
}
Before I implemented the Javascript code as identified in the link, the modal form closed properly but I couldn't validate. After implementation, the modal form validates but the modal receives the redirect instead of closing. What am I doing wrong

the Modal doesn't close, the view data gets populated in the modal and not the window.
It's the expected result, Ajax render the result of redirection to the modal. You should do the redirection in the done function.
Modify the CreateEdit method:
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult CreateEdit(StatusViewModel model)
{
if (ModelState.IsValid)
{
StatusModel status = new StatusModel();
status.IssueID = model.IssueID;
status.StatusDate = DateTime.Today;
status.Status = model.Status;
status.ColorCode = model.ColorCode;
status.NextStep = model.NextStep;
if (model.addedit == "edit")
{
status.UpdatedByNTID = AppHttpContext.Current.Session.GetString("userntid").ToString();
string done = _adoSqlService.UpdateStatus(status);
}
else
{
status.EnteredByNTID = AppHttpContext.Current.Session.GetString("userntid").ToString();
string done = _adoSqlService.InsertStatus(status);
}
TempData["ID"] = status.IssueID;
}
return PartialView("_CreateEdit", model);
}
Add a hidden input in the partial view to mark if the returned model has passed the validation.
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
Then determine whether to redirect in the script:
$('body').on('click', '.relative', function (e) {
e.preventDefault();
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var dataToSend = form.serialize();
$.post(actionUrl, dataToSend).done(function (data) {
$('body').find('.modal-content').html(data);
var isValid = $('body').find('[name="IsValid"]').val() == 'True';
if (isValid) {
$('body').find('#modal-container').modal('hide');
window.location.href = "/Issue/Edit";
}
});
})
Result:

Related

Form data not updating after calling action with ajax

I have a table in my form and I don't want it to display until a link on the page is clicked which would call an action to get the data and redirect back to the same view so the table is populated. In essence like a master/detail form. I added a css class to hide the table but the only way I know of to call the action is to use ajax. I wrote the code for the function but and it seems to work fine but the table does not populate and I can't figure out why. I am using a partial view for the table which is supposed to be populated after the link is clicked. I'm very new to MVC so I have no idea what I'm missing, any help would be appreciated.
This is the link used in the jquery click event
<td><a id="viewOrder" href="#" data-PONumber-id=#osOrder.PurchaseOrderNumber>View Order</a></td>
This is my jQuery to call the action in the controller
$(document).ready(function() {
$("#viewOrder").on("click",
function(e) {
var button = $(this);
$.ajax({
type: "GET",
url: '#Url.Action("Details", "Receiving")',
data: { "id": button.attr("data-PONumber-id") },
success: function() {
var orderButton = $(".js-Order");
orderButton.removeClass("invisible");
orderButton.addClass("visible");
}
});
});
});
This is my controller code.
public ActionResult Details(int id)
{
var purchaseOrder = _context.PurchaseOrders.Single(p => p.PurchaseOrderNumber == id);
var viewModel = new ReceivngFormViewModel
{
PO = purchaseOrder.PurchaseOrderNumber,
Vendor = purchaseOrder.Vendor.VendorName,
Contact = purchaseOrder.Vendor.Phone,
OutstandingOrders = _context.PurchaseOrders.Where(od =>
od.Closed == false && !String.IsNullOrEmpty(od.PurchaseOrderNumber.ToString()) &&
od.OrderDate != null).ToList(),
ReceivedOrderDetails = _context.PurchaseOrderDetails
.Where(pod => pod.PurchaseOrderID == purchaseOrder.PurchaseOrderID && (pod.Quantity - pod.ReceiveOrderDetails
.Sum(rod => rod.QuantityReceived)) != 0)
.Select(pod => new ReceivedOrderDetail
{
PurchaseOrderId = pod.PurchaseOrderID,
PurchaseOrderDetailId = pod.PurchaseOrderDetailID,
PartId = pod.PartID,
PartDescription = pod.Part.Description,
QtyOnOrder = pod.Quantity,
QtyOutstanding = pod.ReceiveOrderDetails.Select(rod => rod.QuantityReceived).Any() ?
pod.Quantity - pod.ReceiveOrderDetails.Sum(rod => rod.QuantityReceived) : pod.Quantity
}).ToList()
};
return View("Index", viewModel);
}

Calling HttpGet Method Using Ajax on IpagedListPager Mvc

I want to call HttpGet method on every page using ajax using IpagedList in MVC
Controller [HttpGet]
public ActionResult TestStarted(int TestId,DateTime End_Time,int page=1)
{
ViewBag.ct = 0;
ViewBag.TestId = TestId;
var Questions = GetNoOfQuestions().ToList();
ViewBag.Questions = Questions;
EAssessmentNew.BAL.StudentBal studBal = new EAssessmentNew.BAL.StudentBal();
EAssessmentNew.Dal.Student_Answer_Master _studAnsdal = new EAssessmentNew.Dal.Student_Answer_Master();
String TestName = studBal.FetchTestName(TestId);
ViewBag.TestName = TestName;
ViewBag.EndTime = End_Time;
List<Question> model = new List<Question>();
model = new Test_Planning().Fetch_Question_By_Test(TestId);
ViewBag.total = model.Count();
if (Request.QueryString["cnt"] != null)
{
int count = Convert.ToInt16(Request.QueryString["cnt"].ToString());
List<int> ChkOptions = studBal.GetCheckedAnswers((int)TestId, model[count].QuestionId, (int)(studBal.getStudentId(Session["sname"].ToString())));
ViewBag.ChkOptions = ChkOptions;
int cnt = 0;
if (ChkOptions.Count() != 0)
{
for (int i = 0; i < model[count].Options.Count(); i++)
{
if (model[count].Options[i].OptionId == ChkOptions.ElementAt(cnt))
{
model[count].Options[i].IsChecked = true;
cnt++;
}
else
{
model[count].Options[i].IsChecked = false;
}
if (cnt >= ChkOptions.Count() - 1)
{
cnt = ChkOptions.Count() - 1;
}
}
}
return View(model.OrderByDescending(v => v.Question_Id).ToPagedList(page, 1));
}
else
{
return View(model.OrderByDescending(v => v.Question_Id).ToPagedList(page, 1));
}
}
My View
<script type="text/javascript">
var TestId ='#ViewBag.TestId'
function loadQuestions() {
alert("ok")
$.ajax({
url: '#Url.Action("Student","TestStarted")',
data: { TestId:TestId },
contentType:"application/json",
success:function(responce){
}
});
}
</script>
<div class="pagedList">
#Html.PagedListPager(Model, page => Url.Action( "",new { onclick="loadQuestions()"}), PagedListRenderOptions.TwitterBootstrapPager)
</div>
I have done paging using IpagedList i want to call HttpGet Method Of controller on each and every page but i want this to perform without page refresh i have written ajax for it now i just want to know how can i call that ajax method using #Html.PagedListPager and on onClick event
Just Correct your url in ajax request as :
Instead of this
url: '#Url.Action("Student","TestStarted")'
It should be this
url: '#Url.Action("TestStarted","Student")'
and #Html.PagedListPager produces 'anchor' tag in html so you can put a click event on document as shown :-
$(document).on('click', 'a', function() {
$.ajax({
url: this.href,
type: 'GET',
datatype: "html",
data :{ TestId : $("#TestId").val(), End_Time :$("#End_Time").val(), page :$("#page").val() }
cache: false,
success: function(result) {
$('#results').html('');
$('#results').html(result);
}
});
return false;
});
In above code click event binds with every 'anchor' tag so if you need for specific 'anchor' tags then you can specify class as $(.pager).on('click', 'a', function() {}) and here '#results' is the target div id whose html is coming from controller action in your case this id may be different.

Creating a pdf from a Knockout JS view & viewModel

Scenario: In our application a user can create a invoice by filling in certain fields on a Knockout view. This invoice can be previewed, via another Knockout page. I want to use the preview url within our PDF creator (EVOPdf), so we can provide the user with a PDF from this invoice.
To preview the invoice we load the data (on document ready) via an ajax-request:
var InvoiceView = function(){
function _start() {
$.get("invoice/GetInitialData", function (response) {
var viewModel = new ViewModel(response.Data);
ko.applyBindings(viewModel, $("#contentData").get(0));
});
};
return{
Start: _start
};
}();
My problem is within the data-binding when the PDF creator is requesting the url: the viewModel is empty. This makes sense because the GetInitialData action is not called when the PDF creator is doing the request. Calling this _start function from the preview page directly at the end of the page does not help either.
<script type="text/javascript">
$(document).ready(function() {
InvoiceView.Start();
});
</script>
Looking at the documentation of EvoPdf, JavaScript should be executed, as the JavaScriptEnabled is true by default: http://www.evopdf.com/api/index.aspx
How could I solve this, or what is the best approach to create an pdf from a knockout view?
Controller action code:
public FileResult PdfDownload(string url)
{
var pdfConverter = new PdfConverter();
// add the Forms Authentication cookie to request
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
pdfConverter.HttpRequestCookies.Add(
FormsAuthentication.FormsCookieName,
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
}
var pdfBytes = pdfConverter.GetPdfBytesFromUrl(url);
return new FileContentResult(pdfBytes, "application/pdf");
}
Javascript:
var model = this;
model.invoiceToEdit = ko.observable(null);
model.downloadInvoice = function (invoice) {
model.invoiceToEdit(invoice);
var url = '/invoice/preview';
window.location.href = '/invoice/pdfDownload?url=' + url;
};
The comment of xdumaine prompted me to think into another direction, thank you for that!
It did take some time for the Ajax request to load, but I also discovered some JavaScript (e.g. knockout binding) errors along the way after I put a ConversionDelay on the pdf creator object
pdfConverter.ConversionDelay = 5; //time in seconds
So here is my solution for this moment, which works for me now:
To start the process a bound click event:
model.downloadInvoice = function (invoice) {
var url = '/invoice/preview/' + invoice.Id() + '?isDownload=true';
window.open('/invoice/pdfDownload?url=' + url);
};
which result in a GET resquest on the controller action
public FileResult PdfDownload(string url)
{
var pdfConverter = new PdfConverter { JavaScriptEnabled = true };
// add the Forms Authentication cookie to request
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
pdfConverter.HttpRequestCookies.Add(
FormsAuthentication.FormsCookieName,
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
}
pdfConverter.ConversionDelay = 5;
var absolutUrl = ToAbsulte(url);
var pdfBytes = pdfConverter.GetPdfBytesFromUrl(absolutUrl);
return new FileContentResult(pdfBytes, "application/pdf");
}
The Pdf creator is requesting this action on the controller, with isDownload = true (see bound click event):
public ActionResult Preview(string id, bool isDownload = false)
{
return PartialView("PdfInvoice", new InvoiceViewModel
{
IsDownload = isDownload,
InvoiceId = id
});
}
Which returns this partial view:
PartialView:
// the actual div with bindings etc.
#if (Model.IsDownload)
{
//Include your javascript and css here if needed
#Html.Hidden("invoiceId", Model.InvoiceId);
<script>
$(document).ready(function () {
var invoiceId = $("#invoiceId").val();
DownloadInvoiceView.Start(invoiceId);
});
</script>
}
JavaScript for getting the invoice and apply the knockout bindings:
DownloadInvoiceView = function() {
function _start(invoiceId) {
$.get("invoice/GetInvoice/" + invoiceId, function(response) {
var viewModel = new DownloadInvoiceView.ViewModel(response.Data);
ko.applyBindings(viewModel, $("#invoiceDiv").get(0));
});
};
return {
Start: _start
};
}();
DownloadInvoiceView.ViewModel = function (data) {
var model = this;
var invoice = new Invoice(data); //Invoice is a Knockout model
return model;
};

knockout.js api search form

The goal of my code is to search on the API search string:
So if you fill out the form you get the hits bij name.
I used the following Knockout.js script:
var viewModel=
{
query : ko.observable("wis"),
};
function EmployeesViewModel(query)
{
var self = this;
self.employees = ko.observableArray();
self.query = ko.observable(query);
self.baseUri = BASE + "/api/v1/search?resource=employees&field=achternaam&q=";
self.apiurl = ko.computed(function() {
return self.baseUri + self.query();
}, self);
//$.getJSON(baseUri, self.employees);
//$.getJSON(self.baseUri, self.employees);
$.getJSON(self.apiurl(), self.employees);
};
$(document).ready(function () {
ko.applyBindings(new EmployeesViewModel(viewModel.query()));
});
The html binding is:
<input type="text" class="search-query" placeholder="Search" id="global-search" data-bind="value: query, valueUpdate: 'keyup'"/>
But if i fill the text box i onley get the default "wis" employees? What am I doing wrong?
Not entirely sure what is wrong here, but have you debugged it and seen what the value of query is in apiurl?
One potential issue is that you are passing employees to getJSON as the observable, not the underlying array, so you could try:
$.getJSON(self.apiurl(), self.employees());
After some digging I found a solution.
var employeesModel = function(){
var self = this;
self.u = base +'/api/v1/search';
self.resource = 'employees';
self.field = 'achternaam';
self.employees = ko.observableArray([]);
self.q = ko.observable();
//Load Json when model is setup
self.dummyCompute = ko.computed(function() {
$.getJSON(self.u,{'resource': self.resource, 'field': self.field, 'q':self.q }, function(data) {
self.employees(data);
});
}, self);
};
ko.applyBindings(new employeesModel());

Codeigniter get random record with limit

I've created a page that load 4 products everytime you scroll down the page with codeigniter and Ajax.
I followed this tutorial for create pagination with codeigniter and jQuery.Everything works fine, moreover I've changed the load type from database using Ajax.
I've got a problem with codeigniter.
When I try to get random record from the table I've got duplicate products.
This is the functions in codeigniter:
UPDATE CONTROLLER
function index()
{
$this->load->helper('url');
$data['description'] = "Description";
$data['keywords'] = "Keywords";
$data['products'] = $this->abitainterni->getAllProductsLimit();
$data['get_products'] = $this->abitainterni->get_products();
$this->load->view('welcome', $data);
}
function get_products($offset)
{
$already_used = $this->input->post('already_used');
$already = explode(',', $already_used);
$data['products'] = $this->abitainterni->getAllProductsLimit($offset, $already);
$arr['view'] = $this->load->view('get_products', $data, true);
$bossy = '';
foreach($data['products'] as $p) {
$bossy .= $p->productID.',';
}
$arr['view'] = $bam;
$arr['ids'] = $bossy;
echo json_encode($arr);
return;
}
UPDATE SCRIPT
<script type="text/javascript">
$(document).ready(function(){
<?
$like_a_boss = "";
foreach($products as $gp):
$like_a_boss .= $gp->productID.',';
endforeach;
?>
var products = '<?= $like_a_boss; ?>';
var loaded_products = 0;
$(".loadMoreProducts").click(function(){
loaded_products += 4;
var dati = "welcome/get_products/" + loaded_products;
$.ajax({
url:'welcome/get_products/' + loaded_products,
type: 'post',
data: {already_used: products},
cache: false,
success: function(data) {
var obj = $.parseJSON(data);
$("#mainContainerProductWelcome").append(obj.view);
already_used += obj.ids;
if(loaded_products >= products - 4) {
$(".loadMoreProducts").hide();
} else {
// load more still visible
}
},
error: function() {
// there's something wrong
}
});
// show spinner on ajax request starts
$(".loading-spinner").ajaxStart(function(){
$(".loading-spinner").show();
$(".text-load").hide();
});
// ajax request complets hide spinner
$(".loading-spinner").ajaxStop(function(){
$(".loading-spinner").delay(5000).hide();
$(".text-load").show();
});
return false;
});
// submit form contact
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() >= $(document).height()) {
// click on load more btn
$(".loadMoreProducts").click();
return false;
}
});
});
</script>
you'll need to keep track of the products you've already queried, throw their id's in an array, and then use something like a where not in. So something like this:
function getAllProductsLimit($offset=0, $already_used = array(0))
{
$this->db->order_by('productID', 'RANDOM');
$this->db->where_not_in('productID', $already_used);
$query = $this->db->get('product', 4, $offset);
if($query->num_rows() > 0){
return $query->result();
} else {
return 0;
}
}
NEW CONTROLLER
function index()
{
$this->load->helper('url');
$data['title'] = "Scopri i nostri prodotti";
$data['description'] = "Description";
$data['keywords'] = "Keywords";
$data['products'] = $this->abitainterni->getAllProductsLimit();
$data['get_products'] = $this->abitainterni->get_products();
$this->load->view('welcome', $data);
}
function get_products($offset)
{
$already_used = $this->input->post('already_used');
$already = explode(',', $already_used);
$data['products'] = $this->abitainterni->getAllProductsLimit($offset, $already);
$arr['view'] = $this->load->view('get_products', $data, true);
$bossy = '';
foreach($data['products'] as $p)
{
$bossy .= $->productID.',';
}
$arr['view'] = $bam;
$arr['ids'] = $bossy;
echo json_encode($arr);
return;
}
NEW SCRIPT
<script type="text/javascript">
$(document).ready(function(){
<?
$like_a_boss = '';
foreach($get_products as $gp):?>
$like_a_boss .= $gp->productID.',';
endforeach;?>
var products = '<?= $like_a_boss; ?>';
var loaded_products = 0;
$(".loadMoreProducts").click(function(){
loaded_products += 4;
var dati = "welcome/get_products/" + loaded_products;
$.ajax({
url:'welcome/get_products/' + loaded_products,
type: 'post',
data: {already_used: products},
cache: false,
success: function(data) {
var obj = $.parseJSON(data);
$("#mainContainerProductWelcome").append(obj.view);
already_used += obj.ids;
if(loaded_products >= products - 4) {
$(".loadMoreProducts").hide();
} else {
// load more still visible
}
},
error: function() {
// there's something wrong
}
});
// show spinner on ajax request starts
$(".loading-spinner").ajaxStart(function(){
$(".loading-spinner").show();
$(".text-load").hide();
});
// ajax request complets hide spinner
$(".loading-spinner").ajaxStop(function(){
$(".loading-spinner").delay(5000).hide();
$(".text-load").show();
});
return false;
});
// submit form contact
$(window).scroll(function() {
if($(window).scrollTop() + $(window).height() >= $(document).height()) {
// click on load more btn
$(".loadMoreProducts").click();
return false;
}
});
});
</script>
Then whereever your using that function, before you echo out your results to your ajax function, run a quick foreach to add the ids of the products you just got to the already used array. You can either store this is session, or pass it back and forth between your ajax stuff, or if your ajax stuff is written fine, you don't need to worry about it, just attach the product id's to each product you're displaying using a data attribute or something and generate the array that way.