we are writing an ASP.NET MVC3 Application and want to do it "rich", for example by using bootstrap und modal dialogs.
I now wonder how to implement modal dialogs, without breaking all the cool staff from ASP.NET (ModelErrors, ...).
The workflow should be like this:
IndexView with a list of items, each item with an actionlink that shows a modal dialog
#Ajax.ActionLink(
"Edit", // Link Text
"Edit", // ActionMethod
new { id = item.Id }, // RouteValues
new AjaxOptions {
HttpMethod = "Get",
OnBegin = "modal.showModalDiv()",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "modal-div",
OnSuccess = "modal.ajaxSuccess()" },
new { data-toggle = "edit-modal" } // HTML-Attributes
)
The modal dialog (simple div styled with css) renders the editview (returned from controller actionmethode)
[HttpGet]
public ActionResult Edit(int id) {
// Load Data and create Model
var model = new ...
return PartialView(model);
}
The form in the edit view can be used to edit the item, including client-side validation
#{
AjaxOptions ajaxOptions = new AjaxOptions() {
HttpMethod = "Post", OnSuccess="modal.hideModalDiv()"
};
}
#using (Ajax.BeginForm("Edit"), ajaxOptions){
... element to edit item ...
<input type="submit" value="submit" />
}
When the submit edit controller-methode recognizes errors (not caught by client-side-validation) the page should be displayed again with model-errors. Otherwize the index-page should be shown or the table refreshed and the modal dialog closed.
[HttpPost]
public ActionResult Edit(Guid id, ItemModel model) {
try{
...Save Item ...
return RedirectToAction("Index")
} catch (Exception ex){
ModelState.AddModelError("", "An error occured")
return PartialView(model);
}
}
My Problem is: How to implement step 4? Does Someone has an advice?
Try with this,
[HttpPost]
public ActionResult Edit(Guid id, ItemModel model)
{
if(model != null && ModelState.IsValid)
{
return RedirectToAction("Index")
}
else
{
return PartialView(model);
}
}
I guess you've already solved this problem or found a work around but in case you have not I suspect if you were to return a partial view that contains javascript to do the redirection you could get the behaviour you describe.
For example:
RedirectToIndex.cshtml
#{ Layout = null; }
<script type="text/javascript">
window.location.href = "#Url.Action("Index")";
</script>
Then update your action to return this partial view.
[HttpPost]
public PartialViewResult Edit(Guid id, ItemModel model) {
try{
//Save Item ...
return PartialView("RedirectToIndex")
} catch (Exception ex){
ModelState.AddModelError("", "An error occured");
return PartialView(model);
}
}
Not the most graceful but should work, I say should as I have not tested this...
Related
I have this URL
var url = "/test/Create/" + $("#hdnFlag").val() +"?CmpT="+"Ilim";
window.location.href = url;
and in my Test controller I do this to get query string value
tM_PMO.Type = Request.QueryString["CmpT"];
But always give me null values.
There is a difference between the GET and POST types.
Query string can be read with the URL of the GET request. However, you cannot read the Query string value in the URL when you make a POST request. For this you need to submit it to the server.
Below I give you a few examples of usage.
GET Request
You can read Query string with URL as below
public ActionResult Test(string CmpT)
{
if (!string.IsNullOrWhiteSpace(CmpT))
{
//your codes...
}else
{ }
return View();
}
POST Request
If you are making a POST request and trying to read from the URL, it will return null. In order to read it, you need to send this value to the server as follows.
1st way : In your Html.BeginForm in your View Below, submit Query string as below and read this value as Action parameter
View Page
#using (Html.BeginForm("Test", "XController", new { returnUrl = Request.QueryString["CmpT"] }, FormMethod.Post, new { role = "form" }))
{
<button type="submit">Send</button>
}
Controller
public ActionResult Test(string returnUrl)
{
if (!string.IsNullOrWhiteSpace(returnUrl))
{
//your codes...
}else
{ }
return View();
}
2nd way : Create a hidden form element as part of the form between the Html.BeginForm tags in your view page and give its value as a query string. Then call it in Action method like below.
View Page
#using (Html.BeginForm("Test", "XController", FormMethod.Post, new { role = "form" }))
{
#Html.Hidden("returnUrl", Request.QueryString["CmpT"])
<button type="submit">Send</button>
}
Controller
public ActionResult Test(string returnUrl)
{
if (!string.IsNullOrWhiteSpace(returnUrl))
{
//your codes...
}else
{ }
return View();
}
or for multiple form items (You can also access other form elements this way)
public ActionResult Test(FormCollection fc)
{
string _returnUrl = fc["returnUrl"];
if (!string.IsNullOrWhiteSpace(_returnUrl))
{
//your codes...
}else
{ }
return View();
}
I hope you are looking for below code sample where we just fetch the value in url which we says query string:
Request.QueryString["querystringparamname"].ToString();
You can assign this in any Var and use accordingly.
I am developping an ASP MVC 5 application and I am heading up a problem with the showing of a Toast's Notifaction.
The toast notifaction appears after updating the informations of a user in order to confirm the success of operation.
Unfortunately it disappears quickly (in 2 seconds) after the loading of the next page. I think this is due to the using of server side code (C# in this case), where the entire page reloads, including the javascript files. This is why the toastr disappears too.
Is there any way to keep it a lil longer ?
I tried to o pass the necessary information to the next page, so that page two would know to display the toastr instead of page1 but it didn't work.
This is the code's snippet :
View :
#using (Html.BeginForm("Edit", "Client", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
<input type="submit" value="Save" class="btn btn-default" onclick="Update()">
}
<script src="/template/web/js/jquery-2.2.3.min.js"></script>
<script src="~/Scripts/toastr.js"></script>
<script>
function Update() {
toastr.success("Your infos are updated with succes !");
}
</script>
Controller :
[HttpPost]
public ActionResult Edit(int id, Client cmodel)
{
try
{
ClientManagement cdb = new ClientManagement();
if (cdb.UpdateDetails(cmodel))
{
ViewBag.Message = "Client Details Edited Successfully";
}
return RedirectToAction("Profil");
}
catch
{
return View();
}
}
Profil :
public ActionResult Profil()
{
ClientManagement dbhandle = new ClientManagement();
ViewBag.Message = TempData["Message"];
return View(dbhandle.GetClientsInfo());
}
In View Profil (buttom of page) :
<link href="~/Content/toastr.min.css" rel="stylesheet" />
script src="~/scripts/toastr.js"></script>
<script src="~/scripts/toastr.min.js"></script>
#if (ViewBag.Message != null)
{
<script type="text/javascript">toastr.success("#ViewBag.Message");</script>
}
If you want to pass one-time message from an action method to another action method, you will need to use TempData.
[HttpPost]
public ActionResult Edit(int id, Client cmodel)
{
try
{
ClientManagement cdb = new ClientManagement();
if (cdb.UpdateDetails(cmodel))
{
TempData["Message"] = "Client Details Edited Successfully";
}
return RedirectToAction("Profil");
}
catch
{
return View(cmodel);
}
}
You then retrieve it at the next page either inside action method or view. Please note that it would be cleared out at the end of the request after you read it.
Profil.cshtml
#if (TempData["Message"] != null)
{
<script type="text/javascript">toastr.success("#TempData["Message"]");</script>
}
Update
Above code works. If you use default ASP.NET MVC Layout, please make sure you place the script inside Scripts section, so that it renders after jQuery.
#section Scripts {
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" />
#if (TempData["Message"] != null)
{
<script type="text/javascript">toastr.success("#TempData["Message"]");</script>
}
}
FYI: If you use toastr in other places, you might want to consider bundle and minfy them with other scripts and styles.
If you don't need to redirect to "Profil", then I agree you could use Ajax and on the success callback you could show your toastr. If you need to redirect then you can set the ViewBag.Message as you do and then do something like
#if (ViewBag.Message != null)
{
<script type="text/javascript">toastr.success("#ViewBag.Message");</script>
}
I am new to Ajax and Jquery so i need you to forgive me if i am trying to do something stupid,
i am working with MVC 5 and Ajax.Beginform,
and what i am trying to do is i have a ajax form i need to validate it with jquery unobtrusive,if i am getting it right the Jquery validation work with ModelState and will return the view back again if is a validation error found, in this case i need to update my form so the validation message appears to the user browser ,for example here is my controller :
[HttpPost]
public ActionResult Index(AddProduct model)
{
if (ModelState.IsValid)
{
// connect to the database save data etc...
return PartialView("~/Views/Shared/_MyModal.cshtml");
}
else
{
return View(model);
}
}
if the ModelState.IsValid i should save the data and return partial view (Bootstrap Modal) indicating that the data has been saved successfully,
else it will return the whole view to display the validation messages and to do that i have to put the TargetId of the ajax form to wrap up the whole ajax form to updated,
and here the ajax form :
<div id="result">
#using (Ajax.BeginForm("Index", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "result",
HttpMethod = "POST",
OnBegin = "onBegin();",
OnComplete = "onCompleated();",
OnSuccess = "onSuccess()",
OnFailure = "onFailure()"
}))
{
#Html.ValidationSummary(true)
<div id="form1" class="form-horizontal">
<div class="row">
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Name, String.Empty, new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Price, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Price, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Price, String.Empty, new { #class = "form-help form-help-msg text-red" })
</div>
</div>
<div class="form-group">
<button class="btn btn-default col-md-2 col-md-offset-2" type="submit">Save </button>
</div>
</div>
</div>
}
</div>
else i will return a partial view and display it as Bootstrap Modal and i need in this case not to update the ajax form targetId i need to keep it intact and just Displaying the Modal, but the problem is in both situation ModelState.IsValid or Is not Valid all response fires onSuccess Method in ajax form i didn't know these is normal or not, and here is the javascript onsuccess() Method :
function onSuccess() {
$('#myModal').modal('hide')
$("#resultModal").modal({
backdrop: 'static',
keyboard: false
});
$('#resultModal').on('hidden.bs.modal', function (e) {
window.location = "/product";
});
}
$(#'myModal').modal('hide') is a progress Modal i hide it after the posting has finish,and the next step is i show result Modal which i had return it if the Posting execute fine without validation error,
the Problem is :
i get to update the form using the UpdatetargetId in the ajax form just fine if validation Error happen to be Exist, but that's happen any way if i am returning Validation Error Or partial view that caring the Modal for me,in both situation the form disappear,and that's something i don't wont to do,
i need to keep the form in case the return Content was partial view and updated in case ModelStat validation Error, maybe i miss understand everything but if i am, i need some explanation to put me back on track .
I did an ugly code and messy and i didn't like in onSuccess() Mehtod as mentioned above, i used event fires when i close my result Modal redirect me again to Index so the form Display again but its not clean and i don't like it, i need something professional if exist.
Thank you in Advance,
I perfectly understand your problem and I am ready to show you the method that I am using to handle this case.
So because onSuccess method is called every time not depending if ModelState is valid or not , you will need to handle the response a little bit different.
So your[HttpPost] method should look like this:
[HttpPost]
public ActionResult Index(AddProduct model)
{
if (ModelState.IsValid)
{
return Json(new {isValid = true, data = this.RenderPartialViewToString("ViewWhenModelStasteIsValid",model,false)});
}
else
{
return Json(new { isValid = false, data = this.RenderPartialViewToString("ViewWhenModelStasteIsNotValid", model, false) });
}
}
public static string RenderPartialViewToString(this Controller controller, string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
{
viewName = controller.ControllerContext.RouteData.GetRequiredString("action");
}
controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
In the view you will need to take off UpdateTargetId from the form and handle it manually on success function.
So your onSuccess function should look like this
function onSuccess(result, ref) {
if (result.isValid) {
jQuery("#result).html(result.data);
} else {
jQuery("#form1").html(result.data);
}
}
Make sure to pass the response data to onSuccess function, so you will need to change on the form from OnSuccess = "onSuccess()" to OnSuccess = "onSuccess(data,this)".
So your form will look like this:
#using (Ajax.BeginForm("Index", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
OnBegin = "onBegin();",
OnComplete = "onCompleated();",
OnSuccess = "onSuccess(data,this)",
OnFailure = "onFailure()"
}))
Hope this is what you need.
I know that a very similar question was posted and ansered:
How to gracefully handle AJAX PartialView update when Javascript is disabled
But that solution isnt satisfying me.
Is it possible, to update a Element with the Ajax Helper Method when Java Script is disabled, that it will show a partial view in the same page and not in an extra tab?
I want that the Tag in the Index View is updated with a partial View (_Details), when I click on a AJAX ActionLink.
With the PView 1 Method, I get the same result with JS enabled and disabled. But i dont linke the PView 1 solution (as suggested in the similar question), because this makes the Partial View Class useless. Why would i need it when i reload the hole page anyway.
I would prefer a solution similar to PView 2. But there the Partial View is opened in a new Tab when JS is disabled.
My very simplified code so far:
HomeController Class
public class HomeController : Controller
{
public ActionResult Index()
{
var obj_str = new SimpleString { astr = "Nothing Yet" };
return View(obj_str);
}
public ActionResult PView1()
{
string str_posted = "String of PView 1";
var obj_str = new SimpleString {astr = str_posted};
if (Request.IsAjaxRequest())
{
return PartialView("_Details", obj_str);
}else
{
return View("Index", obj_str);
}
}
public PartialViewResult PView2()
{
var obj_str = new SimpleString {astr = "String of PView 2"};
return PartialView("_Details", obj_str);
}
}
Index.cshtml
<h2>AJAX Actionlink Index</h2>
#Ajax.ActionLink("Click me for PView 1", "PView1", new AjaxOptions
{
UpdateTargetId = "partv",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
})
#Ajax.ActionLink("Click me for PView 2", "PView2", "Home", new AjaxOptions
{
UpdateTargetId = "partv",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
})
<h3>Partial View here!</h3>
<div id="partv">
<p>#Model.astr</p>
</div>
_Details.cshtml (the Partial View)
#model MVCPartialViewTest.Models.SimpleString
<p>This is from the Partial View cshtml</p>
<p>#Model.astr</p>
SimpleString Class (the Model)
public class SimpleString
{
public string astr { get; set; }
}
I am designing a site and am trying to be compatible with javascript turned off or on.
I have a Controller Action named as follows...
public RedirectToRouteResult AddWorkAssignment(int id)
{
// delegate the work to add the work assignment...
return RedirectToAction("Index", "Assignment");
}
and my jQuery I do a post
$('#someButton').click(function() {
var id = $('#whereTheIDIs').val();
$.post('/Assignment/AddWorkAssignment/' + id);
return false;
});
but the RedirectToAction on the controller will do just that.... how do I stop the redirect to occur, or how do I structure the controller and page to handle this, because I want the redirect to occur if javascript is turned off.
Change your controller to something like this:
public ActionResult AddWorkAssignment(int id)
{
// do work to add the work assignment....
if (Request.IsAjaxRequest())
return Json(true);
return RedirectToAction("Index", "Assignment");
}
You could create your own filter attribute too... much like the AcceptVerbs attribute.
HTHs
Charles
EDIT: AjaxRequest ActionMethodSelectorAttribute attribute
Kickstart from here
public class AjaxRequest : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
return controllerContext.HttpContext.Request.IsAjaxRequest();
}
}
Then your controller:
public RedirectToRouteResult AddWorkAssignment(int id)
{
// do work to add the work assignment....
return RedirectToAction("Index", "Assignment");
}
[AjaxRequest]
public JsonResult AddWorkAssignment(int id)
{
// do work to add the work assignment....
return Json(true);
}