Inconsistent null attribute handling in ASP.NET MVC 4 - asp.net-mvc

I have been trying to handle optional HTML required and readonly attributes in ASP.NET MVC 4. For my surprise, I found out that null attributes in HTML helpers are rendered as empty strings while they are removed completely in Razor (desired behavior).
For example, this code:
#{ string disabled = null; string #readonly = null; }
#Html.TextBox("t1", "Value", new { disabled, #readonly })
<input type="text" name="t2" value="Value" disabled="#disabled" readonly="#(#readonly)" />
Renders:
<input disabled="" id="t1" name="Txt1" readonly="" type="text" value="Value" />
<input type="text" name="t2" value="Value" />
Basically what I want to know is:
What is the reason behind these two different behaviors?
Is there a way to get the same result using Html.TexBox without writing any custom code?
EDIT
This is not possible without writing a custom Html Helper, but there's a feature request for this on CodePlex.

The Html.TextBox() behavior comes from code in System.Web.Mvc.Html that transforms a RouteValueDictionary of attributes into actual HTML. (I believe that code is in TagBuilder)
The raw HTML tag behavior comes from a feature in the Razor v2 language parser that removes attributes in Razor markup that resolve to null at runtime.

Related

Parse Html string with Razor

I have an application which generates dynamic html code with asp.net core tag helpers like
<input type="hidden" asp-for="Id" />
<input asp-for="Name" />
<input asp-for="IsActive" />
I return this html code from the database into the controller, I need to parse it with Razor Engine of asp.net core and of course pass a model for the engine to get data from, How can I do that?
A possible way to solve this, is by creating a custom IFileProvider that access the database.
Here is a blog post that explains how, but it's a bit older:
https://www.mikesdotnetting.com/article/301/loading-asp-net-core-mvc-views-from-a-database-or-other-location
The current offical FileProvider docs are here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/file-providers?view=aspnetcore-2.2
This would simulate a virtual file-system with the content from the database, those views can then be used like any cshtml file (e.g. as partial view)
I ended up creating a custom file provider which get the views from the database and this link explains it
https://www.mikesdotnetting.com/article/301/loading-asp-net-core-mvc-views-from-a-database-or-other-location
Define a model
public class Model {
Id, Name...
}
and
#Html.Raw(your html template)

Tag helpers seem to interfere with traditional Razor syntax

Applying traditional Razor syntax to a textarea with a asp-for tag applied to it (and a RequiredAttribute applied in the view model) seems to interfere with each other.
#{ string disabled = "disabled"; }
<textarea asp-for="Motivation" class="form-control" rows="3" #disabled></textarea>
The disabled attribute is not applied.
<textarea
class="form-control" rows="3"
data-val="true" data-val-required="..."
id="Motivation" name="Motivation" placeholder="...">
</textarea>
I can understand something must be going wrong (or is unsupported) since asp-for needs to be rewritten to the matching attributes derived from the view model.
My main questions are:
Is this documented/expected behavior?
What is the 'ASP.NET' way of going about similar modifications to the HTML output?
I am only just starting out with ASP.NET, so am uncertain as to the first question: "Is this documented/expected behavior?" This is thus only a partial answer and I am looking for more information regarding the first question.
However, to answer the second question, one possibility seems to rely on (older?1) HTML helpers which offer more flexibility:
#{ string disabled = "disabled"; }
#Html.TextAreaFor(
m => m.Motivation,
new { #class = "form-control", rows = 3, disabled = disabled } )
The second parameters are additional attributes which are applied to the output of the textarea.
Furthermore, it should also be noted that in XHTML attribute minimization is forbidden.
In XHTML, attribute minimization is forbidden, and the disabled
attribute must be defined as <input disabled="disabled" />.
1 I can't seem to find official documentation on this.

MVC Using a custom indexer with a EditorFor

I have an EditorFor template that I call with an array of items (there may be a few) and I reference the indexes individually (not in a loop) so I can lay them out a certain way
#Html.EditorFor(x => Model.SomeViewModels[0], "SomeTemplate")
all works fine but ideally I want to reference the array by a nicer indexer
#Html.EditorFor(x => Model.SomeViewModels["Item to Find"], "SomeTemplate")
for better readability and some flexibility to cope if the index changes
so I was wondering if I could set up a custom indexer on the Model - like this
public ObjecToReturn this[string TextToFind]
{
get
{
return ObjectToReturn based on TextToFind
}
}
and it does work - and pulls the value through correctly however when I look at the Html the input fields all have names like name=[Item to Find].AnswerValue which I can see why but it messes up the post back
Just wondered if any clever person had worked out how I can send in Model.SomeViewModels["Item to Find"] to the EditorFor yet the underlying Html in the template reflects Model.SomeViewModels[0] (0 being the index of "Item to find" in the array) so the Postback works
Or maybe this is the wrong approach - I'm hoping I'm missing something straightforward.
Thanks in advance
By default the DefaultModelBinder binds collections where the indexers start at zero and are consecutive. You can make this work by adding a hidden input for a Index property (note Index is not a property of your model - its just a special value used for binding collections). For example
<input type="text" name="SomeViewModels[ABC]" value = "" />
<input type="hidden" name="SomeViewModels.Index" value="ABC" />
<input type="text" name="SomeViewModels[XYZ]" value = "" />
<input type="hidden" name="SomeViewModels.Index" value="XYZ" />
The addition of the hidden input where the value attribute matches the indexer value allows the DefaultModelBinder to correctly bind the collection

difference in input type=Checkbox, #HTML.CheckBox and #HTML.CheckBoxFor?

I m new to MVC and confused what is difference in <Input type="Checkbox">, #HTML.CheckBox and #HTML.CheckBoxFor.
Can you please guide why two helpers are provided for same thing ? In which situation which one should be used ?
Thanks
Edit:
Added Input type=checkbox
<Input type="Checkbox"> is Html markup for a checkbox and #Html.CheckBox & #HTML.CheckBoxFor are Html Helpers for Razor view engine..
suppose your viewmodel has a property Person.HadDinner, then usually for model binding to work properly you will have to name the checkbox Person.HadDinner and id as Person_HadDinner..
you can use #Html.CheckBox like
#HTML.CheckBox("Person.HadDinner", Model.Person.HadDinner)
but if you are using #HTML.CheckBoxFor, it will be strongly typed..
#HTML.CheckBoxFor(x => x.Person.HadDinner)
in both the cases, final output markup will be
<input type="checkbox" id="Person_HadDinner" name="Person.HadDinner">
The CheckboxFor (MSDN)
Returns a check box input element for each property in the object that
is represented by an expression.
This means a checkbox element is created for each property in the expression provided. Where as Checkbox (MSDN)
Returns a check box input element by using the specified HTML helper
and the name of the form field.
This creates a simple Checkbox element with the (optional) attributes provided.
Typically when referencing a property of an object (or the View Model) the most desired technique is to use CheckboxFor as the checkbox will be formatted correctly against your model.
Hope this helps.
EDIT: Response to OP Changes.
Both the CheckboxFor and Checkbox generate standard HTML output such as below.
#Html.CheckboxFor(m => m.SomeProperty)
<input type="checkbox" name="SomeProperty" id="SomeProperty" />
#Html.Checkbox("SomeProperty")
<input type="checkbox" name="SomeProperty" id="SomeProperty" />
The helper methods simply generate the HTML required to meet the expressions and attributes defined in the helpers.
Additionally, you dont have to use the helpers. You can write your HTML elements directly as needed.

performing searches with ASP.NET MVC

Ok, I've just started learning ASP.NET MVC after being an ASP.NET developer for awhile. I think my main problem I'm having is trying to "unlearn" ASP.NET when developing my MVC projects.
Here's my question: I have a page which has some input fields. These fields are parameters to a search I'm trying to run against a database. The user checks the boxes next to the types of items they want to see and then clicks "Search". Very simple stuff.
I'm having trouble wrapping my mind around how exactly to "postback" to the page to display the results. Is it better to use jQuery and serialize the form? Do I use my Entity Framework models I've created? What's the best way to go about
I'm really excited about MVC and the control it gives me, but I need to get over these initial obstacles if I ever want to "sell" it to my boss as the way to develop all of our web apps. Thanks for reading!
If you haven't already, consider taking a look at the NerdDinner Tutorial featured in Professional ASP.NET MVC 1.0 by Rob Conery, Scott Hanselman, Phil Haack, and Scott Guthrie. It contains a great demonstration of many of the features of ASP.NET MVC including performing a search and returning the data both through a full page post and also asynchronously using JSON.
If your inputs are inside html form element (different story if javascript is involved) - you can use default model binding (it binds route values and querystring parameters too).
<form ...>
<input type="text" name="query" />
<input type="submit" .../>
</form>
on submit it will automagically bind form values (by name) to action parameters:
public ActionResult PerformSearch(string query)
{
//whatever
}
In your case - i suspect you got inputs as checkboxes. Something like this should work:
<form...>
<input type="checkbox" name="p" value="value1" />
<input type="checkbox" name="p" value="value2" />
<input type="checkbox" name="p" value="value3" />
<input type="checkbox" name="p" value="value4" />
<input type="checkbox" name="p" value="value5" />
</form>
public ActionResult PerformSearch(string[] p)
{
//whatever
}
Only - if (form method == "GET"), URL won't look nicely. :)
To show results, make a model for your view in action and just show it through view:
public ActionResult PerformSearch(string[] p)
{
var model = _searchService(p);
return View("Results", model);
}
Views/Results.aspx
<% foreach(var bar in Model){ %>
<%= bar.Name %>
<%}%>
P.s. When considering AJAX calls, always remember that you are loosing ability to show URL + search engines don't understand JS.

Resources