Controller code gets called twice - asp.net-mvc

I have an MVC view which will have menu and detail sections. As items are clicked on the menu, I would like the detail section to get updated. The menu and detail would be PartialViews.
My main view is layed out like this (so far):
#model WorkflowData.ProjectWorkflow
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="form-horizontal">
<h4>Project Workflow</h4>
<hr />
<div>
#{Html.RenderPartial(
"_WorkflowSteps",
Model.ProjectWorkflowSteps.ToList());
}
</div>
<div id="StepDetail"></div>
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section scripts{
#Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
<script>
$(function () {
$('.workflow-step').on('click', function (e) {
$.get($(this).prop('href'), function (response) {
$('#StepDetail').html(response)
});
});
});
</script>
}
The '_WorkflowSteps' partial view renders links using this:
#Html.ActionLink(
item.StepName,
"Step",
new { id = item.ProjectworkflowPages.FirstOrDefault().ProjectWorkflowPageId },
new { #class = "workflow-step" });
My controller action for Step is:
public ActionResult Step(int id)
{
if (id == null)
return RedirectToAction("Index");
using (var _db = new MarkTestDbEntities())
{
var stepPage = (from s in _db.ProjectworkflowPages
where s.ProjectWorkflowPageId == id
select s).FirstOrDefault();
var projectModel = new Project
{
ProjectWorkflowId = stepPage.ProjectWorkflowStep.ProjectWorkflowId
};
return PartialView(string.Format("../{0}/{1}",stepPage.Controller, stepPage.CreateAction)
, projectModel);
}
return View();
}
What is happening now is I see the div get populated with the partial view, then the page refreshes with just the partial view from Step. Debugging I see that the Step action is actually called twice, but when I look at the rendered source, I don't see why. Any thoughts?

Your elements with class="workflow-step" are links which make a GET call to your Step() method. You are handling the .click() event of those elements and making a ajax call, but your not cancelling the default redirect so its doing both, the $.get() followed by the redirect. You need to cancel the default action by including return false; in the script
$('.workflow-step').on('click', function (e) {
$.get($(this).prop('href'), function (response) {
$('#StepDetail').html(response)
});
return false; // add this
});

Related

return partialview after httppost asp.net mvc

I am creating a web app in asp.net mvc
I have a partial view to reset password which opens inside bootstrap modal,
now on HttpPost I have a if condition which looks like below
if (string.IsNullOrEmpty(model.NewPassword))
{
TempData["PMessage"] = CustomHelper.Translate("Please Enter new Password");
return PartialView(model);
}
but my main url is changing to this partial view like below
http://localhost:8080/User/ResetPassword
here ResetPassword is my partialview name
my partialView looks like below
#(Html.BeginForm("ResetPassword", "User", FormMethod.Post))
{
<div class="modal-dialog modal-sm">
<!-- Modal data-->
</div>
}
my full post method
[HttpPost]
public ActionResult ResetPassword(ResetPasswordModel model)
{
if (string.IsNullOrEmpty(model.NewPassword))
{
TempData["PMessage"] = "Please Enter new Password";
return PartialView(model);
}
//if success
return RedirectToAction("Index");
}
how can I prevent this, I just want to load modal and not whole page,
what can be the fixes here?
or Should I go for client side validation?
Page flow in detail
this is how my page looks
if the user clicks on reset password, the below bootstrap modal appears
without entering any data if user clicks on OK, post method has been called and the page is being redirected to resetpassword.cshtml as I am returning like below
return PartialView(model);
what I need to do if I only want to refresh modal/partial view on validation on controller
You are making normal HTTP request it's used to handle regular browser calls (like form submits) and return full HTML pages to client.
In your case you have to make ajax request to handle ResetPassword post method to avoid redirect.
<!-- modal placeholder-->
<div id='myModal' class='modal fade in'>
<div class="modal-dialog">
<div class="modal-content">
<div id='myModalContent'></div>
</div>
</div>
</div>
this url return your reset form and load it into a modalcontent
<a href="#Url.Action("Reset", "Account", new { class = "resetpassword" })" ></a>
$(function () {
$(document).on("click", "a.resetpassword", function (e) {
$('#myModalContent').load(this.href, function () {
$('#myModal').modal({
/*backdrop: 'static',*/
keyboard: true
}, 'show');
bindForm(this);
});
return false;
});
});
function bindForm(dialog) {
$('form', dialog).submit(function (e) {
e.preventDefault();
if ($('#resetFormId').valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.success) {
$('#myModal').modal('hide');
} else {
$('#myModalContent').html(result);
bindForm(dialog);
}
}
});
}
return false;
});
}

ASP.NET MVC 5: How do you render a partial view in a div from an action link?

I am new to ASP.NET and web development of any kind. I have searched for many hours for a solution to my problem, but I can't seem to get my partial view to show up in the div.
What is happening is the view is showing up, but it's replacing the entire page instead of showing up in the div within the main page.
My controller looks something like this:
public class MyController
{
public ActionResult ShowView(int id)
{
// get view model from id
return PartialView(viewModel);
}
}
My Main View looks something like this
#model List<viewModelType>
<div>
#foreach (var item in Model)
{
<div>
#Html.ActionLink(item.Name, "ShowView", new { id = item.ID }, new { #class = "btn-sm" })
</div>
}
</div>
<div>
// here is where I want to partial view to go!
</div>
This is what the partial view looks like:
#model viewModelType
<div>Model.DataToDisplay</div>
Would this work for you?
[ChildActionOnly]
public class MyController
{
public ActionResult ShowView(int id)
{
// get view model from id
return PartialView(viewModel);
}
}
And in your view:
<div>
// here is where I want to partial view to go!
#Html.Action("ShowView")
</div>
Okay I figured it out with Christos' help.
The main view should look like this:
#model List<viewModelType>
<div>
#foreach (var item in Model)
{
<div>
<button class="js-showViewBtn" data-itemId=#item.ID>item.Name</button>
</div>
}
</div>
<div class="js-show-view">
// here is where I want to partial view to go!
</div>
<script type="text/javascript">
$(function () {
$('.js-showViewBtn').click(function (e) {
e.preventDefault();
var itemId = $(this).data("itemId");
$.ajax({
method: "GET",
url: "/MyController/ShowView",
data: { id: itemId },
cache: false
}).success(function(data){
$('.js-show-view').html(data);
});
})
});
</script>
For some reason the id of the item was not being returned, so I tried it like this and it worked. Hope this helps others too.
Thanks for your help Christos.
You need a bit of JavaScript to do that you want. Basically, you have to wire up a click event handler for your links and when the user click on one of them an ajax request would be triggered.
#model List<viewModelType>
<div>
#foreach (var item in Model)
{
<div>
#Html.ActionLink(item.Name, "ShowView", new { id = item.ID }, new { #class = "btn-sm js-show-view-btn" })
</div>
}
</div>
<div class="js-show-view">
// here is where I want to partial view to go!
</div>
<!-- Before the following script been loaded, he jQuery should have been loaded -->
<script>
$(function(){
$(".js-show-view-btn").click(function(e){
e.preventDefault();
$.ajax({
method: "GET",
url: "/MyController/ShowView",
data: { id = $(e).id },
cache: false
}).success(function(data)
{
$(".js-show-view").html(data);
});
})
});
</script>

Wrong Function Being Called From MVC View

I have an MVC view, which is launched by a function in the controller. That view has a button that I want to use to submit data to a different function in that same controller, but it always go back to the function that launched it instead.
The controller is called, the ViewForPrepare view is launched from PrepareList, I hit the button on ViewForPrepare, and it submits to PrepareList again instead of RunList.
In the controller I have:
public ActionResult PrepareList(int Key)
{
return "ViewForPrepare";
}
public ActionResult RunList(int Key)
{
return "OtherView";
}
Then in the View:
<input type="button" value="Submit Report" id="submit">
<script type="text/javascript">
$(document).ready(function () {
$('#submit').click(function () { window.location ='#Url.RouteUrl("RunList", new { Key = #Model.caseNumber })' });
});
</script>
So I press the button to go to RunList, but it keeps going to PrepareList. I've checked the routing and it looks OK. What do I need to do to get the button to submit to RunList?
You're code right now basically says "When I click the submit button. Change the window's location to something else." If that is what you want, try using
#Url.Action("RunList", new { Key = Model.caseNumber })
instead of
#Url.RouteUrl("RunList", new { Key = #Model.caseNumber })
and try using a <button> element instead of an <input> element.
If what you want is to post the data from the form, you should wrap your button in a form tag (make sure to replace "ControllerName" below with your actual controller.)
#Html.BeginForm("RunList","ControllerName", new { Key = Model.caseNumber })
{
<input type="submit" value="Submit Report" id="submit">
}
and get rid of the javascript altogether as it isn't necessary in this case. Also you will have to mark your RunList action as HttpPost for this to work.
[HttpPost]
public ActionResult RunList(int Key)
{
return "OtherView";
}
Why dont you just use a RouteLink instead of the Input?
#Html.RouteLink("Submit Report", "RunList", new { Key = Model.caseNumber }, new {#class="btn" })
not sure if you're using bootstrap or jquery ui but there are css classes to make links look like buttons.
ActionLink works the same way.
#Html.ActionLink("Submit Report", "RunList", "ViewForPrepare ", new { Key = Model.caseNumber }, new { #class = "btn" })
Using VS2015 Pro I created a project using the MVC template.
HomeController.cs added:
public ActionResult PrepareList(int Key)
{
return View();
}
public ActionResult RunList(int Key)
{
return View(); ;
}
Index.cshtml added:
#Html.ActionLink("Submit Report", "RunList", new { Key = 4 }, new { #class = "btn" })
Put a break point in "RunList" and it worked!
Using
<input type="button" value="Submit Report" id="submit">
#section script{
<script type="text/javascript">
$(document).ready(function () {
$('#submit').click(function () { window.location ='#Url.RouteUrl("RunList", new { Key = 4 })' });
});
</script>
}
I got the meessage
A route named 'RunList' could not be found in the route collection.
when you need to send data, you must add the attribute HttpPost to this action:
[HttpPost]
public ActionResult RunList(int Key)
{
return "OtherView";
}

MVC 5 partial view async postback

I have a sort of Master-Detail Edit form and I'm trying to follow this post: Using Ajax... to get the partial view to postback.
My Edit form has a partial view that has a list of sub items, and another partial create view in it to add new items. I'd like the partial create view to post back and update the list without refreshing the whole page if possible.
Here's what I have so far:
MyController.cs -
public ActionResult Edit(int? id)
{
//...
ViewBag.CustomFormId = id;
using (var _db = new MkpContext())
{
//...
return View(profileEdit);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomForm editForm)
{
//...
if (!ModelState.IsValid) return View(editForm);
using (var _db = new MkpContext())
{
var form = _db.CustomForms.Find(editForm.CustomFormId);
//...
_db.Entry(form).State = EntityState.Modified;
_db.SaveChanges(User.ProfileId);
return RedirectToAction("Index");
}
}
public ActionResult _CustomFieldList(int id)
{
ViewBag.CustomFormId = id;
using (var _db = new MkpContext())
{
var formCustomFields = (from cf in _db.CustomFields
where cf.CustomFormId == id
select cf);
return PartialView(formCustomFields.ToList());
}
}
// Nested in _CustomFieldList
public ActionResult _CustomFieldCreate(int id)
{
var newField = new CustomField
{
CustomFormId = id
};
return PartialView(newField);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult _CustomFieldCreate(CustomField addField)
{
ViewBag.CustomFormId = addField.CustomFormId;
if (ModelState.IsValid)
{
using (var _db = new MkpContext())
{
_db.CustomFields.Add(addField);
_db.SaveChanges();
}
var newField = new CustomField
{
CustomFormId = addField.CustomFormId
};
return PartialView(newField); // Probably need to change this somehow
}
return PartialView(addField);
}
And the views:
Edit.cshtml -
#model PublicationSystem.Model.CustomForm
#{
ViewBag.Title = "Edit Custom Form";
Layout = "~/Views/Shared/_LayoutSmBanner.cshtml";
}
<div class="form-horizontal">
<div class="row">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#* Fields for this form *#
}
<div id="CustomFields" class="col-md-6">
#Html.Action("_CustomFieldCreate", new { id = ViewBag.CustomFormId })
</div>
</div>
</div>
<script>
$(function () {
$("#createFieldForm").on("submit", function (e) {
e.preventDefault(); //This prevent the regular form submit
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$("#CustomFields").html(result);
}
});
return false;
});
});
</script>
_CustomFieldCreate.cshtml -
#model PublicationSystem.Model.CustomField
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div id="result"></div>
<div class="form-horizontal">
<h4>CustomField</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model =>model.CustomFormId)
<div class="row">
#* Fields for the form *#
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div id="customFieldList">
#Html.Action("_CustomFieldList", new { id = ViewBag.CustomFormId })
</div>
_CustomFieldList.cshtml
#model System.Collections.Generic.IEnumerable<PublicationSystem.Model.CustomField>
<table class="table">
#* List table code *#
</table>
Edit: I rewrote the pages so that the list is part of the create partial view. What happens now is, if you enter data for _CustomFieldCreate and press submit, the first time, it refreshes just that view (including the nested list view). However the second time, it redirects to the view, probably because the first refresh didn't rebind the javascript to the submit button. Also, the Create view doesn't clear out the fields, but persists the originally entered data.
You will need a form in your partial view whose submit action binds to a javascript function that posts to your controller.
For example if your form id is MyForm:
$('#MyForm').on('submit', function (e) {
e.preventDefault(); //This prevent the regular form submit
$.ajax({
url: $(this).action, // This will submit the post to whatever action your form goes to
type: "POST", // This tells it that it is a post
data: $(this).serialize(), // This sends the data in the form to the controller
success: function (data) {
// do some javascript on success
},
error: function (xhr, ajaxOptions, thrownError) {
// do some javascript on error
}
});
});
This javascript overrides the default form submit and does an ajax post to your controller and then returns with success or error where you can do anything you want.
Here is some jquery ajax documentation:
http://api.jquery.com/jquery.ajax/
You should look into using AJAX. That should accomplish what I think you are describing. You'll want to create a javascript function that handles the submit event on the form, then post the form data to some create action in your MVC app using AJAX. If you are using jQuery, the library makes it pretty simple.
http://api.jquery.com/jquery.ajax/

Can you just update a partial view instead of full page post?

Is there a way to submit a partial view form in asp.net mvc without reloading the parent page, but reloading the partial view only to its new state? Similar to how knockout.js updates using data-bind.
My data table renders with a variable number of columns/names so I don't think knockout.js is an option for this one, so I am trying to use a partial view instead.
Not without jQuery.
What you would have to do is put your Partial in a div, something like:
<div id="partial">
#Html.Partial("YourPartial")
</div>
Then, to update (for example clicking a button with the id button), you could do:
$("#button").click(function () {
$.ajax({
url: "YourController/GetData",
type: "get",
data: $("form").serialize(), //if you need to post Model data, use this
success: function (result) {
$("#partial").html(result);
}
});
})
Then your action would look something like:
public ActionResult GetData(YourModel model) //that's if you need the model
{
//do whatever
return View(model);
}
Actually, if your Partial has a child action method, you can post (or even use an anchor link) directly to the child action and get an Ajax-like affect. We do this in several Views.
The syntax is
#Html.Action("MyPartial")
The Child Action is
public ActionResult MyPartial()
{
return PartialView(Model);
}
If your form posts to the child action
#using (Html.BeginForm("MyPartial"))
{
    ...
}
The Partial View will be updated with the partial view returned from the child action.
Jquery is still a legitimate way to update a partial. But technically, the answer to your question is YES.
As normal what I find when looking for things like this is people give too limited information so I will attempt to help here. The key is to set up a div with an ID you can append the return html to. Also when hitting your controller make sure it returns the partial. There are some potential problems with this method but on a good day it should work.
<div id="CategoryList" class="widget">
#{
Html.RenderPartial("WidgetCategories.cshtml");
}
</div>
function DeleteCategory(CategoryID) {
$.get('/Dashboard/DeleteWidgetCategory?CategoryID=' + CategoryID,
function (data) {
if (data == "No") {
alert('The Category has report widgets assigned to it and cannot be deleted.');
}
else {
$('#CategoryList').html(data);
}
}
);
}
[HttpGet("DeleteWidgetCategory")]
[HttpPost("DeleteWidgetCategory")]
public IActionResult DeleteWidgetCategory(string CategoryID)
{
string Deleted = CategoryModel.DeleteCategory(CategoryID);
if (Deleted == "Yes")
{
return PartialView("WidgetCategories");
}
else
{
return this.Json("No");
}
}
I would use the Ajax Form helper for such scenarios using a partial view and #html.RenderPartial("partialName")
partial helpers
In your Main View
<div id=SearchResult>
#Html.Partial("_NameOfPartialView", Model)
</div>
<input type="button" id="btnSubmit" value="Submit">
In your Javascript file
$('#btnSubmit').click(function () {
GetData(Id);
});
function GetData(Id){
$.ajax({
url: "/Home/GetEmployee/",
type: "get",
data: { Id:Id },
success: function (result) {
$('#SearchResult').html(result);
}
});
}
In your Home Controller
public ActionResult GetEmployee(int Id)
{
var employee= context.Employee.Where(x=> x.EmployeeId == Id)
return this.PartialView("_NameOfPartialView", employee);
}

Resources