Model attributes not passed to next view - c#

I am trying to reuse a model in different views in asp.net mvc. But if the model is passed to the next view and new attributes are submited the old values are lost.
Here the code example I'am using:
Model
public class ExampleModel
{
public string Attribut1 { get; set; }
public string Attribut2 { get; set; }
}
Controller
public class HomeController : Controller
{
public IActionResult Page1()
{
return View(new ExampleModel());
}
public IActionResult Page1Check(ExampleModel model)
{
return View("Page2", model);
}
public IActionResult Page2Check(ExampleModel model)
{
return View("Page2", model);
}
}
View1
#model Example.Models.ExampleModel
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<form asp-controller="Home" asp-action="Page1Check">
<label asp-for="Attribut1"></label>
<input asp-for="Attribut1" placeholder="Attribut1" />
<button type="submit">Submit</button>
</form>
View2
#model Example.Models.ExampleModel
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<form asp-controller="Home" asp-action="Page2Check">
<label asp-for="Attribut2"></label>
<input asp-for="Attribut2" placeholder="Attribut2" />
<button type="submit">Submit</button>
</form>
If I open Page1 a new model is created. If the attribut1 is entered in Page1 and submited Page1Check is called and the attribut1 is filled. Now the model is passed to page2. Here the attribut2 is filled and submited. But if I check the model inside of Page2Check the attribut1 is null and only attribut2 is filled.
Strangely if I check the attribut1 in the page2 view its still there. Only after the submit the attribut1 is gone.
Do I miss something, how do I prevent that the filled attributs are empty after the submit?

In order for a piece of data to be sent from a user's browser to the server when the corresponding form is submitted, it must be contained within an HTML element that will be sent to the server (e.g. an <input> or a <select>).
In your first view, you have an input for Attribut1 but not for Attribut2, so Attribut2 won't be part of the data sent to the server when the form in view 1 is submitted. The same is true for Attribut1 in view 2.
To solve this, you can add hidden fields to each view, contained by the <form> tag, to store the data:
For view 1, add <input type="hidden" asp-for="Attribut2" />.
For view 2, add <input type="hidden" asp-for="Attribut1" />.
As an example, view 1's form would now look like this:
<form asp-controller="Home" asp-action="Page1Check">
<label asp-for="Attribut1"></label>
<input asp-for="Attribut1" placeholder="Attribut1" />
<input type="hidden" asp-for="Attribut2" />
<button type="submit">Submit</button>
</form>

Related

Is it possible to populate a datamodel using a form and send it to the controller from Razorpage?

What is the best way to fill the field of a datamodel instance using a form on a Razorpage?
Is it possible to instantiate and submit a model directly from the Razorpage, or do i have to read each individual field of the form and assign them to a model instance in the controller?
Currently i am doing something like this: (not my actual code)
In the view:
<form method="post" action="some_action">
<input type="text" name="name" />
<input type="submit" value="submit" />
</form>
In the controller:
[HttpPost("some_action")]
Public IActionResult create_using_form()
{
person p = new person();
p.name = Request.Form["name"];
/*do whatever with person, like save to a db*/
return view();
}
I imagine there is a better solution, one where i dont have to populate every single field on my person object, one after the other. Any advice?
You don't need to deal with each field in the form separately. Just use strongly typed view by defining the data model by using #model.
Let say you have data model class DataModel.
Then short way:
public IActionResult Index()
{
return View(new DataModel());
}
[HttpPost("some_action")]
public IActionResult create_using_form(DataModel model)
{
if (ModelState.IsValid)
{
//...
}
return View("Index", model);
}
The Html.EditorForModel() helper was developed in ASP.NET Framework MVC, but it still has implementation in the ASP.NET Core MVC. The Html.EditorForModel() returns an HTML input element for each property in the model.
#model DataModel
<form method="post" action="some_action">
#Html.EditorForModel()
<input type="submit" value="OK" />
</form>

How can I post a form content that is not related to a model?

I have an assignment where I need to capture three values in a form, submit them, do a specific operation with the values based on some conditions that will lead me to an index of a database row, fetch a row from the database with the calculated index and then update the view.
The problem is that the form per se is not a model nor does create an instance/entity/row in a table. When I create the view I get an error from Razor that says that the expression tree may not contain a dynamic operation, I know that it's because the form is not related to any model (and thus I'm not adding the #model directive in the .cshtml file). I think I need to POST the content to a Controller and with the fetched value update the content (I did try using a submit button, but it redirected me to an empty page, I know that's how forms usually function, but that's not the behavior I need).
I tried adding the #model directive to the Index.cshtml file that contains the form, but since the model I'm attaching the Index does not contain the same attributes as the ones I'm using in the form, Razor throws errors. I tried creating a Model class for the form and it somehow works, I get the values and all (it redirects on submit, thats not what I want) but I don't believe it's the correct thing to do since the form itself is not for creating an entity in the Table.
This is the Model from which I will be getting the values from the database:
using System;
using System.Collections.Generic;
namespace PrototipoExploratorio.Models
{
public partial class Factores
{
public int FactorId { get; }
public int ValorUno { get; }
public int ValorDos { get; }
public int ValorTres { get; }
}
}
This is the form, currently its inside the folder Views/Factores (as the model above)
#{
ViewData["Title"] = "Index";
}
<h2>Factores</h2>
<form method="post" asp-controller="FactoresForm" asp-action="ObtenerFactor">
<div class="form-group">
<label for="Estatura" class="control-label">Estatura en centímetros</label>
<input asp-for="Estatura" name="estatura" type="number" id="estatura" placeholder="168, 190, 155...">
</div>
<div class="form-group">
<label for="Edad" class="control-label">Edad</label>
<input asp-for="Edad" name="edad" type="text" id="number" placeholder="15, 24, 50...">
</div>
<div class="form-group">
<label for="Peso" class="control-label">Peso en kilogramos</label>
<input asp-for="Peso" name="peso" type="number" id="peso" placeholder="80, 70.5, 55.40...">
</div>
<div class="form-group">
<label for="EstadoCivil" class="control-label">Estado civil</label>
<select id="EstadoCivil" asp-for="EstadoCivil" for="EstadoCivil" name="EstadoCivil">
<option value="soltero">Soltero</option>
<option value="casado">Casado</option>
</select>
</div>
<div class="form-group">
<div>
<label class="control-label">Factor calculado: </label>
<h6 id="FactorCalculado">{this is where I will update the value calculated from the values submitted here and the database row that I will fetch}</h6>
</div>
<div class="form-group">
<button asp-controller="FactoresForm" asp-action="ObtenerFactor" class="btn btn-primary" type="submit">Obtener factor</button>
<button asp-controller="FactoresForm" asp-action="LimpiarDatos" class="btn btn-primary" type="submit">Limpiar</button>
</div>
</div>
</form>
And this is the controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using PrototipoExploratorio.Models;
namespace PrototipoExploratorio.Controllers {
public class FactoresForm : Controller {
// GET: /<controller>/
public IActionResult Index() {
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
// I expect to reach this code on form submit
// I know that right now this does not work given the explanation above
public IActionResult ObtenerFactor() {
Console.WriteLine("");
return View("Index");
}
}
}
I expect to submit the form, do some business logic operations to get the index of the database row (which is mapped to the Factores model), get the value from the row and update the view.
The problem is that the form per se is not a model nor does create an instance/entity/row in a table
You misunderstood the role of View Model. A View Model is a POCO (Plain Old CLR Object) which is used to render the view. It can be any plain simple object. An entity is usually known as Data Model that represents a row in datatable.
I tried creating a Model class for the form and it somehow works, I get the values and all (it redirects on submit, thats not what I want) but I don't believe it's the correct thing to do since the form itself is not for creating an entity in the Table.
In fact, that's the correct way. Just remember:
the View Model is used to render the view. It has nothing to do with the Data Model.
The DTO is used to transfer the data. DTO doesn't have to share the same type of Data Model.
You can define your custom DTO/View Model as you need.
How to fix
In this scenario, you'll render the view and then post the data to controller. To avoid the confusing "model" naming, let's create a DTO to hold the data.
public class FactorDto
{
public int Estatura {get;set;}
public int Edad {get;set;}
public decimal Peso {get;set;}
public string EstadoCivil {get;set;}
}
The name FactorDto doesn't matter at all. Feel free to change the name as you like.
To render the form view with this DTO, you can add a #model directive in your form view file:
#model FactorDto
#{
ViewData["Title"] = "Index";
}
<h2>Factores</h2>
...
Finally, change your controller as below to receive the data sent from browser:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ObtenerFactor(FactorDto dto)
{
// ... now you've got the data
}
Now it should works fine.
In case you have a complex view model MyComplexViewModel, you can combine them in following way:
```csharp
public class MyComplexViewModel
{
public FactorDto FactorDto{get;set;}
public Abc Abc {get;set;}
public string A {get;set;}
// ... feel free to add other properties as you like
}
```
And declare the view model as MyComplexViewModel:
#model MyComplexViewModel
...
<th2>Factores&l;/h2>
...
<input asp-for="FactorDto.Estatura" name="estatura" type="number" id="estatura" placeholder="168, 190, 155...">
...

how to take input from TextBox and show in view in MVC

I want to show one TextBox. In that if give any input string and button clicked it should so like this
hai , what is ur name
[TextBox]
welcome,ur name is "xyz"
I am new in MVC. Please help me to do this.
View
#{
ViewBag.Title = "MyPage";
}
<h2>Mymethod</h2>
<h3>#ViewBag.Message</h3>
#Html.TextBox("Name")
<form method="post">
<input type="submit" value="Submit" name="btn" />
</form>
HomeController.cs
public ActionResult Mymethod()
{
ViewBag.Message = "Hello what is ur name ??? ";
return View();
}
There are many ways to do this to accomplish what you want. I will provide you with a simplistic approach, please modify and change it to fit in with your scenario.
I would normally recommend using a view model above any other way, for example using a single string value or using FormCollection or ViewBag. They can work but I prefer to use view models.
I answered a question on what view models are and what they are supposed to do, please read it:
What is ViewModel in MVC?
First you will create a view model that will handle your input data, like first name, last name, age, etc. You will then pass this view model through to the view. In your example I will only include name:
public class ViewModel
{
public string Name { get; set; }
}
In your Create action method you will instantiate this view model and pass it to the view. And when you click on the button to submit the form then the post action method will receive this view model as input parameter:
public ActionResult Create()
{
ViewModel model = new ViewModel();
return View(model);
}
[HttpPost]
public ActionResult Create(ViewModel model)
{
if (!ModelState.IsValid)
{
// If validation fails send the view model back to the view and fix any errors
return View(model);
}
// Do what you need to do here if there are no validation errors
// In your example we are posting back to the current view to
// display the welcome message
return View(model);
}
And then finally you view will look like this:
#model Project.Models.ViewModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.Name)
<button type="submit">Submit</button>
if (!string.IsNullOrWhiteSpace(Model.Name))
{
<p>welcome, your name is #Model.Name</p>
}
}
Please spend some time reading through the many online tutorials on ASP.NET MVC.
Modify your current view to
#using(Html.BeginForm("ControllerName","Mymethod",FormMethod.Post))
{
<input type="submit" value="Submit" name="btn" />
}
Add another action method in your controller like this :
[HttpPost]
public ActionResult Mymethod(FormCollection form)
{
string Name = form["Name"];
Viewbag.Name = Name;
return View()
}
Then add view to this controller and write this into it.
Hi , Your Name is #Viewbag.Name
You should wrap your form in form tag. It is a form after all. So when you click submit, you are submitting the form.
<form method="post">
<h2>Mymethod</h2>
<h3>#ViewBag.Message</h3>
#Html.TextBox("Name")
#if (!String.IsNullOrEmpty(ViewBag.Name))
{
<h3>
welcome,ur name is #ViewBag.Name
</h3>
}
<input type="submit" value="Submit" name="btn" />
</form>
On the controller, you need to add HttpPost handler for your Mymethod action. This is where your web server is accepting the form you've submitted.
[HttpPost]
public ActionResult Mymethod(string name)
{
ViewBag.Message = "Hello what is ur name ???";
ViewBag.Name = name;
return View();
}

Handling checkbox values being modified from 'true'

During a scan of an application at work, it became clear that modifying an input's value, then posting it potentially causes rendering issues if you try to pass that model back to the view.
For example, with the model:
public class Survey
{
public bool LikesCandy { get; set; }
}
The actions:
public ActionResult Survey()
{
return View(new Survey());
}
[HttpPost]
public ActionResult Survey(Survey model)
{
//do something worthwhile with the model, like validation, etc. etc.
//even setting the model's boolean doesn't help the rendering.
model.LikesCandy = true;
//then return to the page
return View(model);
}
And a basic razor view:
#model BoolTest.Models.Survey
#using (Html.BeginForm())
{
#Html.EditorFor(i => i.LikesCandy)
<input type="submit" value="Submit"/>
}
The form gets rendered something like:
<form action="/Home/Survey" method="post">
<input checked="checked" id="LikesCandy" name="LikesCandy" type="checkbox" value="true">
<input name="LikesCandy" type="hidden" value="false">
<input type="submit" value="Submit">
</form>
Posting that works fine.
However, if someone changes the input value like so:
<form action="/Home/Survey" method="post">
<input checked="checked" id="LikesCandy" name="LikesCandy" type="checkbox" value="foobar">
<input name="LikesCandy" type="hidden" value="false">
<input type="submit" value="Submit">
</form>
The model looks reasonable on the server-side (it defaults LikesCandy to false), but the view rendering will always fail with String was not recognized as a valid Boolean..
Can someone explain why this is so? And is there some idiomatic way to fix/handle this? I can do something like use reflection to compare the model's property's types to the request form and give the user some "stop that!" message, or write my own html tags and manually bind the model to those, but neither seem very clean/extensible/correct.
Edit: As stated in the comments, the model binder is expecting a true or a false. If you submit something else for that field then a 500 error is the expected response. Also, if you change the value of the model during the post processing and then redisplay the view then you're not going to see the model change made in the controller reflected in the POST response. See here:
http://patrickdesjardins.com/blog/modelstate-clear-is-required-to-display-back-your-model-object
public ActionResult Survey()
{
return View(new Survey());
}
[HttpPost]
public ActionResult Survey(Survey model)
{
//do something worthwhile with the model, like validation, etc. etc.
//even setting the model's boolean doesn't help the rendering.
model.LikesCandy = true;
//clear model state so that the change on the line above is applied
ModelState.Clear();
//then return to the page
return View(model);
}

How to pass a value from TextBox as parameter to Action

This is what I've had in mind but of course it doesn't work.
#{
var textBoxData = form.find('input[name="textboxList"]').val();
}
<input type="button" value="Add" title="Add" onclick="location.href='#Url.Action("Create_Add", "Controller", new { textboxList = textBoxData })'" />
How should I pass this? Controller action name and parameter are correct. Just that I don't know how to get the value entered in textbox...
I have trouble with saving a form within a form, so someone suggested this solution. Proxy code would be:
<firstForm>
textboxfor Name
dropdownfor DType
If DTypeDDL value is "List" then
<secondForm>
textboxfor nameOfItem
submitSecondForm (using that method i mentioned above)
</secondForm>
End If
submitFirstForm
</firstForm>
I've been trying to save 2 forms for quite a while now but no luck. This is basically my last resort.
First of all, you should go with a viewmodel oriented html file since you are using MVC (Model, View, Controller):
Create a viewModel:
public class ExampleViewModel
{
public ExampleViewModel()
{
}
public virtual string TextBoxData { get; set; }
}
After, code your html using the viewmodel as model:
#model Models.Views.ExampleViewModel
#using (Html.BeginForm())
{
<div class="editor-row">
<div class="editor-label">
#Html.LabelFor(model => model.TextBoxData)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TextBoxData)
</div>
</div>
<input type="submit" value="submit" />
}
and your controller:
public ActionResult Example()
{
ExampleViewModel model = new ExampleViewModel();
return This.View(model);
}
[HttpPost]
public ActionResult Example(ExampleViewModel model)
{
string infoEntered = model.TextBoxData;
// Do something with infoEntered
}
Hope this will help you!
If you're using view models, check out this answer: MVC sending data from View to Controller
If you're only interested in sending the data from an input to the action method without view models, you can do that as well:
View:
#using (Html.BeginForm("Edit", "Some", FormMethod.Post))
{
<input type="text" id="myTextBox" name="myTextBox" />
<input type="submit" value="Submit" />
}
Notice the BeginForm line. The first parameter is the Action I want the data to go to, which I named Edit. The next parameter is the Controller I am using, which I named SomeController. You don't add the Controller bit to the name when you're referencing the Controller in BeginForm. The third parameter is telling the form to use the POST method when sending the data to the server.
Controller:
public class SomeController
{
[HttpPost]
public ActionResult Edit(string myTextBox)
{
// Do what you want with your data here.
}
}
If you added more inputs (again, without a view model here), you can add them as parameters to the Edit method. This isn't really the preferred method, though. Look into using a view model. ScottGu has a nice blog post on doing what you need, using view models:
http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx

Categories