Updating View with Model changes via Ajax Post - MVC3 - asp.net

I am trying to use an Ajax (I think) call to update my model value and then have that new value reflected in the view. I am just using this for testing purposes for the moment.
Here's the overview:
MODEL
public class MyModel
{
public int Integer { get; set; }
public string Str { get; set; }
}
CONTROLLER
public ActionResult Index()
{
var m = new MyModel();
return View("Test1", m);
}
[HttpPost]
public ActionResult ChangeTheValue(MyModel model)
{
var m = new MyModel();
m.Str = model.Str;
m.Str = m.Str + " Changed! ";
m.Integer++;
return View("Test1", m);
}
VIEW
#model Test_Telerik_MVC.Models.MyModel
#using Test_Telerik_MVC.Models
#{
ViewBag.Title = "Test1";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>
Test1</h2>
#if (false)
{
<script src="~/Scripts/jquery-1.4.4.min.js" type="text/javascript"></script>
<script src="~/Scripts/jquery-ui.min.js" type="text/javascript"></script>
}
<h2>
ViewPage1
</h2>
<div>
<input type="button" onclick="changeButtonClicked()" id="changeButton" value="Click Me!" />
<input type="text" value="#Model.Str" class="txt" id="str" name="Str"/>
<div></div>
</div>
<script type="text/javascript">
function changeButtonClicked() {
var url = '#Url.Action("ChangeTheValue", "Test1")';
var data = '#Model';
$.post(url, data, function (view) {
$("#Str").value = '#Model.Str';
});
}
</script>
Basically the view renders a button with a textbox. My sole aim is to simply display the value of my model (Str property) in the textbox.
I have tried various combinations of the changeButtonClicked() function to no avail. Test1 is the name of my controller. What I don't understand is when I debug it, the controller action fires and sets my values correctly. If I place a breakpoint on the "#Model.Str" section of the input tag, it shows me that my #Model.Str is equal to Changed! which is correct. However, as soon as my success function fires in the javascript, the value reverts back to it's original value.
I can make it work by changing the input type to submit and wrapping it in a #Html.BeginForm() section but I am wondering if/how to do it like this? Or is a Submit the only way to accomplish it?
Thanks

First thing in the jQuery the proper way to set a value of an input is to use:
$("#Str").val(#Model.Str);
Next we'll look at the controller. In the POST action result you are returning the entire View in your AJAX call. That means all the HTML, script references, and JavaScript are being returned in your jQuery post request. Since all you are trying to update is the value of the input named Str, I would just return that value as JSON and nothing else.
[HttpPost]
public ActionResult ChangeTheValue(MyModel model)
{
var m = new MyModel();
m.Str = model.Str;
m.Str = m.Str + " Changed! ";
m.Integer++;
return Json(m.Str);
}
Next I would place your HTML inputs in a <form> so you can have jQuery serialize your model for you and then you can change your jQuery post code to be:
function changeButtonClicked() {
var url = '#Url.Action("ChangeTheValue", "Test1")';
$.post(url, $('form').serialize(), function (view) {
$("#Str").val(view);
});
}
All the serialization is doing is encoding the inputs in your form into a string and if everything is named properly ASP.NET will bind that back to your model.
If you need to have your route handle both AJAX calls and full requests you could use ASP.NET's IsAjaxRequest function to test the request and return different results depending on if the request is AJAX or not. You would do something like this in your controller:
[HttpPost]
public ActionResult ChangeTheValue(MyModel model)
{
var m = new MyModel();
m.Str = model.Str;
m.Str = m.Str + " Changed! ";
m.Integer++;
if (Request.IsAjaxRequest) {
return Json(m.Str);
}
else {
return View("Test1", m);
}
}
In the ActionResult above you are doing everything you did before, but now are testing the request type and if it's AJAX you return a JSON result of your string value. If the request was not from an AJAX call then the full View (HTML, scripts, etc) are returned to be displayed in the browser.
I hope this is helps you out and is what you were looking for.

You can update the view, just not the model. The model in a razor page is compiled on the server in order to render the view; you would need to recompile the razor page after every ajax request.
Only real option is to return json from server and manually update DOM/View.

Related

asp.net mvc add partial view dynamically using ajax

I am new to ASP.NET and I have a problem to add a content to my main view. In HtmlBeginform I upload the file on a button click and after file loading I need to display partial view under my main view I donĀ“t know how to call ajax script properly.
My main view:
#using prvniAplikace.Models;
#using Chart.Mvc.ComplexChart;
#using Chart.Mvc.Extensions;
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("LoadFile", "Home", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
<div class="form-group" align="left">
<label for="exampleInputFile">Load File</label>
<input type="file" accept=".tcx" class="form-control-file" name="exampleInputFile" id="exampleInputFile" aria-describedby="fileHelp">
</div>
<div class="form-group" align="left">
<button class="btn btn-primary" type="submit" id="add" name="add" value="Add">Display</button>
</div>
}
#section Scripts {
<script type="text/javascript">
$("#add").on('click', function () {
$.ajax({
async: false,
url: '/Home/getContent'
}).success(function (partialView) {
$('#getContent').append(partialView);
});
});
</script>
}
View I want to add to a main view:
#{
ViewBag.Title = "getContent";
Layout = null;
}
<h2>Obsah</h2>
<p>Odstavec</p>
<p>#DateTime.Now.ToString()</p>
Controller:
namespace prvniAplikace.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
public ActionResult getContent()
{
return PartialView("~/Views/Home/AjaxRequest.cshtml");
}
[HttpPost]
public ActionResult LoadFile(HttpPostedFileBase exampleInputFile)
{
if (exampleInputFile.ContentLength > 0)
{
var fileName = Path.GetFileName(exampleInputFile.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
exampleInputFile.SaveAs(path);
string xmlFile = Server.MapPath(fileName);
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlNodeList nodes = doc.GetElementsByTagName("HeartRateBpm");
XmlNodeList nodes2 = doc.GetElementsByTagName("Time");
}
return RedirectToAction("Index");
}
}
}
As per your comment, you would like to load the partial view content via ajax after the index view is (re)loaded after the normal form submit you do to upload the file. To achieve this, you should make the ajax call in the document ready event. Since it is the same page/view user will see before and after the form submit, you should conditionally make the ajax call based on whether the page is loaded for your first GET request or for the GET request issued by the Redirect call in your http post action.
Since Http is stateless, there is no way for the GET action method to know whether this was called from the Redirect method call after successfully processing the submitted form (in your http post action). You may use TempData to address this problem. Before redirecting to the Index view, set a flag to the temp data dictionary which will be available in the next GET request.
[HttpPost]
public ActionResult LoadFile(HttpPostedFileBase exampleInputFile)
{
// your existing code here
TempData["Success"] = true;
return RedirectToAction("Index");
}
Now in your GET action, read this and pass it to view via ViewBag (or a view model property if you have one)
public ActionResult Index()
{
ViewBag.IsSuccess = TempData["Success"];
return View();
}
Now in the view, check this ViewBag item and if it exist and has true value, render the div in which we want to show the partial view content. You can take advantage of the the Url.Action helper method to generate the correct relative path to the action method which returns partial view and keep that in html5 data attribute in the div tag so we can use that in javascript later to make the ajax call.
So add this to your index view.
#if (ViewBag.IsSuccess!=null && ViewBag.IsSuccess)
{
<div data-url="#Url.Action("getContent", "Home")" id="myPartial"> </div>
}
Now all you need is the javascript code which makes the ajax call. Execute that in the document ready event. You can use the jQuery load method.
$(function(){
// Get the url from the data attribute of the div
var url = $("#myPartial").data("url");
// Make the ajax call using load method
$("#myPartial").load(url);
});

MVC - Update model in view after ajax form submit

I have a question concerning MVC4. I have a form that I submit through ajax to my controller. In the controller, I create a new entry in a database, using the data of the form.
In case of success, the form fields need to go empty and a message should appear that the record has been added. So that the user is ready to add the next item through the form. I can clear the fields in the "ResetView" method through javascript (that's called through "OnSuccess"), that's not a problem
In case of error, the form fields need to remain filled in and a message should appear that it failed. In my case, it calls also the "OnFailure" method "ShowError" (which just shows the div "CustomerMessage").
The issue I am having when an item fails, is that the "CustomMessage" from my model is empty, while I did explicitly set it in my controller. So in my view, the #Model.CustomMessage is always empty!
I read that I should call the "ModelState.Clear()" function in my controller but that doesn't seem to do anything.
Can someone check what could be wrong in my code?
Any help is greatly appreciated.
View:
<div id="createform">
#using (Ajax.BeginForm("Create", "Payment", new { username = User.Identity.Name }, new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "createform",
//OnBegin = "SubmitForm",
OnSuccess = "ResetView",
OnFailure = "ShowError"
}))
{
<div id="CustomMessage">#Model.CustomMessage</div>
... bunch of #Html.TextBoxFor stuff (like below) ...
#Html.TextBoxFor(model => model.SenderName, new { id = "SenderName"})
}
</div>
Controller:
[HttpPost]
public ActionResult Create(PaymentViewModel payment, string username)
{
if (ModelState.IsValid)
{
try
{
paymentRepository.AddPayment(payment.PaymentLine, username);
paymentRepository.SaveChanges();
payment.CustomMessage = "success";
}
catch (Exception ex)
{
payment.CustomMessage = "error";
}
}
else
{
payment.CustomMessage = "error";
}
ModelState.Clear();
return PartialView(payment);
}
I found the problem. It seemed that I was missing a reference to the jquery unobtrusive ajax script. After adding that, everything worked correctly
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

How to save the current model when ajax is requesting?

I'm using Ajax in my application to update a view without refresh the webpage.
Can you notice here http://contoso2.azurewebsites.net/Test/DoTest because I'm highlighting the partial view in yellow color.
But the problem is, when I enter data to the items (math problem), when Ajax is requesting, I don't see the changes in the model.
public ActionResult DoTest()
{
List<Worksheet> worksheets = null;
if (Request.IsAjaxRequest())
{
worksheets = Session["Worksheets"] as List<Worksheet>;
return PartialView("_Problems", worksheets[1]);
}
worksheets = new List<Worksheet>()
{
new Worksheet("Addition and Subtraction of absolute values", new List<Problem1>() { ... }),
new Worksheet("Addition and Subtraction of absolute values", new List<Problem1>() { ... })
}
Session["Worksheets"] = worksheets;
return View(worksheets[0]);
}
I'm using a Session to recover my model, but if I debug in it I don't see any changes of the models. How can I do for when I press continue button, my model updates.
EDIT: This contain my AJAX in razor view
#using (Ajax.BeginForm(
new AjaxOptions
{
HttpMethod = "get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "problemList"
}))
{
<input type="submit" value="Continue" />
}
I usually have two Actions for a View . Though this is not a mandatory requirement.
The first action decorated with HttpGet renders the page on a Get when the user comes to the page for the first time. the second action is invoked when the user clicks next and posts a form.
public class TestController {
[HttpGet]
public void DoTest(){
var viewModel = new List<Worksheet>()
{
new Worksheet("Addition and Subtraction of absolute values", new List<Problem1>() { ... }),
new Worksheet("Addition and Subtraction of absolute values", new List<Problem1>() { ... })
};
return View(viewModel);
}
[HttpPost]
public void DoTest(List<Worksheet> worksheets){
//do whatever you want with the user response
var response = worksheets[1];
PartialView("_Problems",responseModel);
}
}
Note : you will have to invoke the ajax call with HttpMethod = "post". The ModelBinder in ASP.NET will bind the values posted in the request to your responseModel worksheets
You should try and go over the "getting-started-with-aspnet-mvc4" tutorials available on http://asp.net/

Cascading dropdown with Static data

i need to develop two dropdown list in my MVC3 project, where first dropdown will be a db driven and based on the data selected on first dropdown my second dropdown should change. Second dropdown is having hard coded text and value. how could i achived this. please help
Following is my codebased
viewmodel
public IEnumerable<SelectListItem> ProductTypeCode{ get; set; }
public int? ProductID { get; set; }
public IEnumerable<SelectListItem> ProductGroupCode;
public int? ProductGrpID { get; set; }
Controller
public ActionResult Index() {
var model = new MyViewModel
{
// TODO: Fetch those from your repository ,
model.ProductTypeCode= new SelectList(obj.ProductTypeCode as System.Collections.IEnumerable, "Value", "DispalyText");
return view(model);
}
VIEW
#Html.DropDownListFor(m => m.ProductID , Model.ProductTypeCode, "ALL" , new { #class = "DropDownList" })</td>
My question is based on the above dropdown of productType, i need to populate another dropdown called "ProductGroup" which is having hardcoded value. based on the ProductType dropdown the value of productgroup should change.
You have several options.
You can write your own javascript to handle the onchange event. There are literally thousands of simple tutorials on this exact topic. This one will probably address your question the best.
You can use extensions like Telerik's. They're relatively straight forward, well documented, but do have the extra weight of another library on them (although the markup they generate is pretty slim).
If you go with option 1 and write your own, the two main things you'll have to add in will be an extra controller action and some javascript.
The javascript will listen for change events on your product Id drop down. Put this in a document ready function on the page itself.
$("#ProductID").change(function() {
// Get the product id selected
var id = $(this).val();
// Fire off an ajax request to get the groups
$.ajax({
// whatever the url may be.
url: "#Url.Action("Groups")" + id, // Append the id to the url.
dataType: "json",
type: "POST",
error: function() {
alert("An error occurred.");
},
success: function(data) {
var items = "";
$.each(data, function(i, item) {
items += "<option value=\"" + item.Id+ "\">" + item.Name + "</option>";
});
// Set the secondary dropdown content to the newly created
// list of options. Use whatever Id your secondary is.
$("#ProductGroup").html(items);
});
The controller action responds to the ajax call with the list of appropriate product groups.
public class ProductsController : Controller
{
public ActionResult Groups(int id)
{
// You only need the id and a name field in the response, not the
// entire object.
var groups = _myService.FindGroupsForProductId(id)
.Select(g => new
{
Id = g.Id,
Name = g.Name
});
// Return a json result. You only need to re
return Json(groups);
}
}
The code above should help you get started. It's assuming several things that you didn't show in your code at all. Your question got downvoted because at first, you didn't post any code. Then, the code that you did post doesn't really show that you've put any effort into finding the solution on your own. Find some jquery tutorials on this, there are thousands, then if you have specific problems, bring them here.

Message Control in Masterpage with ASP.NET MVC

Hey everyone. Got a custom on how to do this as im new to MVC and trying to get a couple of small things implemented. This is the way I did it in WebForms but want to transition it to MVC.
I have a user control that contains CSS which will render a message. This control is located in the MasterPage and called from a ASPX page like this:
Pseudo code:
try{
Msg.MessageMode = WebPageMessageMode.OK;
Msg.ShowOK("Report deleted.");
}
catch
{
Msg.MessageMode = WebPageMessageMode.ErrorMessage;
Msg.ShowError("There was a problem deleting the report.");
}
Masterpage.aspx
<cc1:WebPageMessage runat="server" ID="msg" />
I currently have the control in the MasterPage and now im a bit confused about proceeding from here.
Should I put the 'Msg' object above from the pseudo code to a View from the MasterPage?
What is the proper way to do something like?
I dont think there is a one-solution-fits-all here.
Anyway this is my solution that uses jQuery:
1) Create a MyResultModel class to handle a message to the user
public enum MyResultType { Info, Error }
public class MyResultModel
{
public MyResultModel( MyResultType type, string message ) {
switch ( type ) {
case MyResultType.Info: Title = "OK"; break;
case MyResultType.Error: Title = "Error!!!"; break;
}
Message = message;
}
public String Title { get; set; }
public String Message { get; set; }
}
2) Create a Partial View named MyResult in the Shared Folder to handle the model
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyResultModel>" %>
<div id="resultTitle"><%: Model.Title %></div>
<div id="resultMessage"><%: Model.Message %></div>
3) Create and use a BaseController for your controllers and add the following method to it. The method simply add a custom Http Header to the response
protected PartialViewResult PartialView( string viewName, object model, string resultHeader ) {
Response.AppendHeader( "MyHttpCustomHeader", resultHeader );
return base.PartialView( viewName, model );
}
4) In your action return a MyResultView when you've done
[HttpPost]
public virtual ActionResult DoSomething() {
try {
//Do Something
return PartialView( "MyResult",
new MyResultModel( MyResultType.Info, "Operation Completed" ),
"HttpResultInfo" );
}
catch ( Exception ex ) {
return PartialView( "MyResult",
new MyResultModel( MyResultType.Error, ex.Message ),
"HttpResultError" );
}
}
5) Finally, Submit the form using jquery and handle the results.
$.ajax({
type: "post",
dataType: "html",
url: "your/url/here",
data: $("#myform").serialize(),
success: function (response, status, xml) {
var resultType = xml.getResponseHeader("MyHttpCustomHeader");
if (resultType == null) {
//No message do whatever you need
}
else {
//response contain your HTML partial view here. Choose your
//desidered way to display it
}
}
});
With a scenario like this you dont need to place a control on the master page. You can:
Show the view as it comes from the action without any modification
Use some fancy message display technique as StackOverflow does with the orange sliding message (in this case simply extract the title and the message from the returned html)
Use some fancy jquery plugin as jGrowl to show your message
If you want to check wether it is an Info/Error message simply check the custom header with jQuery in the else branch
var title = $(response).filter("#resultTitle").text();
var message = $(response).filter("#resultMessage").text();
if (resultType == "HttpResultInfo") {
showInfoMessage(title, message);
}
else if (resultType == "HttpResultError") {
showErrorMessage(title, message);
}
Hope it helps!
In a controller, as part of the action, you can set a message like this:
public ActionResult MyAction()
{
// Do some stuff
TempData["message"] = "This is a message.";
return View("MyView");
}
In your master page or in your view:
<%
string text = TempData["Message"] as string;
Response.Write(text);
%>

Resources