MVC - Data back from controller to view - asp.net-mvc

My question : If we already have made view -a strongly typed, then why do we need to return model object back from controller post method to view - MVC 4 asp.net ?
For example : I have calculator view :
#using (Html.BeginForm())
{
<p>Number One : #Html.TextBoxFor(m => m.numberOne)</p>
<p>Number Two : #Html.TextBoxFor(m => m.numberTwo)</p>
<input type="submit" value ="Addition" name="calculateOperation" />
<input type="submit" value ="Subtraction" name="calculateOperation" />
<input type="submit" value ="Multiplication" name="calculateOperation" />
<input type="submit" value ="Division" name="calculateOperation" />
}
#using (Html.BeginForm())
{
<p>Output : #Html.TextBoxFor(m => m.result)</p>
}
and controller :
public ActionResult Calculate(Calculator model, string calculateOperation)
{
if (calculateOperation.Equals("Addition"))
{
int[] array = { 1, 12, 5, 26, 7, 14, 3, 7, 2 };
model.result = model.numberOne + model.numberTwo;
}
if (calculateOperation.Equals("Subtraction"))
{
model.result = model.numberOne - model.numberTwo;
}
if (calculateOperation.Equals("Multiplication"))
{
model.result = model.numberOne * model.numberTwo;
}
if (calculateOperation.Equals("Division"))
{
model.result = model.numberOne / model.numberTwo;
}
return View(model);
}
If I don't return the model object, I don't get value of model.result.
Looking for a valid reason.

HTTP is a stateless protocol. Hence, when you do work on the server, if you want it to display something on the client, you have to send it back down. An MVC strongly-typed view is really just an abstraction on top of the rendering engine.
When you "Submit" the form you are just doing an HTTP POST back to your controller action (an http request).
Calling
return View(model)
means that you are sending an HTTP response that returns a rendered html page (using your view). In your case you're simply passing in the model as a parameter.

Well, you don't have to send back a model, you could just use the FormCollection parameter, but then you would have to fetch the values and cast them to the correct type your self.
public ActionResult Calculate(FormCollection form, string calculateOperation)
{
// Need to check if form["numberOne"] is null or empty or do a int.TryParse()
int numberOne = int.Parse(form["numberOne"]);
}
With a strongly typed model you get that for free by the model binders in asp.net mvc. The code looks much cleaner and it's easier to use.
With a model you also get the power of attributes, like validation and scaffolding. It's much cleaner and easier to validate most scenarios using a model with validation attributes.
In this case you need to send a model to the view simply because the view requires it. That's how it is designed. How would the model or the view know that you have made an calculation if you don't store it somewhere? Of course you could also use ViewBag:
ViewBag["result"] = model.numberOne + model.numberTwo;
And in your view:
<p>Output :#Html.TextBox("result", (string)ViewBag["result"])</p>

I always figured that this was to cover cases when there was some kind of explanation or response type data.
For example. You submit an address to be added to the database and you have a service which checks the address for correctness. If it is correct, it gets persisted, otherwise it gets corrected, added to a special field in the original object and sent back for confirmation.

There's no actual requirement for your controllers method to return anything that consumes that or any other model. As a result you still need to be explicit with what View and the data associated with it that you want to return.
They could add some sort of overload to View that would implicitly assume it should use some ViewModel in the method parameters, but that's non-intuitive and unnecessary.

Related

Two ways of passing model back from view to controller

I'm new in MVC and have a question of principle about the way how the model is passed back from the view to the controller.
In the usual way the model-object comes from the controller, "spreads" its data into fields of the view and is then gone. The data will then be re-collected by a FormCollection-object passed back to the controller.
But there is also the other way where the model-object itself can be passed back to the controller as routedObject of e.g. an ActionLink, URL-Action or whatever. Then it's not necessary to spread and re-collect all the data.
In my work-place the other way is blocked, I get a warning about forbidden chars in the link-string. When investigating the issue I found that the other way seems mostly unknown.
For many reasons I think it's much better to pass the model-object back instead of the elaborate re-collecting of data.
So what is the reason for this curiosity please?
Update: Added View-Example
#model MvcApplication2.Models.TestClass
#{
#(Model.TestValue = 111);
}
<a href="#Url.Action("ValueBack", Model)">
<span>Test</span>
</a>
public void ValueBack(MvcApplication2.Models.TestClass testClass)
{
int x = testClass.TestValue;
}
In MVC we can get the values of the form in the controller by 3 ways.
Using the FormCollection Object.
Using the Parameters.
Strongly type model binding to view.
And I think it is not possible to send the model to the controller through the actionlink (as an ActionLink helper generates an anchor tag which when clicked sends a GET request to the server)
The Another Way :
Send the id of current model so that the controller action can fetch it back from the datastore from which it initially fetched it when rendering the View.
View:
#Ajax.ActionLink(
"Next",
"Step",
new {
StepId = 2,
id = Model.Id
},
new AjaxOptions { UpdateTargetId = "stepContainer" },
new { #class = "button" }
)
Controller:
public ActionResult Step(int StepId, int id)
{
var model = Repository.GetModel(id);
//Code
}

missunderstanding mvc default binding

I have multiselect jquery plagin (Choosen) and when I use it in 'Multiple Select' mode I expect in controller next values:
posted string = 'value1,value2...'
really have
posted string = 'value2'
only if I reffer directly to FormCollection I'll get expected values as below:
[HttpPost]
public ActionResult TagSearech(/*string tagSelect*/FormCollection c)
{
// only one value here
// string[] names = tagSelect.Split(',');
// as expected: value1,....
string expectedValue = c['tagSelect'];
return View();
}
I cant understand what might cause this behavior.
EDIT
Here is View:
#using (Html.BeginForm("TagSearech", "Tag"))
{
#Html.DropDownList("tagSelect", Model, new { #class = "chzn-select", data_placeholder = "tag names", multiple = "" })
<input type="submit"/>
}
MVC will attempt to bind the input data on the URL into the model. I haven't seen how Chosen.js posts the data back to the server, but essentially its coming in in the wrong format, so MVC binds the first element it sees to the string Model.
The FormsCollection retrieves all of the data that was posted in the URL, which is why all of your selected values can be seen there.
Did you try changing the incoming model from string to string[], and see if all of the items are bound to the array?

MVC update Dropdownlistfor with jquery

I have a Dropdownlistfor connected to my model. When the page is loaded the model is empty, but I have a button on the page that updates the model. My problem is that my dropdownlist doesn't update.
Markup:
#Html.DropDownList("ddlRaces", new SelectList(ViewBag.Races, "RaceId", "Name"))
<input type="button" id="btnChangeRace" class="btnGo" value=" " />
#Html.DropDownListFor(m => m.Timers, new SelectList(Model.Timers, "TimerId", "StartTime"), "Velg timer:")
Script:
btnChangeRace.click(function () {
url = "/TimeMerger/GetTimers/?raceId=" + ddlRaces.val();
$.get(url);
});
Codebehind:
[HttpGet]
public ActionResult GetTimers(int raceId)
{
var timeMergeModel = new TimeMergerModel();
timeMergeModel.Timers = TimerModel.GetTimers(raceId);
return View(timeMergeModel);
}
$.get(url);
Here you are sending an AJAX request but you are doing nothing in the success callback so nothing will update. There is not even a success callback. So you need to define a success callback:
$.get(url, function(result) {
// result here will represent the value returned by the server
// which in your case is HTML. So here you might need to update
// the relevant portion of your page.
});
Also your DropDownListFor definition is wrong. You are using the same model variable as first and second argument (Model.Timers). The first first argument of this helper should be a scalar property to which you are binding and not a list. It should be:
#Html.DropDownListFor(
m => m.SelectedTimerValue,
new SelectList(Model.Timers, "TimerId", "StartTime"),
"Velg timer:"
)
where SelectedTimerValue would be a property on your view model that will hold the selected value. Also why you are using DropDownList instead of DropDownListFor for the first drop down? The whole idea of a view model is that it will contain all the necessary information so that the view would simply display it.

Model state inferno - how can Model.A be two different things at the same time?

I have this
<%=Model.StartDate%>
<%=Html.Hidden("StartDate", Model.StartDate)%>
it outputs:
2010-05-11 11:00:00 +01:00
<input type="hidden" value="2010-03-17 11:00:00 +01:00" name="StartDate" id="StartDate">
What the...
It's a paging mechanism so the hidden value was valid on the first page and I've been able to move forward to the next page. But since the values won't update properly it ends there.
What do I need to do?
Using firefox.
Update - more code
using (Html.BeginForm("Program", "Activities", null, FormMethod.Get, new { #name = "ProgramForm", id = "ProgramForm" }))
{
.
viewModel.StartDate = pagingService.StartDate;
return View(viewModel);
Update - complete action
[Authorize]
public ActionResult Program(string[] submit)
{
var viewModel = new ActivityProgramViewModel { UserID = LoggedInUser.UserID };
viewModel.Fresh = true;
TryUpdateModel(viewModel);
var pagingService = new OccurencePagingService(LoggedInUser.AllActivities.Where(a => a.StartTime != null));
if (!viewModel.Fresh)
{
pagingService.StartDate = ((DateTimeOffset)viewModel.StartDate);
pagingService.EndDate = ((DateTimeOffset)viewModel.EndDate);
}
if (submit != null)
if (submit.Contains("MoveBack"))
pagingService.MoveBack();
else if (submit.Contains("MoveForward"))
pagingService.MoveForward();
ViewData.Model = viewModel;
viewModel.Occurrences = pagingService.GetOccurences();
viewModel.Fresh = false;
viewModel.HasLess = pagingService.HasLess;
viewModel.HasMore = pagingService.HasMore;
viewModel.StartDate = pagingService.StartDate;
viewModel.EndDate = pagingService.EndDate;
return View();
}
First one uses Model object, second one uses existing ModelState. Look at ModelState values before generating view. It propably holds value for this field. Html helpers priveded by MVC use ModelState to generate form fields. It helps in recreating values after post back.
To get rid of this kind of problems, use POST-REDIRECT-GET pattern or just pass query parameters through GET.
I think the <%=Html.Hidden("StartDate", Model.StartDate)%> is out of place here.
Html Helpers try to keep data in the UI like they where entered by examining the post/route data. Please dont ask me how someone would ever enter data in a hidden field.
You want something different: You want to set the data to Model.StartDate and dont care what is in the post/route.
I would use <input value="<%=Model.StartDate%>" name="StartDate" /> .

ASP.NET MVC - Html.TextBox - Value not set via ViewData dictionary

I have a search box on a page (actually in a partial view though not sure that is relevant) with an Html.TextBox control.
<%= Html.TextBox("query", ViewData["query"], new { style = "width: 90%;" })%>
The action method takes "query" as a parameter, and I edit this value to clean up the string that is passed in:
public ActionResult SearchQuery(string query) {
ViewData["query"] = StringFunctions.ProperCasing(query.Replace("_", " "));
However, when it gets to the Html.TextBox the original query value (in this case with underscores) is preserved. I can see that the edited value is in the ViewData field, so for example, if:
query == "data_entry"
then, after being passed into the action method
ViewData["query"] == "data entry"
but the value, when it reaches the view, in the Html.TextBox is still "data_entry". It seems like there is a collision between the action method parameter "query" and the search box form parameter "query". Anyone know what is going on here or if there is another way to pass the value?
This action method is separate from the action that results from posting the search box data.
Html.Textbox helper looks for ModelState first (ASP.NET MVC source, InputExtensions.cs line 183, HtmlHelper.cs line 243). The simplest solution would be removing the ModelState for "query":
public ActionResult SearchQuery(string query)
{
ViewData["query"] = StringFunctions.ProperCasing(query.Replace("_", " "));
ModelState.Remove("query");
return View();
}
Dont know if this is the problem but my first thought is is to pass the view data back in the controller.
public ActionResult SearchQuery(string query)
{
ViewData["query"] = StringFunctions.ProperCasing(query.Replace("_", " "));
return view(ViewData):
}

Resources