Why are my single select lists converting to multi select lists on model binding? - asp.net-core

I'm trying to bind multiple single select lists to one list of strings in my view model I have the property: public List<string> Items {get; set;} and in my view I have 3 select lists with single select option, but when I try to model bind my select list to Items property using asp-for="#Model.Items" all my single select lists converts to multi select lists. Why?
My view:
<form asp-controller="Admin" asp-action="Index" method="post">
<div class="col-12">
<div class="row">
<div class="form-group col-4">
<div class="d-inline-block">
<label>Order By Price:</label>
</div>
<div class="d-inline-block">
<select class="form-control" asp-for="#Model.Items" style="width:150px">
<option value="Ascending">Ascending</option>
<option value="Descending">Descending</option>
</select>
</div>
</div>
<div class="form-group col-4">
<div class="d-inline-block">
<label>Order By Name:</label>
</div>
<div class="d-inline-block">
<select class="form-control" asp-for="#Model.Items" style="width:150px">
<option value="A-Z">A-Z</option>
<option value="Z-A">Z-A</option>
</select>
</div>
</div>
<div class="form-group">
<div class="d-inline-block">
<button type="submit" class="btn btn-secondary">Confirm</button>
</div>
</div>
</div>
</div>
</form>
Controller:
[HttpPost]
public IActionResult Index(MangeProductsViewModel vm)
{
return View(new MangeProductsViewModel
{
Products = productRepository.Products.ToList().SortProducts(vm)
});
}
My viewmodel
public IEnumerable<Product> Products { get; set; }
public List<string> Items { get; set; }
And when I select items from lists it adds them correctly the problem is dat user can select multiple options from one list

That's because asp-for invokes a tag helper, and for a prop of type List<string>, the HTML that is generated is going to be a select multiple. However, this wouldn't work for you anyways as it would give all the selects the same name. As a result, they'll simply overwrite each other, not add to each other.
For this, you're going to have to handle things manually via:
<select name="Items[]">
#foreach (var option in Model.SelectList1Options)
{
<option value="#option.Value">#option.Text</option>
}
</select>
For each drop down.

Try this!
In your HttpGet method create a new list with length 3 and assign it to Items property of model and pass the model to view.
And in view write asp-for as follows
asp-for=“#Model.Items[0]”
asp-for=“#Model.Items[1]”
asp-for=“#Model.Items[2]”

The reason is that you have used asp-for="#Model.Items", sicne your item is type of List<string>, so the dropdown list is set as multiple select.
Just use name="Items" instead:
<form asp-controller="Managers" asp-action="SelectTest" method="post">
<div class="col-12">
<div class="row">
<div class="form-group col-4">
<div class="d-inline-block">
<label>Order By Price:</label>
</div>
<div class="d-inline-block">
<select class="form-control" name="Items" style="width:150px">
<option value="Ascending">Ascending</option>
<option value="Descending">Descending</option>
</select>
</div>
</div>
<div class="form-group col-4">
<div class="d-inline-block">
<label>Order By Name:</label>
</div>
<div class="d-inline-block">
<select class="form-control" name="Items" style="width:150px">
<option value="A-Z">A-Z</option>
<option value="Z-A">Z-A</option>
</select>
</div>
</div>
<div class="form-group">
<div class="d-inline-block">
<button type="submit" class="btn btn-secondary">Confirm</button>
</div>
</div>
</div>
</div>
</form>
Result:

Related

drop down select field stays null Livewire

I'm trying out Livewire for the first time. The drop-down is populated from the database. Similar codes works for text fields, but fails with drop-down select fields.
Edit: The form is inside a bootstrap modal
Code is as below: The livewire component (leads-form.blade)
<div class="modal-content">
<form wire:submit.prevent="submit">
#csrf
<div class="input-field col s12">
<label for="role">Client</label>
<select class="error validate" wire:ignore id="client_id" wire:model="client_id">
<option disabled value=" ">Client</option>
#foreach($clients as $client)
<option value="{{$client->id}}"> {{$client->clientname}}</option>
#endforeach
</select>
</div>
<div class="input-field col m12 s12">
<input id="contactperson" type="text" name="contactperson" wire:model="contactperson" />
#error('contactperson') <span class="error"><small>{{ $message }}</small></span> #enderror
<label for="contactperson">Contact Person</label>
</div>
<div class="row">
<div class="row">
<div class="input-field col s12">
<button class="btn cyan waves-effect waves-light right" type="submit">Add
<i class="material-icons right">send</i>
</button>
</div>
</div>
</div>
</form>
</div>
Next Livewire leads class is as a below:
class LeadsForm extends Component
{
public $contactperson;
public $client_id;
public function submit()
{
Lead::create([
'client_id' => $this->client_id,
'contactperson' => $this->contactperson,
]);
Alert::toast('Client created successfully', 'success');
return $this->redirectRoute('leads.index');
}
public function render()
{
$clients = Client::all();
return view('livewire.leads-form',[
'clients'=>$clients,
]);
}
}
I'm using the laravel debugbar, and it shows the array client_id field as null.
Remove the wire:ignore in the select element, that avoid produce any Livewire event that can be wrapped to backend
<select class="error validate" wire:ignore id="client_id" wire:model="client_id">
<option disabled value=" ">Client</option>
#foreach($clients as $client)
<option value="{{$client->id}}"> {{$client->clientname}}</option>
#endforeach
</select>

How to get index of changed array under $watch in vuejs

I am using an array of objects
({"code":"id1","color":"red","description":"eg1"})
where each object contains input fields.User can dynamically add and remove those objects.
Now I want that when he enters code rest of the field should be filled automatically.
For that I have developed an algorithm which will take the code and will give me the color and description but for that I need the POSITION of the object which is changed so that I can update the array on that index itself.
Below is my share of code:
<div v-for="(x,i) in fabric_arr">
<!--<pre>{{x}}</pre>-->
<div class="row">
<div class="col-md-4">
<span>
<input type=”text” list="idOfDatalist" class="form-control border-input" placeholder="Fabric Code" v-model="x._id">
<datalist id="idOfDatalist">
<option v-for="y in all_fabrics">{{y._id}}</option>
</datalist>
</span>
</div>
<div class="col-md-4">
<span>
<input type=”text” class="form-control border-input" placeholder="Fabric Color" v-model="x.color"></span>
</div>
<div class="col-md-4">
<span>
<input type=”text” class="form-control border-input" placeholder="Fabric Description" v-model="x.description"></span>
</div>
</div>
</div>
And my controller:
watch: {
fabric_arr: {
handler : function (val) {
console.log("val");
console.log(val);
//val.color="red always";
this.fabric_arr[0].color="fghjkl"
},
deep: true
}
}
For all those who are looking for the answer,use v-bind:change() to trigger the changes and manipulate index there.
Below is the code:
<input type=”text” list="idOfDatalist" class="form-control border-input" placeholder="Fabric Code" v-model="x._id" v-bind:change="inpChangedForCode(x)">
<datalist id="idOfDatalist">
<option v-for="y in all_fabrics">{{y._id}}</option>
</datalist>

How can I use a Knockout viewmodel inside a server loop?

I want to use Html helpers and Knockout to create a seamless transition of data from server to client, and then back to server again on postback.
My ViewModel has a few properties and then an array of items. I need to know how I can iterate over my array in ASP.NET Razor while assigning my knockout bindings to individual elements in the array.
Here's what I have so far:
#for (int i = 0; i < Model.Fields.Count; i++)
{
<div name="customFormField" data-bind="with: Fields[#i]">
<div class="form-group">
#Html.LabelFor(m => m.Fields[i].SqlDataType)
#Html.ListBoxFor(m => m.Fields[i].SqlDataType, new SelectList(selectDataTypeOptions), new { #class = "form-control", data_bind = "value: SqlDataType" })
</div>
</div>
}
My ko javascript:
<script>
function viewModel() {
this.addField = function() {
alert("wut");
}
}
$(function (){
var jsonModel = #Html.Raw(Json.Encode(Model));
var mvcModel = ko.mapping.fromJS(jsonModel);
var customFormTemplateViewModel = new viewModel();
var g = ko.mapping.fromJS(customFormTemplateViewModel, mvcModel);
ko.applyBindings(g);
});
</script>
When I browse the page, the only elements rendered are the <div name="customFormField" data-bind="with: Fields[x]"></div> elements, neither the form-group div inside it nor the label/listbox are created on the page.
There are no binding errors from KnockoutJS.
If I remove the data-bind="with: Fields[#i]", the other elements render properly, so it must be something about trying to bind to an element in an array that's making this go sideways, but I haven't been able to figure out what.
Edit: Here's the Html that is output from this:
It's interesting because if I inspect the DOM with chrome's inspector, it doesn't show anything inside each customFormField div, but in the source it does have the elements inside. It's outputting the System.Web.Mvc.SelectListItem in each item, but I think I just made my SelectList wrong.
<div name="customFormField" data-bind="with: Fields[0]">
<div class="form-group">
<label for="Fields_0__SqlDataType">SqlDataType</label>
<select class="form-control" data-bind="text: SqlDataType" id="Fields_0__SqlDataType" multiple="multiple" name="Fields[0].SqlDataType"><option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
</select>
</div>
</div>
<div name="customFormField" data-bind="with: Fields[1]">
<div class="form-group">
<label for="Fields_1__SqlDataType">SqlDataType</label>
<select class="form-control" data-bind="text: SqlDataType" id="Fields_1__SqlDataType" multiple="multiple" name="Fields[1].SqlDataType"><option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
</select>
</div>
</div>
<div name="customFormField" data-bind="with: Fields[2]">
<div class="form-group">
<label for="Fields_2__SqlDataType">SqlDataType</label>
<select class="form-control" data-bind="text: SqlDataType" id="Fields_2__SqlDataType" multiple="multiple" name="Fields[2].SqlDataType"><option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
</select>
</div>
</div>
<div name="customFormField" data-bind="with: Fields[3]">
<div class="form-group">
<label for="Fields_3__SqlDataType">SqlDataType</label>
<select class="form-control" data-bind="text: SqlDataType" id="Fields_3__SqlDataType" multiple="multiple" name="Fields[3].SqlDataType"><option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
</select>
</div>
</div>
<div name="customFormField" data-bind="with: Fields[4]">
<div class="form-group">
<label for="Fields_4__SqlDataType">SqlDataType</label>
<select class="form-control" data-bind="text: SqlDataType" id="Fields_4__SqlDataType" multiple="multiple" name="Fields[4].SqlDataType"><option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
<option>System.Web.Mvc.SelectListItem</option>
</select>
</div>
</div>
Edit 2: This example is showing when I tried the text binding, but I've tried it both ways.
Edit 3: I copied the output Html into another place on the page, and it didn't render that either, until I removed the data-bind="with: Fields[n]" from the containing div, so the problem seems to be there. However, if I remove that from the parent in the actual file, I get a binding error from data-bind="value: SqlDataType"

Bootstrap - Buttons inline with input with label above

I have code like below. How to make buttons in-line with input ? Now buttons are inline with adjacent column and looks ugly. Live demo here
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label for="x" class="control-label">Function</label>
<input type="text" id="x" class="form-control" placeholder="x"/>
</div>
</div>
<div class="col-md-1">
Get
</div>
<div class="col-md-1">
Stat
</div>
</div>
You can use an input group (though bootstrap say you shouldn't use input groups with selects because it doesn't always behave)
http://getbootstrap.com/components/#input-groups-buttons
edit: for the label above the input group:
<div class="container" style="width: 600px;">
<form id="form" role="form">
<div class="row">
<label for="x" class="control-label">Function</label>
<div class="input-group">
<select id="x" class="form-control" placeholder="x">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
<span class="input-group-btn">
Get
Stat
</span>
</div>
</div>
</form>
</div>
or for the label as part of the input group:
<div class="container" style="width: 600px;">
<form id="form" role="form">
<div class="row">
<div class="input-group">
<span class="input-group-addon"><label for="x" class="control-label">Function</label></span>
<select id="x" class="form-control" placeholder="x">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
<span class="input-group-btn">
Get
Stat
</span>
</div>
</div>
</form>
</div>
I added an update to your fiddle:
input above group: http://jsfiddle.net/ced7k0hq/11/
input as part of group: http://jsfiddle.net/ced7k0hq/12/
From the bootstrap docs: http://getbootstrap.com/components/#input-groups
Textual <input>s only
Avoid using <select> elements here as they cannot be fully styled in WebKit browsers.
Avoid using <textarea> elements here as their rows attribute will not be respected in some cases.
thanks #ovitinho for comment about joining the two anchors inside a single input-group-btn :D

Two inputs same form column taking all the cell width Bootstrap 3

I'm trying to get hands over Bootstrap 3 and I'm kindof struggling with the new classes for the grid. Previous versions of Bootstrap let you set the width of a input by adding the span-* class so if you wanted to put two inputs on the same form column, adding span-2 and span-10 did the job. I'm trying to do this with bootstrap 3 but I don't get the same result as you can see in this fiddle, I want to set the select and the input on the same col. I want the labels at top of the inputs so adding the inline class to the form don't work.
What I'm missing here?
Cheers and thanks in advance
http://jsbin.com/esewOyo/1/
Most people can't believe how many classes are involved but form-control is always 100% so each one must go inside a col-- class and if you want to put elements on the same row, in this situation, then you'd use a nested row with columns as follows:
<div class="container">
<form role="form">
<div class="row my-row">
<div class="col-sm-4">
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label>C-Band</label>
<select class="form-control">
<option value="C15+">C15+</option>
<option value="C12-14">C12-14</option>
<option>
</select>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label> </label>
<input type="text" class="form-control">
</div>
</div>
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label>C-Band</label>
<select class="form-control">
<option value="C15+">C15+</option>
<option value="C12-14">C12-14</option>
<option>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label>C-Band</label>
<select class="form-control">
<option value="C15+">C15+</option>
<option value="C12-14">C12-14</option>
<option>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label>C-Band</label>
<select class="form-control">
<option value="C15+">C15+</option>
<option value="C12-14">C12-14</option>
<option>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label>C-Band</label>
<select class="form-control">
<option value="C15+">C15+</option>
<option value="C12-14">C12-14</option>
<option>
</select>
</div>
</div>
</div>
</form>
</div>
Because the gutter is too wide for my taste:
.row.my-row, .row.my-row .row {
margin-left:-1%;
margin-right:-1%
}
.row.my-row [class*="col-"] {
padding-left: 1%;
padding-right: 1%;
}
.row.my-row .row [class*="col-"] {
padding-left: 1%;
padding-right: 1%;
}