I have an ASP.NET MVC application. I am having multiple drop-down list in my page (HTML SELECT), I have to disable them, as user goes on selected them one by one. When the user posts it back to the controller, I am getting null as the function (action method) paramters. I searched and found that HTML does not send value of disabled fields in the form data. Replacing disabled attribute with readonly would not work as it would render drop-down working.
I am generating the dropdowns dynamically using javascript as user goes on. So there isn't a single dropdown, but as many as user wants.
Can someone please tell me how should I get the values ?
One possibility is to make the dropdown list disabled="disabled" and include a hidden field with the same name and value which will allow to send this value to the server:
#Html.DropDownListFor(x => x.FooId, Model.Foos, new { disabled = "disabled" })
#Html.HiddenFor(x => x.FooId)
If you have to disabled the dropdown dynamically with javascript then simply assign the currently selected value of the dropdown to the hidden field just after disabling it.
This is the default behavior of disabled controls. I suggest you to add a hidden field and set the value of your DropDownList in this hidden field and work with this.
Something like:
//just to create a interface for the user
#Html.DropDownList("categoryDump", (SeectList)ViewBag.Categories, new { disabled = "disabled" });
// it will be send to the post action
#Html.HiddenFor(x => x.CategoryID)
You could also create your own DropDownListFor overload which accepts a bool disabled parameter and does the heavy lifting for you so your view isn't cluttered with if disablethisfield then ....
Something among these lines could do:
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, bool disabled)
{
if (disabled)
return MvcHtmlString.Create(htmlHelper.HiddenFor(expression).ToString() + htmlHelper.DropDownListFor(expression, selectList, new { disabled="disabled" }).ToString());
else
return htmlHelper.DropDownListFor(expression, selectList);
}
There are 6 overloads for DropDownListFor alone so it's a lot of monkeycoding but it pays off in the end imho.
Create a hidden field with a specified Id and set it before disabling the drop-down-list.
In MVC,
#Html.DropDownListFor(x => x.FooId, Model.Foos)
#Html.HiddenFor(x => x.FooId, new { #id = "hdnFooId" })
In JQuery,
function handleDropDownListFooChange(){
// Get the selected value from drop-down-list before disabling it.
var selectedFooId = $('#FooId').val();
$('#FooId').prop("disabled", "disabled");
$("#hdnFooId").val(selectedFooId);
// Load any data the depends on selected FooId using `selectedFooId` variable.
}
The selected value will automatically be binded to Model.FooId.
before submit call $('#FooId').removeAttr('disabled')
Related
I have the following Razor markup:
#Html.DropDownListFor(x => Model.WorkTypeId, new SelectList(Model.WorkTypeList, "Id", "Name", Model.WorkTypeId), " - please select - ", new { style = "background-color: yellow;"})
#Html.DropDownListFor(x => Model.PhaseGroupId, new SelectList(Model.PhaseGroupList, "Id", "Name", Model.PhaseGroupId), " - please select - ", new { style = "background-color: yellow;"})
Then I load the form these reside on using a jQuery $.get call, and assign change handlers to both dropdowns in the success function of the call:
function(data) {
$("#formContainer").html(data);
$("#WorkTypeId").change(function () {
lookupMatrixValues($("#WorkTypeId").val(), $("#PhaseGroupId").val());
});
$("#PhaseGroupId").change(function () {
lookupMatrixValues($("#WorkTypeId").val(), $("#PhaseGroupId").val());
});
})
When I select an item in the WorkTypeId dropdown, the change event does not fire, while if I select a PhaseGroupId item, its event does fire.
Also, when I POST the form, no matter what value is selected for a worktype, the value of the model property WorkTypeId is always zero, as if the select itself doesn't detect a change event.
If I look at what is rendered for the DropDownFor markup, I see the two selects are rendered slightly differently:
<select id="WorkTypeId" name="WorkTypeId" style="background-color: yellow;">
...
<select data-val="true" data-val-number="The field PhaseGroupId must be a number." id="PhaseGroupId" name="PhaseGroupId" style="background-color: yellow;">
I am curious as to why only the PhaseGroupId select has the data-val and data-val-number attributes while the WorkTypeId select does not have these attributes. The model properties are exactly the same:
public int? WorkTypeId { get; set; }
public int? PhaseGroupId { get; set; }
Why is the WorkTypeId select rendered differently and why does its bound model property never reflect what is selected. No matter what is selected, $("#WorkTypeId").val() is always zero.
You have a strange lambda syntax. IMHO should be
#Html.DropDownListFor(m=> m.WorkTypeId, ... //or model=>model.WorkTypeId
#Html.DropDownListFor(m => m.PhaseGroupId ...
and check if you have another WorkTypeId somewhere in your view. Javascript binds the first id it meets.
I've added a new field to the Taxon entity, and have the new field showing up on the form on the edit Taxon page in the admin panel. My problem is that I can't get my new field to save to the Taxon. I receive a success message, but only existing Taxon fields save, while my new field clears. Here's my code (note I'm making changes to Sylius core rather than extending, as proof of concept. Once I have this working, I'll extend properly):
src/Sylius/Bundle/TaxonomyBundle/Form/Type/TaxonType.php:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('translations', 'sylius_translations', [
'type' => 'sylius_taxon_translation',
'label' => 'sylius.form.taxon.name',
])
->add('my_new_field', 'text', [
'label' => 'My New Field',
'required' => false
])
->addEventSubscriber(new AddCodeFormSubscriber())
->addEventSubscriber(new BuildTaxonFormSubscriber($builder->getFormFactory())
);
}
src/Sylius/Component/Core/Model/Taxon.php:
protected $my_new_field;
and
public function getMyNewField() {
return $this->my_new_field;
}
public function setMyNewField($myNewField) {
$this->my_new_field = $myNewField;
}
After updating the Taxon model, I ran doctrine:diff and then ran the migration to add my new field to the model. I can confirm, the field was added to the database.
The field also shows up on Taxons now, on the edit screen. I can input text into the field and it posts correctly, but does not ever save to the taxon.
I would think I'm missing some controller logic, but it seems that taxons go through the standard ResourceController's updateAction() and I haven't been able to figure out how to make this aware of the new field which needs to be saved.
You're probably missing ORM mapping in Taxon.orm.xml file.
Remember:
if you've added your properties on Sylius\Component\Taxonomy\Model\Taxon you have to update src/Sylius/Bundle/TaxonomyBundle/Resources/config/doctrine/model/Taxon.orm.xml.
if you've added your properties on Sylius\Component\Core\Model\Taxon you have to update src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/Taxon.orm.xml
It's also probably better to extend Taxon model from Core component.
Here is a basic add action:
public function add()
{
$article = $this->Articles->newEntity();
if ($this->request->is('post')) {
$article = $this->Articles->patchEntity($article, $this->request->data);
if ($this->Articles->save($article)) {
$this->Flash->success('Success.');
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('Fail.');
}
}
$this->set(compact('article'));
}
If a malicious user injects at form a field with name id and set the value of this field to 2. Since the user do that the id value will be in $this->request->data so at $this->Articles->patchEntity($article, $this->request->data) this id will be patched and at $this->Articles->save($article) the record 2 will be updated instead of create a new record??
Depends.
Entity::$_accessible
If you baked your models, then this shouldn't happen, as the primary key field will not be included in the entities _accessible property, which defines the fields that can be mass assigned when creating/patching entities. (this behavior changed lately)
If you baked your models, then this shouldn't happen, as the primary key field(s) will be set to be non-assignable in the entities _accessible property, which means that these the fields cannot be set via mass assignment when creating/patching entities.
If you didn't baked your models and haven't defined the _accessible property, or added the primary key field to it, then yes, in case the posted data makes it to the patching mechanism, then that is what will happen, you'll be left with an UPDATE instead of an INSERT.
The Security component
The Security component will prevent form tampering, and reject requests with modified forms. If you'd use it, then the form data wouldn't make it to the add() method in the first place.
There's also the fieldList option
The fieldList option can be used when creating/patching entities in order to specifiy the fields that are allowed to be set on the entity. Sparse out the id field, and it cannot be injected anymore.
$article = $this->Articles->patchEntity($article, $this->request->data, [
'fieldList' => [
'title',
'body',
//...
]
]);
And finally, validation
Validation can prevent injections too, however that might be considered a little wonky. A custom rule that simply returns false would for example do it, you could create an additional validator, something like
public function validationAdd(Validator $validator) {
return
$this->validationDefault($validator)
->add('id', 'mustNotBePresent', ['rule' => function() {
return false;
}]);
}
which could then be used when patching the entity like
$article = $this->Articles->patchEntity($article, $this->request->data, [
'validate' => 'add'
]);
My table in the DB contains 2 fields. 'Title' and 'IsBusiness'(which is stored as a bool on wether the record is a business account or not.
When adding a new record on screen the editorFor is used to display a checkbox for 'IsBusiness' which passes back true or false.
#Html.EditorFor(x => x.IsBusiness)
#Html.ValidationMessageFor(x => x.IsBusiness)
I want to change this to 2 radio buttons. 'Product' and 'Business' which passes back false if product is selected and true is business is selected.
So far my code below keeps passing back false. It wont store 'True'...any ideas?
<label>#Html.RadioButtonFor(x => x.IsBusiness, "Business")Business</label>
<label>#Html.RadioButtonFor(x => x.IsBusiness, "Product")Product</label>
Try:
<label>#Html.RadioButtonFor(x => x.IsBusiness, true) Business</label>
<label>#Html.RadioButtonFor(x => x.IsBusiness, false) Product</label>
See also: ASP.NET MVC Yes/No Radio Buttons with Strongly Bound Model MVC
I can get the value of a dropdownlist this way but i cant get the value of a selectlist item with this code. What i can do to get the value into my controller for my create action.
My Controller Contains :
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Product products, Design designs, Material materials, Color colors, Picture pictures, FormCollection form,EventArgs e)
{
if (Request.Files != null)
{
long prod = Convert.ToInt64(form["Product"]);
pictures.product_id = db.Products.Single(x => x.id == prod).id;
My View Contains :
#Html.DropDownList("Product", new SelectList((System.Collections.IEnumerable)ViewData["Productlist"], "id", "name"), "Please Select Product", new { onchange = "productlist()", style = "width:190px; padding:4px; margin:4px;" })
i can get dropdownlist value but cant get the value of selectlist..
My View Contains : (SelectList)
<select id="Color" style=" width:190px; padding:4px; margin:4px;" onchange="colorlist()">
<option label="Please Select Color" ></option>
</select>
so if im gonna need to use json how can i use it inside create action and in view.
If you want to use the default binding, then you need an argument in your Create action named "Product" of whatever type you are passing (i.e. string). Then when the form POSTs to the action the binder will set that argument value to the option selected at the time of POST.