Simple question, but I cannot find solution anywhere.
I have 1 Get ActionResult, 1 Post ActionResult and 1 View.
In the Get method I initialize some part from the model.
After that in the View I initialize the other part.
When the model comes in Post method, it is not initialized well.
How to pass the data throught View and Methods?
I don't want to use Temporary objects.
Example:
[HttpGet]
public ActionResult Register(Guid product_id)
{
RegistrationModel model = new RegistrationModel();
model.Product = **someProduct**;
return View(model);
}
[HttpPost]
public string Register(RegistrationModel model, Product Product)
{
Here the objects comes initialized only with the parts from the view...
}
You need to understand how the MVC works.
There is a concept called Model Binding which is responsible for Mapping a Model with suitable HTML.
When I say suitable I mean that it needs to be formatted according to specific rules.
In Order to simplify the things, MVC has inbuilt HtmlHelpers which handles the transition between a Model's property to an HTML Element.
From your [HttpGet] method you may return:
1) a model-less view return View();
2) a view with a Blank model return View(new Product());
3) a view with a model which contains some data return View(product);
Inside the View, you should decide:
1) If you only want to display the model you could use (may not be wrapped in a form):
HtmlHelpers like #Html.DisplayFor(x => x.Name)
Simple Model calls like <h1>#Model.Name</h1>
2) If you want some data to be posted back to your [HttpPost] you should
Take care to "Map" a Model's property to an HTML element specifically (everything wrapped inside a form ):
Through HtmlHelpers like #Html.TextBoxFor(x => x.Price), which will generate a "suitable" HTML output:<input id="Price" name="Price" value="">52.00</input> (recommended)
Through well formatted HTML (suitable ^_^ ):
<select id="SelectedLanguageId" name="SelectedLanguageId">
<option value="1">English</option>
<option value="2">Russian</option>
</select>
To summarize:
1)If you want to receive something back to a [HttpPost] method you should have a suitable HTML element inside your .
2)If you want only to display some data you could simply call model's property
3)Use HTML helpers instead of raw HTML code.
Note!
Model binding on complex Models is achieved through EditorTemplates, Hidden inputs inside Loops, Different kind of Serialization etc.
Related
I've been able to successfully return a model to a view and display the results in a strongly-typed fashion.
I have never seen an example where multiple models get returned. How do I go about that?
I suppose the controller would have something like this:
return View(lemondb.Messages.Where(p => p.user == tmp_username).ToList(), lemondb.Lemons.Where(p => p.acidity >= 2).ToList());
Does MVC let you return multiple models like that?
And then in the view I have this line at the top of the file:
#model IEnumerable<ElkDogTrader.Models.Message>
And I frequently make calls to "model" in the view.
#foreach (var item in Model)
If there were 2 models, how would I refer to them separately?
Is this possible with multiple models, or is this why people use ViewBag and ViewData?
You can create a custom model representing the data needed for your view.
public class UserView
{
public User User{get;set;}
public List<Messages> Messages{get;set;}
}
And then,
return View(new UserView(){ User = user, Messages = message});
In the view:
Model.User;
Model.Messages;
The ViewBag is useful because it is dynamically typed, so you can reference members in it directly without casting. You do, however, then lose static type checking at compile time.
ViewData can be useful if you have a one-off on your view data types and know the type and will be doing a cast in the view anyway. Some people like to keep the actual typed view pure in a sense that it represents the primary model only, others like to take advantage of the type checking at compile time and therefore make custom models needed for the view.
I believe ViewModel should be the way to go. Within the customary ViewModel, you can reference other models or define all the related domain models in the viewModel itself.
I have a pretty basic web application showing the content of a table. The user can change some parameters by clicking on links.
To simplify let's consider only a simple one, the name of the column used to order rows.
User can either click on the column or select the name of the column from a right sidebar to change the ordering.
I use an <option> element to show the list of available column name.
Is it correct to take those names directly from the Model object or should I somehow pass them from the Controller? This is the code by now
<select>
#{
// get list of all properties names
List<string> EtlProceduresNames = (((new MIPortal.Models.EtlProcedure()).GetType().GetProperties()).Select(item => item.Name)).ToList();
// display an option item for each property name
foreach (string EtlProceduresName in EtlProceduresNames) {
<option value=#EtlProceduresName >#Html.DisplayName(EtlProceduresName)</option>
}
}
</select>
This way the View interact directly with Model. Is this a conceptual error? Should I put that code on the Controller and then store the name list on a ViewBag object?
You should not access the Model directly from the view as that is against the MVC pattern (separation of concern), and you also should NOT be putting this data into the view bag.
View bag should only be used for the simplest of things to pass around and not when you have objects full of data.
Look into using Viewmodels, that is the best way of coding asp.net MVC.
Example of such-
http://www.codeproject.com/Articles/826417/Advantages-of-ViewModel-in-MVC-Model-View-Controll
I can suggest you to use so called ViewBag injection.
You need to declare an action filter attribute class for your data.
public class EtlProceduresNamesAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//you can add some caching here
filterContext.Controller.ViewBag.EtlProcedures = (new MIPortal.Models.EtlProcedure()).GetType().GetProperties()).Select(item => item.Name)).ToList();
}
}
All you to annotate your view action with this attribute, for example
[EtlProceduresNames]
public ActionResult Action()
{
return View();
}
You need to replace you view code with
<select>
#foreach (string EtlProceduresName in ViewBag.EtlProceduresNames)
{
<option value=#EtlProceduresName >#Html.DisplayName(EtlProceduresName)</option>
}
</select>
I have two models which is connected. To simplify lets say I have a Post and Comment. When the details page Post, i want to build a form for posting a comment.
I can do that easily with just plain html. But I wish to use the Html.BeginForm.
First I pass a new Comment object to the details page from the controller.
#{
ViewBag.Title = "Details";
Comment newComment = ViewBag.Comment;
}
#using (Html.BeginForm("Create", "Comments", FormMethod.Post))
{
#Html.EditorFor(newCooment => newComment.Comment, new { htmlAttributes = new { #class = "form-control" }
})
But how can i tell the HtmlHelper to use my Comment model? ("newComment => ... " does not work)
So this happens in your controller. You need to have a view model which is a container for all the objects you want to pass to your view. In your view you then take advantage of the #Model property to access this VM.
public class MyController : Controller
{
public ActionResult Index
{
var myViewModel = new MyViewModel
{
Post = post,
Comment = comment // assumed that these have been read from a DB
};
return View(myViewModel);
}
}
The view model:
public class MyViewModel
{
public Post Post {get;set;}
public Comment Comment {get;set;}
}
In your View:
#model some.namespace.to.viewmodel.MyViewModel
#using(Html.BeginForm())
{
}
#Model <-- this is your MyViewModel instance you created in the controller
#Model.Comment <-- this has your Comment in
#Model.Post <-- this has your Post in
In the controller we do this return View(myViewModel). This is telling the Razor engine that we want to set the #Model to our ViewModel and use it in our page.
I tend to stay clear of helper functions that create a whole bunch of html. I like full control over my HTML so I use the #Html.TextBoxFor() or the #Html.TextAreaFor() those "low level" helpers. If I want absolute control then I just write the HTML my self! The id properties should relate to your object levels:
<input id="Post.Name" type="text" />
It's kind of the architecture of MVC right, so you have Models which define the DB Domain. You have Views which show the model information. You have controllers which should delegate getting the models from the DB and sending them to the page. To get multiple Models to the page we need to use a ViewModel.
The semantics are important and widely used through many systems. Web and Desktop, so they are pretty important to understand.
In my products, I use the N-Tier architecture approach. I have services, DALs, Controllers, ViewModels, Models, DTOs.
DTOs are what I map models to, it stands for Domain Transfer Objects. So I got my Domain Model, I may not want everything on it (I don't use navigation properties for example so my DTOs have those navigation properties), in order to reduce it I create a DTO which is what is used around my system to transport that data. When I want to save that data I map it back to the Domain Model and persist to the database.
A key example is if you are using the ASP.NET Identity stuff. So ASP.NET went down the route of making the authentication EF Code First friendly. If you look at the User model for this, it has a tonne of properties and navigation's that I don't need to use. Especially if you just want to register a user.
My registration DTO will have a field my User does not. ConfirmPassword, I want it on my register so that I can confirm that the original password is what they meant it to be. But it stops at my validation layer, past then, it gets completely dropped - when we've confirmed the passwords match I only need the original password they entered.
So I have 3 views, a controller and one model. (just an example) The first view sets the user first name and last name. Which gets posted back to the controller, and I can see the data in the view-model. The controller then calls the second view sets the email (I can call the data from view 1). The third view shows all of the data (the original stuff from view 1 is no longer there)
#Html.DisplayFor(m => m.FirstName)
#Html.DisplayFor(m => m.LastName)
#Html.DisplayFor(m => m.Email)
Do you think creating a static singleton model would work in the controller? or should I be using TempData
EDIT: sorry I forgot about my controller
Would my GET methods in my controller need a parameter?
[HttpGet]
public virtual ActionResult SignUp1(model m)
{
return View(m)
}
you can call into another view using #Html.Partial("view name", object) if you want to preform logic you can call another controller action with #Html.Action("action", "controller", object). then it's just like any other controller action. typically calling actions from a view are decorated with [ChildActionOnly]
Static is a bad idea for web pages, because it is not inherently thread-safe (see here). That means that you'll get really bizarre behavior if you have two or more people using it at once.
I'm not sure why you're even considering doing it this way - is there some specific reason you're thinking about it? The proper way to do it would be to post the model back to each controller action from each view, populating more data each time. Alternatively, you could post back to the same action, and then return the appropriate view based on which fields are missing from the model (and the display if none are).
I wanted to create a simple paginated search for my project. I'm having trouble with my 'advanced search' page, where I have several textbox inputs that the user would fill with the appropriate data (basically just several filters).
My view is strongly-typed with the paginatedList class similar to the NerdDinner tutorial.
In my controller, I wanted to pass the PaginatedList as a parameter since my view contains several bits of info from the PaginatedList model. The PaginatedList was null (as parameter), then I changed added the route; the object itself is not null anymore, but the values are.
View:
<%= Html.TextBox("Manufacturers", Model.Manufacturers) %>
<%= Html.TextBox("OtherFilters", Model.FilterX) %>
//...etc etc
Controller:
public ActionResult AdvancedSearchResults(PaginatedList<MyModel> paginatedList) {
//...
}
Any ideas? Am I even going about this correctly? Should I create a ViewModel that encapsulates the paginatedList info as well as the additional info I need instead?
You may want to create SearchCriteria class which contains user's input data for filtering.
Controller will have the Action as below:
public ActionResult AdvancedSearchResults(SearchCriteria criteria)
{
PaginatedList<MyModel> result = SearchService.Search(criteria);
return View(result);
}