I'm making a test page for a project I'm working on and I've made desired progress so far but I'm trying to create TextBoxes from a model of List being passed to the view, however, the it seems to just ignore anything I have tried.
<form id="form1" runat="server">
<input id="btnsubmit" type="submit" name="Submit" onclick="Submit" />
<div id="divControls">
<% foreach (TextBox control in (this.Model as List<TextBox>))
{
Html.Label("lblLabel", control.Text);
Html.TextBox(control.ID, control.Text, new { id = control.ID, style = "width:50", name = "txt" + control.ID });
} %>
</div>
</form>
The List isn't null in the Controller on return. I don't have a clue at what the problem could be. If I throw a Something in the for loop it executes the appropriate number of times so why isn't it creating the labels or textboxes?
At first I thought it was that I'm adding them inside a form but I removed the form tags and it still didn't work so I really have no Idea, any help would be much appreciated. I'm relatively new to MVC.
[HttpPost]
public ActionResult Index(FormCollection form)
{
List<TextBox> controls = new List<TextBox>();
foreach (String Key in form.Keys)
{
if (Key.Contains("txt"))
{
TextBox textBox = new TextBox();
textBox.ID = Key;
textBox.Text = form.GetValues(Key)[0];
controls.Add(textBox);
}
}
return View("Index", controls);
}
Here's my Action encase it's helps.
Also encase I wasn't clear enough, I am adding controls to a form at runtime using JQuery and then that Action will be part of the submit so it must send the textboxes back to the view so they are not deleted.
Like I said I'm new to the whole MVC and Asynchronous thing so If there's a better way to do this, advice would be much appreciated.
Your not printing the html
<% foreach (TextBox control in (this.Model as List<TextBox>))
{%>
<%=Html.Label("lblLabel", control.Text)%>
<%=Html.TextBox(control.ID, control.Text, new { id = control.ID, style = "width:50", name = "txt" + control.ID })%>
<% } %>
Your code is looping through the controls and the Html.whaterever is returning a string but your not doing anything with it, just discarding it.
you also don't need to return a whole TextBox object. This is probably inefficient. Just return an struct or a class containing your data
Html.Label returns a string containing a <label> tag.
You're discarding that string.
You need to write it to the page by writing <%= Html.Whatever() %>.
Related
I'm creating a website with ASP.NET MVC5 and I'm using MaterializeCSS for the first time, which looks like a very exciting framework.
However, the checkboxes generated by CheckBoxFor helper become hidden !
When I write :
#Html.CheckBoxFor(m => m.IsAgreeTerms)
The generated HTML is :
<input name="IsAgreeTerms" type="hidden" value="false">
Why does Materialize change my type=checkbox into type=hidden ?
I tried to add type="checkbox" in the CheckboxFor helper, but it doesnt change anything. The only way is to modify in in my browser's console.
The only solution I found is this SO thread.
However, the accepted answer doesn't change anything for me.
The other answer works, but I think it's ugly to add some JS script to modify what Materialize modifies without my consent.
Is there any way to say "Hey, I ask for a type=checkbox, so just let my type=checkbox in the generated HTML" ?
Thank you
UPDATE :
My full ASP.NET MVC code is :
#Html.CheckBoxFor(m => m.IsAgreeTerms, new { #type = "checkbox" })
#Html.LabelFor(m => m.IsAgreeTerms, new { #class = "login-label" })
The full generated HTML is
<input data-val="true" data-val-required="Le champ IsAgreeTerms est requis." id="IsAgreeTerms" name="IsAgreeTerms" type="checkbox" value="true"
<input name="IsAgreeTerms" type="hidden" value="false">
<label class="login-label" for="IsAgreeTerms">IsAgreeTerms</label>
Here's a solution in the form of a html helper. It constructs a checkbox and label in the correct order:
public static IHtmlString CheckBoxWithLabelFor<TModel>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, bool>> expression,
string labelText,
object htmlAttributes = null
)
{
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
var checkBoxWithHidden = htmlHelper.CheckBoxFor(expression, htmlAttributes).ToHtmlString().Trim();
var pureCheckBox = checkBoxWithHidden.Substring(0, checkBoxWithHidden.IndexOf("<input", 1, StringComparison.Ordinal));
var labelHtml = htmlHelper.LabelFor(expression, labelText).ToHtmlString().Trim();
var result = pureCheckBox + Environment.NewLine + labelHtml + Environment.NewLine + $"<input type=\"hidden\" name=\"{ExpressionHelper.GetExpressionText(expression)}\" value=\"false\" />";
return new MvcHtmlString(result);
}
Is there other html generated by materialize.css? I think this happens because it is not possible apply a custom CSS to element input of type checkbox.
So, the checkbox becomes hidden and other html component represents visually the checkbox. Many components work like that.
UPDATE:
Why is Html checkbox generating an additional hidden input
OP here. Problem looks like more complex.
Actually, when using #Html.CheckBoxFor, MVC5 generates 3 fields, in that order :
Your input, with type="checkbox", binded to your model property
An hidden field (see the Claudio's link for an explaination)
Your label, generated by #Html.LabelFor
Problem is Materialize expects that in another order to work.
In your browser's console, just move the <label> element between the input and the hidden field, and everything works fine !
I found this very useful link, where, basically, it is said that the order of the generated fields by #Html.checkBoxFor will change ... In MVC6 !
As I'm working with MVC5, I use this very ugly solution in my _Layout:
$(":checkbox").each(function () {
$(this).nextAll("label").before($(this))
})
If anyone has a better idea, please feel free to post an elegant solution.
MVC3 VB.NET Application. I have multiple file upload boxes on a form in my mvc3 application. Request.Files shows 3 files when I put a break point in the below function and look at it.. Problem is other than coding a counter and using a select case I dont see a way to handle saving each file name to the database column it belongs to... Is there away to assign the variable on the fly I guess you could say So that the foreach loop would drop the file in the correct column.. Ie handoutFile1, handoutFile2 , handoutFile3, Etc.... As it stands the below will overwrite the filename in handoutfile1 every time the loop is gone through....I thought about throwing a counter in the loop and just put a select case on it to assign the db column based on the counter number. Seems like a cheap work around though.
For Each File As String In Request.Files
Dim hpf As HttpPostedFileBase = TryCast(Request.Files(File), HttpPostedFileBase)
If hpf.ContentLength = 0 Then
Continue For
End If
Dim savedfileName As String = Path.Combine(AppDomain.CurrentDomain.BaseDirectory) + "\CourseHandouts\" + hpf.FileName
hpf.SaveAs(savedfileName)
_filename = hpf.FileName
courses.handoutFile1 = _filename
Next
I dont know VB.Net but i will try to answer in C# code,
In your action you can define in parameter as
[HttpPost]
public ActionResult Index(IList<HttpPostedFileBase> postedFiles)
{
foreach (var file in postedFiles)
{
//
}
return View();
}
and in the view like the following,
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>TestModel</legend>
#Html.TextBox("postedFiles[0]",null, new { type = "file" })
#Html.TextBox("postedFiles[1]",null, new { type = "file" })
#Html.TextBox("postedFiles[2]",null, new { type = "file" })
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Your database design seems wrong. Instead of trying to save each individual file in a separate column, you could create another table that will contain all files as rows and which will have a foreign key to the first table to match the record. So you will basically have courses.handoutFiles which will be an array instead of having courses.handoutFile1, courses.handoutFile2, ...
If on the other hand for some reason you need to work with the initial schema and save into separate columns, you don't have much choice other than using ugly code.
I have a model that contains a collection, such as this:
class MyModel
{
public List<MySubModel> SubModels { get; set; }
}
In the view, I want to dynamically add/remove from this list using Javascript before submitting. Right now I have this:
$("#new-submodel").click(function () {
var i = $("#submodels").children().size();
var html = '<div>\
<label for="SubModels[' + i + '].SomeProperty">SomeProperty</label>\
<input name="SubModels[' + i + '].SomeProperty" type="textbox" />\
</div>'
$("#submodels").append(html);
});
This works, but it's ugly. And, if I want to show those labels/textboxes for the existing items, there's no clean way to do that either (without duplicating).
I feel like I should be able to use Razor helpers or something to do this. Any ideas? Help me stay DRY.
You approach may lead to unexpected errors if you when you are removing or adding the divs. For example you have 4 items, you remove the first item, then $('#submodels').children().size() will return 3, but your last inserted div has the name attribute value set SubModels[3].SomeProperty which results in a conflict. And if your posted values contain SubModels[1] but not SubModels[0] the default model binder will fail to bind the list (it will bind it as null). I had to learn this the hard way...
To eliminate the aforementioned problem (and your's) I suggest you do something like this:
$("#addBtn").click(function() {
var html = '<div class="submodel">\
<label>SomeProperty</label>\
<input type="textbox" />\
</div>'; // you can convert this to a html helper!
$("#submodels").append(html);
refreshNames(); // trigger after html is inserted
});
$(refreshNames); // trigger on document ready, so the submodels generated by the server get inserted!
function refreshNames() {
$("#submodels").find(".submodel").each(function(i) {
$(this).find("label").attr('for', 'SubModels[' + i + '].SomeProperty');
$(this).find("label").attr('input', 'SubModels[' + i + '].SomeProperty');
});
}
Then your view (or even better an EditorTemplate for the SubModel type) can also generate code like:
<div class="submodel">
#Html.LabelFor(x => x.SomeProperty);
#Html.EditorFor(x => x.SomeProperty);
</div>
It would also be possible to convert the code generation to a html helper class, and use it in the EditorTemplate and in the JavaScript code
I would recommend you going through the following blog post.
I'm just now starting to learn ASP.NET MVC. How would I go about creating a reusable tri-state checbox? In WebForms this would be a control, but I don't know the MVC equivalent.
Add a TriStateCheckBox (or TriStateCheckBoxFor if you use the strongly typed overloads) extension method to HtmlHelper and add the namespace of that extension method class to the namespaces section of your web.config.
As for the implementation, I'd recommend having at look at the InputExtensions source on codeplex and using that to create your own.
Limitations:
View Rendering - When rendering HTML content, there is no attribute you can possibly place on an <input type="checkbox" /> that will give it the property indeterminate.
At some point, you'll have to use JavaScript to grab the element and set the indeterminate property:
// vanilla js
document.getElementById("myChk").indeterminate = true;
// jQuery
$("#myCheck).prop("indeterminate", true);
Form Data - model binding will always be limited to what values are actually sent in the request, either from the url or the data payload (on a POST).
In this simplified example, both unchecked and indeterminate checkboxes are treated identically:
And you can confirm that for yourself in this Stack Snippet:
label {
display: block;
margin-bottom: 3px;
}
<form action="#" method="post">
<label >
<input type="checkbox" name="chkEmpty">
Checkbox
</label>
<label >
<input type="checkbox" name="chkChecked" checked>
Checkbox with Checked
</label>
<label >
<input type="checkbox" name="chkIndeterminate" id="chkIndeterminate">
<script> document.getElementById("chkIndeterminate").indeterminate = true; </script>
Checkbox with Indeterminate
</label>
<label >
<input name="RegularBool" type="checkbox" value="true">
<input name="RegularBool" type="hidden" value="false">
RegularBool
</label>
<input type="submit" value="submit"/>
</form>
Model Binding - Further, model binding will only occur on properties that are actually sent. This actually poses a problem even for regular checkboxes, since they won't post a value when unchecked. Value types do always have a default value, however, if that's the only property in your model, MVC won't new up an entire class if it doesn't see any properties.
ASP.NET solves this problem by emitting two inputs per checkbox:
Note: The hidden input guarantees that a 'false' value will be sent even when the checkbox is not checked. When the checkbox is checked, HTTP is allowed to submit multiple values with the same name, but ASP.NET MVC will only take the first instance, so it will return true like we'd expect.
Render Only Solution
We can render a checkbox for a nullable boolean, however this really only works to guarantee a bool by converting null → false when rendering. It is still difficult to share the indeterminate state across server and client. If you don't need to ever post back indeterminate, this is probably the cleanest / easiest implementation.
Roundtrip Solution
As there are serious limitations to using a HTML checkbox to capture and post all 3 visible states, let's separate out the view of the control (checkbox) with the tri-state values that we want to persist, and then keep them synchronized via JavsScript. Since we already need JS anyway, this isn't really increasing our dependency chain.
Start with an Enum that will hold our value:
/// <summary> Specifies the state of a control, such as a check box, that can be checked, unchecked, or set to an indeterminate state.</summary>
/// <remarks> Adapted from System.Windows.Forms.CheckState, but duplicated to remove dependency on Forms.dll</remarks>
public enum CheckState
{
Checked,
Indeterminate,
Unchecked
}
Then add the following property to your Model instead of a boolean:
public CheckState OpenTasks { get; set; }
Then create an EditorTemplate for the property that will render the actual property we want to persist inside of a hidden input PLUS a checkbox control that we'll use to update that property
Views/Shared/EditorTemplates/CheckState.cshtml:
#model CheckState
#Html.HiddenFor(model => model, new { #class = "tri-state-hidden" })
#Html.CheckBox(name: "",
isChecked: (Model == CheckState.Checked),
htmlAttributes: new { #class = "tri-state-box" })
Note: We're using the same hack as ASP.NET MVC to submit two fields with the same name, and placing the HiddenFor value that we want to persist first so it wins. This just makes it easy to traverse the DOM and find the corresponding value, but you could use different names to prevent any possible overlap.
Then, in your view, you can render both the property + checkbox using the editor template the same way you would have used a checkbox, since it renders both. So just add this to your view:
#Html.EditorFor(model => model.OpenTasks)
The finally piece is to keep them synchronized via JavaScript on load and whenever the checkbox changes like this:
// on load, set indeterminate
$(".tri-state-hidden").each(function() {
var isIndeterminate = this.value === "#CheckState.Indeterminate";
if (isIndeterminate) {
var $box = $(".tri-state-box[name='" + this.name + "'][type='checkbox']");
$box.prop("indeterminate", true);
}
});
// on change, keep synchronized
$(".tri-state-box").change(function () {
var newValue = this.indeterminate ? "#CheckState.Indeterminate"
: this.checked ? "#CheckState.Checked"
: "#CheckState.Unchecked";
var $hidden = $(".tri-state-hidden[name='" + this.name + "'][type='hidden']");
$hidden.val(newValue);
});
Then you can use however you'd like in your business model. For example, if you wanted to map to a nullable boolean, you could use the CheckState property as a backing value and expose/modify via getters/setters in a bool? like this:
public bool? OpenTasksBool
{
get
{
if (OpenTasks == CheckState.Indeterminate) return null;
return OpenTasks == CheckState.Checked;
}
set
{
switch (value)
{
case null: OpenTasks = CheckState.Indeterminate; break;
case true: OpenTasks = CheckState.Checked; break;
case false: OpenTasks = CheckState.Unchecked; break;
}
}
}
Alternative Solution
Also, depending on your domain model, you could just use Yes, No, ⁿ/ₐ radio buttons
ASP.NET MVC certainly doesn't provide such component, actually it simply relies on the standard elements available in HTML but you may want to check out this solution.
Why doesn't this display the date/time when rendered?
<asp:Label runat="server" ID="test" Text="<%= DateTime.Now.ToString() %>" ></asp:Label>
Is there anyway to make this work?
Asp.net server controls don't play well with the <%=, instead you can do:
<span><%= DateTime.Now.ToString() %></span>
Ps. you could alternatively set the label's text on the code-behind. It might work for your scenario to set it on the PreRenderComplete.
I'm not sure if you've got a code behind file, but if you really need to set the label's Text property in the .aspx markup you could add the following code to the page:
<script runat="server">
protected override void OnPreLoad(EventArgs e)
{
if (!Page.IsPostBack)
{
this.test.Text = DateTime.Now.ToString();
base.OnPreLoad(e);
}
}
</script>
This way you can maintain the label control's state on postback.
Put the inline code inside the label's tag as below,
< asp:Label ID="Lbl" runat="server" Text="">
<%= DateTime.Now.ToString() %>
< /asp:Label>
Well the asp tags are rendered. You will have to set the property at runtime. or just do the <%= DateTime.Now.ToString() %>.
The simplest way to make that work would be to use a data-binding expression in place of the code render block...
<asp:Label runat="server" ID="test" Text="<%# DateTime.Now.ToString() %>" ></asp:Label>
Now the Text property will be set whenever Page.DataBind() is called, so in your code-behind you'll want something like
protected override void OnPreRender(EventArgs e)
{
if (!Page.IsPostBack)
{
DataBind();
}
base.OnPreRender(e);
}
The real problem here though is I need to set the property of a WebControl with code on the markup page. The only way I've found to do this is put the whole control in a code block. Its not elegant or suggested but when all else fails this will work.
<%
var stringBuilder = new StringBuilder();
var stringWriter = new StringWriter(stringBuilder);
var htmlWriter = new HtmlTextWriter(stringWriter);
var label = new Label { Text = DateTime.Now.ToString() };
label.RenderControl(htmlWriter);
Response.Write(stringBuilder.ToString());
%>
But this won't work if you need the control to maintain state.
UPDATE:
After researching Kev's answer I did find an even better solution. I don't have a code behind (its an MVC page) but you can still reference a control on the page through a code block so my new solution is the following. Note - You have to place the code block first for this to work.
<%
lblTest.Text = DateTime.Now.ToString();
%>
<asp:label runat="server" ID="lblTest" />
Thanks for the inspiration Kev!