General approach for displaying a message on any page - c#

I have a fairly simple (to explain) requirement for my ASP.NET MVC web application:
On any page, be able to display a message based on an event that happened on a preceding page. Messages may include dynamic content, such as information entered by the user in a form submission.
Similar to this...
The approach must work in POST-REDIRECT-GET scenarios.
If the user has two browser tabs (which share session state) open on the application, then any message should only be displayed in the tab in which the related event happened.
For example:
After submitting a feedback form, a message thanking the user (by name)
When logging in, a message telling the user the date/time when they last logged in to the application
My first thought was to use a strongly-typed view with an appropriate model (with a string property) for this. However this would mean changing every single existing non-strongly-typed view to use this model, and updating all existing models to inherit from this new model.
This seems like overkill.
An alternative is to store the message in TempData:
// In my controller
TempData["Message"] = "Some kind of message";
// In my shared master view
#if (TempData["Message"] != null)
{
<div class="message">#TempData["Message"]</div>
}
Using TempData is covered in a bit more detail in this blog posting. Interestingly, the author also suggests using a custom HTTP header as an alternative. An interesting idea, but does not work with POST-REDIRECT-GET.
Is using TempData the preferred option, or is there a "better" alternative? (e.g. something more strongly typed)

My first thought was to use a strongly-typed view with an appropriate model (with a string property) for this. However this would mean changing every single existing non-strongly-typed view to use this model, and updating all existing models to inherit from this new model.
This is the only consistent, reusable, testable solution to do this that I can imagine, despite the extra work it may cause.

It is best practice to use ViewModels to communicate between View and Controllers. You can have a base View Model and all other View Models derived from that as below:
public class BaseVM
{
public string Message{ get; set;}
}
public class CreateViewModel: BaseVM
{
public string CustoomerName{ get; set;}
}
You can populate the Message property while returning the model to the controller as below:
public ActionResult Step2()
{
//Some Logic
step2Model.Message = "Yes, Success..!!";
return View(step2Model);
}
After that, on each view page, you can check if that property has something in it.You can do so as below:
#if(!string.IsNullOrEmpty(Model.Message))
{
//Show message
}
EDIT:
OP is well aware of this approach, but still keeping this answer as it has a snippet to show how to do this in code. Secondly, when it comes to use ViewModels, I agree with following comment by CodeCaster in his answer.
This is the only consistent, reusable, testable solution to do this that I can imagine, despite the extra work it may cause.

You could make a convention in your project that a message to display would always go into ViewBag.InfoMessage dynamic variable. So, in your layout file you would display it if it's passed into the view from a controller.
More strict way would be to create a base model class with InfoMessage property and derive all other models / viewmodels from that base.
To persist the message through POST-REDIRECT-GET scenario, use a session variable which you'd clear once the value is sent to the view - to avoid displaying on more than one page.
Here some code:
public class BaseViewModel
{
public string InfoMessage { get; set; }
}
public class SpecificViewModel : BaseViewModel
{
// other model properties
}
In your controller then:
SpecificViewModel vm = new SpecificViewModel();
vm.InfoMessage = Session["InfoMessage"] as string;
Session["InfoMessage"] = null;
// other code
return View(vm);
Of course your view will have strongly typed model: SpecificViewModel.

Related

further clarification about passing data from controller to layout sub view in asp.net mvc 5

I guess I have a pretty standard problem as I want to pass certain data from an asp.net MVC 5 controller to a shared view, in my case navigation.
I have a template that shows username and userpicture in the navigation shared view, so I need to pass the respective data to it.
My layout structure:
Shared\Layout
Shared\Header
Shared\Navigation
%Body%
Shared\Footer
Where the Layout ist the master view and of course I also have my other views like Home\Index.
As you can imagine, I have to show the username and his userpicture in every view except the login/registration or any error views.
These views do not use the layout at all, so everytime a view is rendered using the layout structure, the user is already logged in.
So I was researching about ways to pass data from my controller to the navigation view, although my controller returns the Index view and would appreciate some clarification on their disadvantages and valid choices in my use case:
Use case:
My project has a pretty dumb MVC application that the user can access. Once he logs into the MVC app authenticates the user against the same webapi where it get's it's data from and stores the access token as well as other user details for further requests. I'm not yet sure where to store that data. As far as I understand it, the options would be Cookies, Session and local storage.
As I am pretty new to asp.net, MVC and C# in general, I didn't yet figure out how to make the [Authorize] Attribute work inside the MVC app so it can mark the user as authenticated :/
I guess the key problem is that the MVC app does not have access to the database and therefore cannot check the login and populate the User Identity.
How to transfer data from controller to view:
ViewBag: The easiest way of passing data to the view. It is not strongly typed and can be accessed in all views. I was told it is kind of a bad practise to use it and was advised to use viewModels.
ViewData: seems to be kind of the same thing as viewdata.
ViewModel: A strongly typed model that is passed to the view and needs to be declared in any view that uses it. So if I want to use it in my navigation view, I'd need to declare it there. The big disadvantage of this approach is that every viewmodel needs to have kind of a baseViewModel so they have a common structure which appearently can cause problems later down the road and also prevents me from inheriting other models to populate my viewModelStructure.
Cookies: Obviously I can store data in cookies during login and then access them in the view, but the cookies HAVE to be there so I would not be able to save this information in the session or local storage
Session: I can also store data in the session, but the session expires when the user closes the browser tab.
LocalStorage: This is pretty new to me so I can't judge it.
User Identity: I just discovered that I can also access the user's identity from Context.User.Identity.
Global Filter like the [Authorize] attribute or a custom one: If I understand it correctly, with a global filter I can populate needed data automatically in every controller action and exclude the ones that dont need it like Login/register etc. I'm not yet sure how to apply this way because of my project structure (see above).
RenderAction: I could also call another controller method via the RenderAction helper method to always render that section of the page.
How do you guys solve this problem?
If you need more clarification, please do ask.
Thanks :)
Use a base ViewModel. You can still use inheritance to build up functionality in your view models, they'll all just share a common base view model, as you said.
I'm not sure what problems you envisage with this approach, but I would suggest they're outweighed by the benefit of strongly typed, maintainable view models, that can be used by all your views, including partial views and _Layout.
A suggestion to get you started based on your layout structure:
public abstract class ViewModelBase {
public HeaderViewModel Header {get;}
public NavigationViewModel Navigation {get;}
public FooterViewModel Footer {get;}
public ViewModelBase(HeaderViewModel header, NavigationViewModel navigation, FooterViewModel footer) {
Header = header;
Navigation = navigation;
Footer = footer;
}
}
public class HeaderViewModel {
// properties
public HeaderViewModel(...) {
}
}
public class NavigationViewModel {
// properties
public NavigationViewModel(...) {
}
}
public class FooterViewModel {
// properties
public FooterViewModel(...) {
}
}

What is the best pratice to change a field value when a form is submitted in MVC?

I have a lot of fields in my View that are masked with jquery MaskedInput. They're masked because the user need to see it with a mask, but I need to remove these masks before commiting the value in my database.
I don't want to mess up the code, since this is one of the most importants Views in my project.
So what's the best pratice to do this?
Suppose I have this Model:
public class MyViewModel {
public string MyMaskedProperty { get; set;}
}
And this code in View:
#Html.TextboxFor(x=> x.MyMaskedProperty, new { #class="myMask"} )
Should I:
Remove the mask on my View, using javascript, before the form is subimitted
Remove the mask on my Model, changing the get of MyMaskedProperty to return an unmasked value
Remove the mask on my Controller, since it need to be unmasked only from here and beyond
Something better than the 3 solutions above.
Thanks in advance!
Similar to the second option, you might simply add a read-only field to your view model:
public class MyViewModel {
public string MyMaskedProperty { get; set;}
public string MyUnmaskedProperty
{
get
{
// return an "unmasked" version of MyMaskedProperty
}
}
}
Even if you were to modify the value with JavaScript, you can't really implicitly trust that and would want to validate server-side anyway. And doing it in the controller isn't ideal because that's just procedural code that would need to be repeated all over the place. Following the advice to "keep your controllers light and your models heavy", this logic really belongs on the model.
The model itself can be constructed from any version of the data, really. As long as the information used to construct the model can effectively construct it, then the model can expose all sorts of operations and properties for manipulating and viewing that information.

ASP.Net MVC Postback and Models

This is mostly a follow-up to a comment in this issu, but I don't have enough reputation to comment ...
ASP.Net MVC Postback a label value to your controller
Let's say I have a simple model:
public class SimpleClass
{
public String Label { get; set; }
public String FirstName { get; set; }
}
Label is changed based on user/client so it can't be a DataAttribute. If when posted back processing problems occur, we need to redraw the entire page. This is the crux of the problem of the previous post. The accepted solution is to do this:
#Html.DisplayTextFor(model => model.Label)
#Html.HiddenFor(model => model.Label)
#Html.EditorFor(model => model.FirstName)
That makes sense in that it works. But our models are much more complicated and extensive. This method will result in a ton of hidden fields which seems like a very dirty solution.
This brings me to JP's comment:
ASP.Net MVC Postback a label value to your controller
The solution there is to reload the model. But it's not just a reload, it's also a merge since you want to preserve any client-side data changes.
default: SimpleClass { Label="TheLabel", FirstName="Rob"}
postedback: SimpleClass { Label="", FirstName="Steve" }
we want: SimpleClass { Label="TheLabel", "FirstName="Steve" }
My question is does MVC have a good way to know what fields were postedback so it merges correctly? We would need to only merge postedback fields not blank properties.
Or is it better to just ajaxify the entire postback and not do a form submit? This avoids all model reload issues on submit.
Update
To give Pablo credit I accepted his solution. To see my simple example of his solution, check Robert Harvey's comment in the Answers below:
ASP.Net MVC Postback and Models
The main problem here is in trying to fit WebForms' PostBack concepts into MVC. There is no such thing as a stateful postback where things just automatically retain their state.
You only have ViewModels that are bound to the view, and ViewModels that are posted by the view to the Controller. They don't even necessarily need to be of the same Type. Meaning, the controller should only receive the data that the user indeed can change, not large objects with many properties that were part of the initial ViewModel but are read-only.
Labels commonly represent read-only texts and they are not editable form elements. Which is why you have to use hidden fields for that.
And yes, sometimes that implies that you have to reload the original data in the controller, and sync up with new data that you posted, which isn't necessarily a bad thing. If you bind read-only data to a view, which the user can't manually edit, you shouldn't really trust that data coming back in a post afterwards. Just because your html might try to make it read-only doesn't mean I can't manipulate the post and ultimately change your "read-only" data without you knowing.
I just read the second question you mentioned, and from the looks of it, his main problem was that he was trying to reuse the same ViewModel again, so all the data was missing and the model wasn't valid. The solution to that is indeed quite simple, ONLY post what you need, as a new ViewModel type, and have the controller take care of the rest.
[Moved from OP]
I think this is what Pablo is suggesting for those who are wondering. It seems to be a good pattern to resolve this problem.
Models:
public class SimpleClass : SimpleClassPostBack
{
public String Label { get; set; }
public SimpleClass()
{
// simulate default loading
Label = "My Label";
FirstName = "Rob";
}
}
// contains only editable by the user fields
public class SimpleClassPostBack
{
public String FirstName { get; set; }
}
Controller Actions:
[HttpGet]
public ActionResult SimpleClassExample3()
{
SimpleClass simpleClass = new SimpleClass();
return View(simpleClass);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SimpleClassExample3(SimpleClassPostBack postBackSimpleClass)
{
Boolean errorOccurred = true;
if (!errorOccurred)
{
// do whatever success action is necessary
}
// redraw the page, an error occurred
// reload the original model
SimpleClass simpleClass = new SimpleClass();
// move the posted back data into the model
// can use fancy reflection to automate this
simpleClass.FirstName = postBackSimpleClass.FirstName;
// bind the view
return View(simpleClass);
}
View:
#model SimpleClass
#{
ViewBag.Title = "Simple Class Example3";
}
<h2>Simple Class Example3</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<label for="FirstName">#Html.DisplayFor(m => m.Label)</label>
#Html.EditorFor(m => m.FirstName)
<br/>
<button>Submit</button>
}
You should only send data from the client to the server that the server can't "figure out" on its own. If the server knows what the labels were when the user first navigated to that view then if the user cannot modify them, the server will be able to know what the labels are when reloading the view.
Use hidden fields to identify the database objects. So your SimpleClass should probably have some sort of Id which you will use in the hidden input. Use the EditorFor for FirstName. Now when the form is posted, use the sent Id to find the correct SimpleClass from the database and modify its FirstName property with the value posted. The Label property will be null which is ok since you don't need to save it. Now if there's a problem in the post and you want to send the same view back like it was, you need to repopulate the Label the same way you did when the user arrived to the view for the first time. The values of Id and FirstName properties will be automatically sent back to the view with the model state.
In summary:
Only post data that is needed to identify something and what the user
can edit in that view.
Don't trust the client to send you anything valid. The user can change the values of the hidden field labels to anything.

How should I be binding my business models to my views?

I've come across an interesting question during my development. Right now, I use a database-agnostic unit of work layer to abstract the access of data from the actual database dependencies in my ASP MVC 4 web application.
Each individual database project which implements the unit of work interfaces are aware of my business models (models that go directly to/from the database). I'm not too sure how I feel about this approach, but that's not the question I am going to ask.
Should I be using a solution like AutoMapper to convert my business models to/from domain models - models that are passed to the view and used for any work that shouldn't have access to database fields (i.e., IDs)?
For example, consider inside my BusinessModels project, I have the following classes
BusinessModels
/UserAccounts/
User.cs
- ID
- Username
- HashedPassword
- Salt
UserSettings.cs
- IsSubscribedToNewsletter
- AllowDirectEmails
Would it make any sense to bind these User, and UserSettings models into a single model using AutoMapper like so
MyProject
/DomainModels/
User.cs
- Username
- HashedPassword
- Salt
- IsSubscribedToNewsletter
- AllowDirectEmails
for the purpose of views?
This question also extends to non-MVC projects but I feel seeing as I am working on an MVC project it would make more sense to ask it in that tag.
TLDR is there any point in mapping business models/entities to view models or does that provide an unnecessary layer of abstraction? And if so, would the Repositories contain business models, or view models (which map automatically to business models under-the-hood)?
You can use view models for two different things:
rendering a new view (GET action), passing the view model object as the model for the view
receiving data back from the view, in a Post Action (POST action), using the view model as parameter
(I know, the second is arguable. But it's not strange to use the view models for this)
The model for the GET action needs all the properties neccessary to render the View:
the values of the entity you're showing/editing
extra values needed to render the view (for example SelectLists for drop down lists)
Suppose that you have a User which can belong to one UserGroup.
In this case, if you want to edit the user, the model needs:
the User data
a list of UserGroups
I'd use a model like this:
public class EditUserModel
{
public User User {get;set;}
public SelectList UserGroups {get;set;}
}
As you can see, I directly add the User as a property. But I don't add the list of categories as a property, because I don't need the whole list of categories, with all their properties in the view. Besides, if you unit test the controller you can verify that the SelectList is as expected (that couldn't be done if you created the User Groups list in the view)
But, what if you don't need all the properties of the user in the View? Is it worth removing the User property, and add individual properties for Name, Email, JoinedData, Active... ? I think the anser is NO. Imagine you add/remove or rename some of the User entity properties. If you had individual properties in the view model, you'd have to change them as well, before updating the View. And, if you rely on automatic mapping (auto mapper, value injecter) you would't even realized if you make some mistake.
I also said that the view model can be used for posting back data to the controller. So you could do this:
[HttpPost]
public ActionResult Edit(EditUserModel userModel)
If you do so, the model binder will populate the userModel with the values in the form controls. So you'lll get back a half empty model. In this case, the UserGroups list would be null, and, depending on how many of the User's properties you edit, the User could also have many null/non-initialized properties.
To avoid making errors, in some occasions is advisable to create a different model (and probably auxiliary classes) to make it clear what is expected to be posted to the model.
For example, if you have an action to show the whole user data, but which only allows to change its password, you could create a class with two properties: Password, and PasswordConfirmation.
In this case, the view model for the POST could only have the Password and PasswordConfirmation. And derive a model for the GET which has this inherited properties, and also the list of User Groups and the User.
Why inheriting and not using independent classes? Simply beacuse when you use something like Html.TextBoxFor(m => m.User.Name), the Model Binder will be able to set the Name property of the User property, only if the parameter for the post action has the same structure. I.e. if the view model for the get has this structure:
public ChangePasswordModel
{
public string Password {get;set;}
public string PasswordConfirmation {get;set;}
// extra properties, like the list of user groups, the user data...
}
And the model for the post has this structure:
public PostedChanegPasswordModel
{
public User User {get;set;}
}
The content of the input rendered by Html.TextBoxFor(m => m.EditedUser.Name) won't be bound to the User.Name of the PostedEditViewModel.
But if you make this:
public EditUserModel : PostedEditUserModel
{
// extra properties, like the list of user groups
}
the data will be bound without any problem.
In general you have to be careful with the models you use for posting and getting. And I recommend using as many different view models as neccesary.
When to use automatic property mapping to completely new view and different models?
You must have a very strong reason to have different view models. This could be a result of starting the application from outside in (i.e. design first) or because a team is developing the UI before or while the business logie is being implemented.
In this case you can find that the classes for the view models, and the views themselves are already defined, and are quite similart to your entities, but not exactly equal. This is one case when I think it can be good to use mappers.
The other reason to use different classes would be to decouple the interface from the logic. But this usually only happens in the previous case.
Regards viewmodels I treat them like a summary of the data you wish to work with.
So taking from your example, your viewmodel would contain data from both the User and UserSettings classes. Let's say you had a view named UserData.cshtml then I would code it up like so:
public class UserDataViewModel
{
public string Username { get; set; }
public bool AllowDirectEmails { get; set; }
// etc ...
}
public ActionResult UserData()
{
var viewModel = new UserDataViewModel();
viewModel.UserName = "Whatever";
viewModel.AllowDirectEmails = false;
// Or however you get the data for the user.....
return View(viewModel)
}
Hopefully you get the idea. So you are on the right track with merging information from externals classes into one viewmodel class. Bascially tie everything together in the viewmodel class.
I name the viewmodel class the same as the view that it's going to be used for. This can help documentation, as well as make it easier for devs new to the code to follow.

ASP.NET MVC: Populating Derived Strongly Typed View with Base Strongly Typed View

In my application I have different pages: Contact Us, About Us, Home
They all have the same base elements that needs to populate the view:
Page Title
Meta Description
User Information
However on each page, they have some elements that are different:
Contact Us
Contact Information Model
Contact Form Model
About Us
Extended User Information Model
Home
Home Page Text Property
They are all routed to the same Controller Action Method because most of the functionality is similar other than populating the "extra" information dependent on page type.
So far I have done something where:
PageDetailViewData pageDetailViewData = new PageDetailViewData {Title = title, Desc = desc....}
and following this I have:
switch ((PageType)page.PageType)
{
case (PageType.Contact):
return View("ContactUsDetails", pageDetailViewData);
default:
return View(pageDetailViewData);
}
The question is how do I populate the "extra" information? I am not sure if I am going about doing this the right way. Any insight to better structure the logic flow would be appreciated.
The answer of using interfaces to imply some commonality between your view models is certainly going to help to answer some of the points in your questions.
I would however ask how wise it is to "refactor" your Action to support multiple views of differing data structures.
MVC controller actions typically represent the minimum amount of code required to gather the specific data required to generate the intended view. It's not completely uncommon for a single action to return different views of the same model data (Html view or Mobile view for example) but by varying both the structure of the data and view that will generated you introduce a few problems.
In particular you violate common best practices like the Single Responsibility Principle and make your code much more complicated to test - and Pain free testing and TDD are part of the big win with ASP.Net MVC after all.
Personally I would have a separate Action.
As far as your view models are concerned, how would you do it if this was a database?
You would have separate queries for separate data right?
A user's profile information would be queried separately from the page meta data information. This would be done for a number of reasons that could include the ability to cache certain parts of the data but not others for example.
So with the above suggestions your code might look like this (Warning: this code wasn't written in Visual Studio and is probably full of syntax issues):
public interface IMetaDataViewModel
{
PageMetaData MetaData{get; set;}
}
public class HomeViewModel : IMetaDataViewModel
{
public PageMetaData MetaData{get; set;}
public string HomePageText{get; set;}
}
//other view models go here....
public class CommonPagesController : Controller
{
private MetaDataProvider _metaProvider = new MetaDataProvider();
private PageDataProvider _pageDataProvider = new PageDataProvider();
private ContactDataProvider _contactDataProvider = new ContactDataProvider();
public ActionResult Home()
{
var viewModel = new HomeViewModel
{
MetaData = _metaProvider.GetPageMeta();
HomePageText = _pageDataProvider.GetPageData();
};
return View(viewModel);
}
public ActionResult Contact()
{
var viewModel = new ContactViewModel
{
MetaData = _metaProvider.GetPageMeta();
ContactFormData = _contactDataProvider.GetData();
};
return View(viewModel);
}
//you get the picture...
}
There are several ways you could also refactor out the generation of the view model code but thats one possible pattern.
I appreciate that this answer does have a certain amount of opinion in it but I would consider having separate actions to be best practice.
Hope that helps.
The title of your question almost gives you the answer. You can use some form of polymorphism to accomplish this. You could define a base class with the shared properties, or alternatively an interface like this:
public interface ICommonPage
{
string Title { get; }
string MetaDescription { get; }
string UserInformation { get; }
}
Then define three strongly typed ViewModel classes that all implement this interface (or derive from the base class):
ContactUsViewModel : ICommonPage
AboutUsViewModel : ICommonPage
HomeViewModel : ICommonPage
On each of those ViewModel classes, you add the extra properties that you need for those Views.
In your Controller Action, you will need to switch on PageType to select the correct ViewModel and populate it with data.
You will also need to creat three different Views (.aspx) that are strongly typed to each ViewModel class.
If you have shared rendering for the common data, you can extract that into a strongly typed UserControl (.ascx) that is typed to ICommonPage.

Categories

Resources