Conditionally display an item property on view in ASP.NET MVC - asp.net-mvc-3

New to ASP.NET MVC 3. This seems like it should be a really simple issue, but it's actually got me stumped. What I want to do is display a certain DateTime value if a value has been entered, or leave the space empty if no value has been entered. The code in my view is simply this:
<td>
#if (item.TimeReturned > DateTime.MinValue)
{
Html.DisplayFor(modelItem => item.TimeReturned);
}
</td>
which seems really basic and straightforward. Also, though it seems like overkill, TimeReturned explicitly defaults in the model to DateTime.MinValue.
No values are ever displayed. Just to make sure it wasn't some operator funkiness, I also tried
<td>
#if (DateTime.Compare(item.TimeReturned,DateTime.MinValue) != 0)
{
Html.DisplayFor(modelItem => item.TimeReturned );
}
</td>
which also displays nothing. I must be missing some fundamental insight. I could write a method in the model class to accomplish this, I guess, but it seems intuitive to do it this way. Any guidance would be greatly appreciated!

Try,
<td>
#if (item.TimeReturned > DateTime.MinValue)
{
#Html.DisplayFor(modelItem => item.TimeReturned)
}
</td>

You can use it as follows
#if (condetion)
{
#:#Html.DisplayFor(model => model.property)
}

Related

Editing a collection in a partial view

I was having problems updating child collections of my object ( Foreign key constraint, EF with collection of childobjects ) which was solved using this guide: http://www.codetuning.net/blog/post/Binding-Model-Graphs-with-ASPNETMVC.aspx
When cleaning up my code, I moved the collection editing to a partial view.
#Html.Partial("_AttendeeInformationFields", Model.CaptureAttendeeInformationFields)
The partial view looks like this
#model ICollection<EventModel.Models.AttendeeInformationField>
<table id="CaptureAttendeeInformationFields">
<tr>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().Name)</th>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().Required)</th>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().FieldType)</th>
#*<th>#Html.GetDisplayName(model => model.FirstOrDefault().InputType)</th>*#
</tr>
#Html.EditorForModel()
</table>
#Html.LinkToAddNestedForm("Lägg till", "#CaptureAttendeeInformationFields", ".AttendeeInformationField", "CaptureAttendeeInformationFields", typeof(EventModel.Models.AttendeeInformationField))
#Html.ValidationMessageFor(model => model)
Then I have a EditorTemplate for AttendeeInformationField that looks like this
#model EventModel.Models.AttendeeInformationField
<tr class="AttendeeInformationField">
#using (Html.BeginCollectionItem("CaptureAttendeeInformationFields"))
{
<td>#Html.TextBoxFor(model => model.Name) #Html.HiddenFor(model => model.MagnetEventId) #Html.HiddenFor(model => model.Id)</td>
<td>#Html.CheckBoxFor(model => model.Required)</td>
<td>#Html.DropDownListFor(model => model.FieldType, new SelectList(Enum.GetValues(typeof(EventModel.Models.FieldType)), Model.FieldType))</td>
#*<td>#Html.TextBoxFor(model => model.InputType)</td>*#
}
</tr>
The BeginCollectionItem is from this guide: http://ivanz.com/2011/06/16/editing-variable-length-reorderable-collections-in-asp-net-mvc-part-1/
This helps me with two things
1. The index is no longer a sequential 0-based integer series, and I can reorder my items, as well as add/delete without worrying about breaking the sequence.
2. The partial seems to loose the context, and my controls get names like "[0].Required" where it should be "CaptureAttendeeInformationField[0].Required". The BeginCollectionItem takes care of this.
The current problem is that these fixes doesn't seem to be compatible. I suppose it might have something to do with this disclaimer in the first article:
within this implementation we assume that the index is an integer starting at 0
I'm hoping that someone can point me in the right direction.
Adding items in this solution works.
Ugly solution
I sure hope this is not the only way to do this, but for now I've solved the problem like this:
foreach (var attendeeInformationField in viewModel.AttendeeInformationFields)
{
var attendeeInformationFieldId = attendeeInformationField.Id;
var originalAttendeeInformationField = original.CaptureAttendeeInformationFields.FirstOrDefault(aif => aif.Id == attendeeInformationFieldId);
if (originalAttendeeInformationField==null)
{
original.CaptureAttendeeInformationFields.Add(attendeeInformationField);
}
else
{
if (originalAttendeeInformationField != attendeeInformationField)
{
originalAttendeeInformationField = attendeeInformationField;
originalAttendeeInformationField.FieldType = attendeeInformationField.FieldType;
//originalAttendeeInformationField.InputType = attendeeInformationField.InputType;
originalAttendeeInformationField.Name = attendeeInformationField.Name;
originalAttendeeInformationField.Required = attendeeInformationField.Required;
}
}
}
I don't like it at all, but it works. There must be a better way of doing this.

MVC3 Razor foreach problems

I've got a model that is returning a IEnumerable of football picks for a variable number of users. Due to this, I'm dynamically building an html table with a variable number of columns. Basically, my picks will come back like this. However, each game is going to be repeated for each User, so I'm only adding the first 3 columns of each table row once, and then adding only a single tag until the gameID changes. I know there are probably better ways to do this, but it's just a side project that I need to get done quickly. And, I just want to figure out why it's not working.
GameID
GameDateTimeDisplay
GameTeamDisplay
WinningTeamDisplay
PickedTeamAbbr
OK, so here is the insanity that doesn't work. I can get my table headers created successfully, but then the tbody isn't working, but it's erroring in an odd place.
I had to put all the #Html.Raw(...) stuff in there because it was having trouble finding the end tags for the foreach and if statements without them.
Anyway, here is my code. The line that is causing the exception is this:
#gameID = #pick.Game.GameID;
The exception is --> Compiler Error Message: CS1525: Invalid expression term '='
The intellisense shows #gameID as a variable and the #pick.Game.GameID seems to be correct as well.
<table>
<thead>
<tr>
<th>Game</th>
<th>Game Date/Time</th>
<th>Winner</th>
#foreach(var name in #Model.UserNames) {
<th>#name</th>
}
</tr>
</thead>
<tbody>
#{
int lastGameID = 0;
int gameID = 0;
bool firstGame = true;
}
#foreach(var pick in #Model.Picks) {
#gameID = #pick.Game.GameID;
if(#gameID != #lastGameID) {
if(!#firstGame){
<text>#Html.Raw("</tr>")</text>
}
#Html.Raw(
<tr>
<td>#pick.GameTeamDisplay</td>
<td>#pick.GameDateTimeDisplay</td>
<td>#pick.Game.WinningTeam.TeamAbbr</td>
<td>#pick.PickedTeamAbbr</td>
)
}
else {
#Html.Raw(<td>#pick.PickedTeamAbbr</td>)
}
}
#Html.Raw(</tr>)
</tbody>
</table>
UPDATE:
I've removed the #Html.Raw, , etc... Also wrapped the gameID assignment in a #{}. It's now giving me an error on this line,
#{gameID = #pick.Game.GameID;}
Compilation Error: CS1501: No overload for method 'Write' takes 0 arguments
Here is the updated Code:
#foreach(var pick in #Model.Picks) {
#{gameID = #pick.Game.GameID;}
if(#gameID != #lastGameID) {
if(!#firstGame){
#:</tr>
}
#:<tr>
<td>#pick.GameTeamDisplay</td>
<td>#pick.GameDateTimeDisplay</td>
<td>#pick.Game.WinningTeam.TeamAbbr</td>
<td>#pick.PickedTeamAbbr</td>
}
else {
<td>#pick.PickedTeamAbbr</td>
}
}
</tr>
You need to surround it with { } to make it a codeblock
#{gameID = pick.Game.GameID;}
Also, you don't need the # inside the foreach/if statements because they're code blocks.
e.g. you could just write:
foreach(var name in Model.UserNames) {
Just write
#foreach(var pick in Model.Picks) {
<tr>
<td>#pick.GameTeamDisplay</td>
<td>#pick.GameDateTimeDisplay</td>
<td>#pick.Game.WinningTeam.TeamAbbr</td>
<td>#pick.PickedTeamAbbr</td>
</tr>
}
You don't need # inside code block. You can use #: instead of <text>, Html.Raw
You can see here Razor syntax reference
I determined that my Razor view code was simply too complex. The real problem was that I was trying to force a view to work with a Model that I had created for another view. I ended up creating a few more models specifically for this view. Code is much cleaner and best of all it works! Here is the code I ended up with.
<table>
<thead>
<tr>
<th>Game</th>
<th>Game Date/Time</th>
<th>Winner</th>
#foreach(var name in #Model.UserNames) {
<th>#name</th>
}
</tr>
</thead>
<tbody>
#foreach(var game in #Model.Games) {
<tr>
<td>#game.GameDescription</td>
<td>#game.GameDisplayDateTime</td>
<td>#game.GameWinner</td>
#foreach(var pick in game.GamePicks){
<td>#pick.PlayerPick</td>
}
</tr>
}
</tbody>
</table>

Simple Razor question regarding Html.Editor helper (MVC3)

Basically, I have a table with multiple editors like this:
<table>
<tr>
<td>#Html.EditorFor(x => x.Random1)</td>
<td>#Html.EditorFor(x => x.Random2)</td>
</tr>
<tr>
<td colspan="2">#Html.EditorFor(x=> x.Random3)</td>
</tr>
</table>
Now, my problem is, as you probably already figured out from the colspan="2", is that I want my third textbox to stretch all the way thorugh the two columns. In normal HTML is would naturally just add a width attribute. Is there a DataAnnotation like DataType.MultilineText that can change the width of the editors? Any other ideas?
UPDATE: If I change it to a TextBoxFor instead of EditorFor, I can actually add #Html.TextBoxFor(x => x.Random, new { style = "width: 500px;" }).
Only problem is, I have another textbox (lets say random4) and it somehow overrides my DataAnnotation MultilineText and makes it a plain 500px textbox. Guess ill have to digg into the CSS :(
You might find some of the answers to this question useful.
The good thing about templates is that if you don't like the way they work, you can simply drop-in your own implementation.
You can also try using CSS to specify the width for your control, based on it's ID.
Easiest solution is to just style the control in the css. For the random3 textbox ill use input[type="text"]{width:1px} and for the random4 multilinetext, ill use just use textarea{width:1px}
In the Property of the Model - in this case Random3 give it an annotation
public class ViewModelName
{
[DataType(DataType.MultilineText)]
public string Random3 { get; set; }
}
then when you can the Html.EditorFor(x => x.Random3) it will know it needs to be multiline

Why pass data context class through View() instead of ViewBag?

If I have a Controller, with an Index page:
#model IEnumerable<MvcMovie.Models.Movie>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<table>
<tr>
<th>
Title
</th>
....
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
....
</tr>
}
</table>
Whats the advantage of using #model IEnumerable<Movie>? Why couldn't I just define ViewBag.Movies in the Controller and use that in the index page?
The advantage of using #model IEnumerable<Movie> is the clarity of saying right upfront - this view is meant to display (an) IEnumerable<Movie>. It may use some additional information (which will be stored in the ViewBag), but IEnumerable<Movie> is what it's meant to display. That's conceptually the reason, which has a lot to do with the concept of the MVC design pattern. And of course there are the technical reasons as #Tridus said.
Strictly speaking, you could. But you're not going to get the benefit of Intellisense or a straightforward "this is the wrong type" error if you pass in something else to the View. You also won't get errors caught if you enable compiling your views at compile time (though that's off by default so it's less of a benefit).

MVCContrib Grid MVC 3 Razor .RowStart

Having trouble with the examples for the .RowStart method.
Comparing the 2 syntaxes: http://www.jeremyskinner.co.uk/2009/03/01/mvccontrib-grid-part-5-the-action-syntax/
In this
.RowStart(row => string.Format("<tr{0}>", row.IsAlternate ? "style=\"background-color:#CCDDCC\"" : ""))
row.IsAlternate throws an error as row isnt the GridRow, its actually your model (well the row's data item).
The second syntax (ActionSyntax) :
.RowStart((p,row) => {
if (row.IsAlternate) { %>
<tr style="background-color:#CCDDCC">
<% } else { %>
<tr>
<% }
}).Render(); %>
doesnt seem to translate to Razor
.RowStart((x, row) => string.Format("<tr class='{0}'>", row.IsAlternate ? "grid-row" : "grid-row-alt"))
Passes ok, but doesn't emit any row changes.
Any had this working?
I've just noticed some convention stuff that Html.Grid is putting in for you...
Given the Following
Html.Grid(Model.Results).Attributes(#class => "grid")
results in a table with class = "grid",
even rows with a class = "gridrow",
and odd rows with a class = "gridrow_alternate"
Not sure if this will help but one thing I've been doing lately is using:
.RowAttributes(x => new Dictionary<string, object> { { "class", x.value == myValue ? "highlight" : "" } })
This allows me to do a lot with css values for the attributes. then for supporting "Zebra striping" I use pure css (browser compatibility could be an issue here, but it's graceful just doesn't render on old browsers) looks something like
tr:nth-child(odd) {
background-color: #eee;}
Gives u great control over the table. More info on the selectors Sitepoint child selectors
Other wise you could try the google groups for mvccontib Jeremy is usually sharp of the mark to help out.
Hope this helped.

Resources