What's the syntax for Html.Display()? - c#

I've got a model like this:
public class HomeViewModel
{
public IEnumerable<NewsItem> NewsItems { get; set; }
public decimal TotalSales { get; set; }
public IEnumerable<SalesOrderHeader> SalesInformation { get; set; }
}
And in the View I want to do this:
<p>#Html.Display(Model.TotalSales)</p>
But I'm running into this error:
HomeViewModel does not contain a definition for Display"
Which is odd because it should be the Html type that has Display.
What is the correct syntax for what I'm trying to achieve?

It's better to use DisplayFor (Display) fo doing this. If you need to add some display attributes (like only last 2 digits to be shown) you will do this only one time in the ViewModel.
<p>#Html.DisplayFor(model => model.TotalSales)</p>
If you really want to use Display, the MVC is smart enough to render this with your data from Model.
<p>#Html.Display("TotalSales")</p>

Okay it's actually:
<p>#Model.TotalSales</p>
Solved!

Related

ASP.net MVC Is it possible to modify a class object in a view?

I am a newbie and creating a website where you can create your own custom quizes. Ive made a database that stores a class object mytests that consists of a name, and a list of questions parameter.
public class MyTests
{
public int ID { get; set; }
public string name { get; set; }
public string description { get; set; }
public List<MyQuestions> AllTestQuestions;
}
//using this object for questions
public class MyQuestions
{
public string QuestionDescription { get; set; }
public string MultipleChoiceCorrect { get; set; }
public string MultipleChoiceB { get; set; }
public string MultipleChoiceC { get; set; }
public string MultipleChoiceD { get; set; }
public string Answerexplanation { get; set; }
}
I'm using the default database code generated by visual studio. I have no problem adding this test object(mytest) to the database, but what I want to do is that on the edit.cshtml view I want to be able to add elements to the question list before returning the object to the database saved.
The problem is I don't know how to edit the model object from the view, or if this is even possible. I could maybe get it to work through a redirect? but I thought that adding the elements directly from the view would be easier. Is it possible to modify the model.object inside a view from the view (putting security concerns aside)?
For example model.title = something;
or
model.list.add()
Is anything like this possible?
If this question is not clear please let me know and I will try to clarify in the comments.
Yes, it is possible to edit the model from within the view.
From within your .cshtml file specify the view model using the #model declaration, then edit the model like so:
#model Namespace.For.MyTests
#Model.name = "Hello World";
<p>#Model.name</p>
Whilst this would work, it's not really what the view is for so I wouldn't recommend it.
The view is about presenting your data, not mutating it - that should be done in the controller, or domain layer. As soon as the user leaves the page then your changes will be lost due to the stateless nature of the web (.NET MVC passes data to the view from the controller, then ends the request).
This should be done at the controller level. You could do it on a view but it's not what the view is for.
Your issue is that if the page is refreshed you will lose you content, so if you do anticipate on the page refreshing you will need a way in which to temporarily hold the information before it being saved.
On a side note, I'd also consider renaming your classes "MyTests" to "MyTest" (singular) and "MyQuestions" to "MyQuestion"... it's just good practice because then you'd have a List of singleton "MyQuestion" in a "MyTest". EntityFramework Codefirst will pluralise the names when the database is created/update.

View expecting IEnumerable

Well im kinda new in Asp.net Mvc and im learning alone from scratch, i have a aplicattion that controls expends and earnings and what i am trying to do now is, basing on a list of earnings and expends give me the balance from a user, im having a lot of problems trying to control this and i dont know if i am doing it the right way
Here is my model:
public class Balance
{
public int BalanceId { get; set; }
public List<Expense> Despesas { get; set; }
public List<Earning> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
Soo what i did was, first trying to control when the user inserts a Earning or a row like, verifying if the User already exists on the database in the control method Create on the expenses and in the earning, if it doesnt exist he add the aplicationUserId and the expensive or the earning.
I want that the balance appears in every page, soo i added this to my Layout.cshtml
<li>#Html.Action("GetBalance", "Home")</li>
it calls the controller GetBalance:
public PartialViewResult GetBalance()
{
var userId = User.Identity.GetUserId();
var balance = db.Balance.Where(d => d.ApplicationUserId == userId);
return PartialView("_GetBalance",balance);
}
Send to the view _GetBalance the balance model:
#model <MSDiary.Models.Balance>
<p>Saldo: #GetBalance()</p>
#functions
{
HtmlString GetBalance()
{
decimal saldo = 0;
if (Model.Expense.Count != 0 || Model.Earning.Count != 0)
{
foreach (var item in Model.Despesas)
{
balance += item.EarningValue;
}
foreach (var item in Model.Rendimentos)
{
balance -= item.ExpenseValor;
}
}
return new HtmlString(balance.ToString());
}
}
What i want to know is, if there is a easyer way to do this, or what i can do to do what i want, i cant get it why my view expects something different can someone explain me what i am doing wrong?
Ps: Sorry for the long post and English, but i want to learn more :)
Firstly, the model #model <MSDiary.Models.Balance> needs to be changed to:
#model IEnumerable<MSDiary.Models.Balance>
Also, the method GetBalance should ideally be placed in a class not in GetBalance partial view. You could achieve this two ways, either through extension methods or have a Balance View Model that has the calculated balance as a property which is then passed down to your view.
As an example via an extension method:
public static class BalanceExtensions
{
public static string GetBalance(this Balance balance)
{
string displayBalance = "0:00";
// Your logic here
return displayBalance;
}
}
And then in your Partial View you can use the new HTML Helper:
#Html.GetBalance();
As an additional note I would change List to IEnumerable for expenses and earnings as it appears you are only exposing the data and not manipulating the data.
Your model would then look like:
public class Balance
{
public int BalanceId { get; set; }
public IEnumerable<Expense> Despesas { get; set; }
public IEnumerable<Earning> Rendimentos { get; set; }
public string ApplicationUserId { get; set; }
}
#Filipe Costa A few things here.
You should probably name your view the same thing as your method. The underscore preceding the name is fairly standard so I would suggest using that same name for the method. If the name of the method and view are the same you can simply pass in the model and not have to do the name + model signature of PartialView method. It's simpler.
Aside from that your code is fine but your .cshtml partial view should have this for the first line. That will accept the list you're passing.
#model IEnumerable<MSDiary.Models.Balance>
<h1>#Model.BalanceId</h1>
#*Do other stuff!*#

How do I prevent hidden fields from interfering with server side validation in MVC?

I have a partial view that displays a number of inputs based on a view model. In some situations, some of those inputs are not rendered by the partial view, but are still decorated with [Required] attributes in the view model. As a result, when the form is posted back to my controller, ModelState.IsValid returns false. Is there a way to bypass this?
You can use Foolproof to validate your fields conditionally. This way, they'll be required only when they need to, as you can see in the example of the link.
private class Person
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public bool Married { get; set; }
[RequiredIfTrue("Married")]
public string MaidenName { get; set; }
}
In this example, MaidenName will only change your ModelState.IsValid to false if Married == true
I'd recommend separating your validation from your base model.
public class MyModel
{
public string MyString { get; set; }
public string MyHiddenField { get; set; }
}
public interface IMyModel_ValidateMystringOnly
{
[Required]
string MyString { get; set; }
}
[MetadataType(TypeOf(IMyModel_ValidateMystringOnly))]
public class MyModel_ValidateMystringOnly : MyModel
This allows you to create any number of validation types, and only validate what you want when you want.
public ActionResult ShowMyModel()
{
var model = new MyModel(); // or Respository.GetMyModel() whatever..
View(model);
}
public ActionResult ValidateModel(MyModel_ValidateMystringOnly model)
{
if (ModelState.IsValid)
{
// Hey Validation!
}
// MyModel_ValidateMyStringOnly is a MyModel
// so it can be passed to the same view!
return View("ShowMyModel", model);
}
This is just an example, but should be clear on how-to reuse the same model with or without validation.
I have used method at times where the form changes slightly based on specific DropDown or Radio Button selections.
Inside your Action method before you check ModelState.IsValid you can do something like ModelState.Remove("Object.PropertyName")
Note: The property name should be the same as the ID rendered to the client. Use a "." for any underscores.
If isSomeCondition Then
ModelState.Remove("Property1")
ModelState.Remove("Property2")
End If
If ModelState.IsValid() Then
...
End If
You should always separate your VIEW model from your DOMAIN model. There is a very good reason for this and it has to do with security. When you use your domain models as your view models you are vulnerable to an overposting and/or underposting attacks. You can read more about it on these pages:
http://odetocode.com/blogs/scott/archive/2012/03/12/complete-guide-to-mass-assignment-in-asp-net-mvc.aspx
http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx
https://hendryluk.wordpress.com/tag/asp-net-mvc/
In short if you don't need a field then it should not be in your view model. You should convert - map your view models to domain models. Although it can be tedious it makes your application much more secure. There are libraries you can use to help you with mapping such as Automapper.
EDIT: Since my original answer, I have come to a conclusion that the easiest way to deal with this type of scenario is to have your view model implement IValidatableObject interface and then write your validation logic inside the Validate method. It does not give you client side validation but it is the most effective and clean way to accomplish custom/scenario based validation without writing your own custom filters.
You can read more about it here: http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3

Using an Editor Template for a List

I have something like this
public class ResumeVm
{
public ResumeVm()
{
EducationVms = new List<EducationVm>();
}
public Guid UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<EducationVm> EducationVms { get; set; }
}
public class WorkExperienceVm
{
public string Title { get; set; }
}
I now want to make an editor template for each of the EducationVms, I made a Template for 1 Education Vm and tried to use
#Html.EditorForModel("WorkExperienceVm")
but it does not know how to pass in the EducationVms
If I do
#Html.EditorForModel("WorkExperienceVm", #Model.EducationVms )
It gets made as it expects only 1 Vm to be sent in.
// View (WorkExperienceVm)
#model ViewModels.WorkExperienceVm
#Model.Title
The EditorForModel overload that you're using is incorrect. EditorForModel produces an editor template for the current model (i.e. the ResumeVm) and the string you're passing in is the additional view data object, not the name of the view.
I'm assuming that "WorkExperienceVm" is the name of the view. Try using EditorFor:
#for(int i = 0; i < Model.EducationVms.Count; i++)
{
Html.EditorFor(m => m.EducationVms[i], "WorkExperienceVm")
}
An alternative is to create a template that's actually called EducationVm.cshtml and type it to EducationVm, then you can just do the following and the framework will figure out that you want the template called for each item in the collection:
#Html.EditorFor(m => m.EducationVms)
Unfortunately this approach can't be achieved using UIHints or passing in the view name manually into the helper, though that's fairly unlikely to get in your way if you don't mind adhering to strict naming conventions for your templates.
I wrote another answer a while ago explaining the differences between the different helpers for editor templates. It deals specifically with the "label" helpers but the same principles apply to the "editor" helpers.

Display readonly text with MVC Model

I have an MVC Model, with a property defined as:
[DisplayName("Service Version")]
public string ServiceVersion { get; set; }
On the screen, I want it to display as:
Service Version: 0.1
'ServiceVersion' has the version number.
In the view, I have:
#model Main_UI.Models.HomeModel
And then:
#Html.LabelFor(m=>m.ServiceVersion) #Html.DisplayText(Model.ServiceVersion)
This isn't correct, as I get a nothing displayed for the value part. The label appears, but no version number. What am I doing wrong?
Edit: Changing the
#Html.DisplayText(Model.ServiceVersion)
, to just
#Model.ServiceVersion
has fixed it. Is this the right way to do this?
either you can try what #karthik has mentioned in the comments or you can use the attribute
[ReadOnly(true)]
[DisplayName("Service Version")]
public string ServiceVersion { get; set; }
and in the view use the default
#Html.LabelFor(m=>m.ServiceVersion) #Html.DisplayText(Model.ServiceVersion)

Categories