Binding checkboxes to array with Knockout.js (MVC Razor) - asp.net-mvc

I have a Knockout.js view model that looks like this:
"UserName":null,
"FirstName":null,
"LastName":null,
"Countries":{
"arrCountries":[]
}
And a set of country checkboxes that all must be rendered with the same data-bind value (I'm using this custom CheckBoxListFor helper, which can apply custom HTML to each checkbox, but it's the same custom HTML for each checkbox. It takes a property that lists every available country from my razor view model to create the checkboxes, and then renders some as marked based on another razor view model property).
The checkbox values are integers (1, 2, 3, etc) and it's those integers that I'd like to throw into the arrCountries property of the view model.
Here's how a checkbox is rendered as it stands at the moment:
<input checked="checked" data_bind="checked: ???" id="Countries.arrCountries107" name="Countries.arrCountries" type="checkbox" value="1"></input>
I've proven the concept of using viewModel.Countries.arrCountries.push(1); to update the view model (followed by an alert that gives me a count of the elements in arrCountries to prove that the push worked), but I can't seem to get the data-bind HTML property on the checkboxes to wire up so that they check and uncheck as I send push and remove commands.
Any help much appreciated!

Make the checked data attribute point to the selected countries. If the checkbox's value is in the array of checkboxes, then it will be checked. It won't otherwise.
<input checked="checked" data-bind="checked: Countries" type="checkbox" value="3"></input>
Take a look at this fiddle I put up.
http://jsfiddle.net/u8xP9/3/

Related

ViewModel binding without SelectList in ASP.NET MVC

In my ASP.NET Core application, I have an ASP.NET MVC View which displays a list of products. (I guess this applies to other versions of MVC as well).
When I select an item from this list and submit the page, I want the selected product ID to be bound to a ViewModel property in the action method of the controller. I am using POST for this.
I do NOT want to use SelectList for rendering the list of products due to some formatting issues. Therefore I am looping over the list in my view.
How can I bind the selected product with the ProductId property of the inbound ViewModel object?
It's unclear what you mean by "select an item from this list and submit the page". Are you picking an item, potentially filling out more fields, and then submitting the whole shebang, or does picking an item constitute submitting the form. Depending on the situation there's a few approaches:
If picking an item submits the form, then you can simply make the "Select" button a submit button and give it a name and value. For example:
Item 1 <button type="submit" name="ProductId" value="1">Select</button>
Whichever "Select" button is clicked, then, will have its value posted as ProductId.
If you need to pick an item while remaining on the page to fill out other fields, then you can use radio buttons as an alternative to a select list. This is similar to approach above, except you will not instantly post the form. Your inputs would look similar though:
<label>
<input type="radio" name="ProductId" value="1"> Item 1
</label>
<label>
<input type="radio" name="ProductId" value="2"> Item 2
</label>
...
Finally, if you need to not instantly submit and you do not want to use radio buttons either, then your only real option is using JavaScript to set a hidden input. You would bind to the click event of your "Select" button or whatever and then set a hidden input with name="ProductId" to the appropriate value. Then, when you finally submit the form, the value of the hidden input will be posted as ProductId.

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.

Dynamic Form in ASP.NET MVC3 Razor

I have a small problem.
I want to have dropdown list with some objects. After clicking on one I want to add it to list with textfield for naming it. I don't want to limit quantity of this fields. I want to receive in controller ID (stored in dropdown list) and name (given by user) for each selected item. How can I do it?
I was thinking about storing it in some fields as a text, and parsing in cotroller but I think it's not elegant.
EDIT.
Ok, Thansk for your help, but it's not working for me correctly.
I generate html like this:
<input type="hidden" value="96" name="Inputs[0].Key">
<input type="text" name="Inputs[0].Value">
In my controller I'm receiving this dictionary. The problem is that quantity of elements is correct, but all values are null. What is wrong here?
The best way to go about this is by using array-style model binding.
So, for each element you wish to name you create a hidden field to store the drop down value plus a text field to store the user-given name. You name them as follows:
<input type="hidden" name="element[0].Key" /><input type="text" name="name[0].Value" />
increasing the index value each time. This is easily achieved with a bit of JavaScript. You then create an action method which takes a KeyValuePair<string, string>[] as a parameter. You will then be able to parse through your values no problem with a loop or LINQ expression.
Use IEnumerable<KeyPairValue<string,string>> MySelectedItem = new List<KeyPairValue<string,string>>(); on model, and when adding it to the list, name it like an array:
MySelectedItem[1].Key, MySelectedItem[1].Value, MySelectedItem[2].Key...
(I haven't tested this, but it should work)
Edit: check out this blog post with better explanation on how to do it: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

How to bind list of dynamic objects to the Model

I have a strongly typed view where one of the properties of the model that is passed in is a list of objects. I am handling dynamic binding of inputs to this list by using the BeginCollectionItem helper which works fine. However, I also want to initially have a group of inputs visible on the view that will bind to the 'first' object in the list.
I've tried just copying the inputs that BeginCollectionItem generates but that doesn't seem to work.
It generates inputs with name attributes like
invoiceItems[ef43a8f2-c6b7-4791-bf7f-6764c8e3fc9b].Description
invoiceItems[ef43a8f2-c6b7-4791-bf7f-6764c8e3fc9b].Cost
so I manually put 2 inputs on the view with the name attributes
invoiceItems[firstOne].Description
invoiceItems[firstOne].Cost
but it didn't show up in the model on the controller after submitting the form.
I can't just insert one the BeginCollectionItem way when the view loads because the initial item has to be displayed differently.
Not exactly answer to your question, but I hope it will solve your problem.
Instead of changing the name in the view, you may try changing the model. Add a boolean property IsFirst. I believe you know in the controller(or wherever the model is being instantiated) which one is first. Set the first items IsFirst to true.
I figured it out. I was on the right track with manually putting in the stuff that BeginCollectionItem did, I was just missing a part. You need another hidden input that tells the binding what the index you're using is.
So the following works perfectly
<input type="hidden" name="invoiceItems.index" autocomplete="off" value="firstOne" />
<input type="text" placeholder = "ex. Labour" id="invoiceItems_firstOne__Description" name="invoiceItems[firstOne].Description" />
<input type="text" placeholder = "ex. $15" id="invoiceItems_firstOne__Cost" name="invoiceItems[firstOne].Cost" />
I just added the top hidden input to what I had before and it's all good.

How to mimic MVC's checkbox -> bool model binding?

I've got an editor template which renders out a checkbox:
#Html.CheckBoxFor(model => model.Follow)
Which renders something like this:
<input checked="checked" data-val="true" data-val-required="The Follow field is required." id="Follow" name="Follow" type="checkbox" value="true" />
<input name="Follow" type="hidden" value="false" />
AFAIK the hidden field is something to do with catering when an unchecked box isn't sent to the server or something.
Anyway, if i take a look at the Request.Form["Follow"] when the checkbox is checked, i see a value of "true,false".
How do i coerce a bool from this value? Do i simply ignore the second field? (e.g the hidden field).
I'm doing this is a base controller (protected method, invoked from child controller), so i don't have a strongly-typed view model, only the raw Request object.
Can anyone help? Or alternatively, if someone could point me to where in the MVC source code this happens, i could take a look myself, but not sure where to start looking.
You are correct the hidden field is just so the form will be submitted to the server. Because if the form had just checkboxes that are not checked then nothing will be submitted and the server would not know to set them to false.
You only require 1 hidden field per form, you do not need one per checkbox. But if your making your own control it is hard to tell if a hidden textbox is already on the field or not. If you know you are always going to have a textbox or select list etc somewhere else on your forms you do not need a hidden textbox at all
You can rename your hidden textbox to anything name it "dummy" or something different to the checkbox name so Request.Form["Follow"]; will only return the value of the check box not need to split. You never need to check the value of the "hidden textbox".
On a side note you shouldn't be using Request.Form["Follow"] you Action method should have a parameter like this instead "bool? follow"
MVC helper renders checkbox input control with two input fields, the checkbox and the hidden, because the browser do not send a value for checkbox input field if the checkbox is not selected. If you do not use auto mapping, you need to parse the input value that you receve from your form.
Use this simple rule to detect the checkbox:
var rawFollow = Request.Form["Follow"];
if (rawFollow.Contains("true"))
{
// do something
}
As far as i know, the extra hidden field is because if the checkbox is NOT checked, that input will not be submitted with the form and therefore we need the hidden field with the value of false.
So the only solution is can think of is this:
var rawFollow = Request.Form["Follow"];
var rawFollows = rawFollow.Split(',');
if (rawFollows.Count() > 1)
{
rawFollow = rawFollows[0];
}
But this seems hacky (and what about the order of the elements on the page, what if for some reason the hidden field was FIRST, then it would always evaluate to false), which is why i'm wondering how the MVC source does this.

Resources