Is there a way to bind a complex object to radiobutton - c#

I have a list of radiobuttons that I am generating from my model like this:
#foreach (var offer in Model.AvailableOffers)
{
<div>
#Html.RadioButtonFor(m => m.selectedAvailableOffer, offer)
#Html.Label(offer.begin_date.Month + "/" + offer.begin_date.Day + " - " + offer.end_date.Month + "/" + offer.end_date.Day + ", " + offer.offer_name)
</div>
}
Model includes these:
public List<AvailableOffer> AvailableOffers { get; set; }
public AvailableOffer selectedAvailableOffer { get; set; }
See how I am trying to post my offer object back to my controller in the RadioButtonFor method? This is obviously not working, but it's what I want to accomplish. Is there any way to post the whole selected offer object back to my controller or am I stuck adding some sort of ID to my object and only posting back the ID as is suggested here:
Correct way to bind an mvc3 radiobutton to a model

Shortly: No, you can't pass whole 'offer' object back to the server. It's better to use only id's on the client side.
If you realy want to post back whole offer you can use some kind of serialization to store data from each offer object as a value of radiobutton.

Related

Save multiselect c#

I use cshtml for my select:
<div class="form-group">
<label class="control-label col-md-2" for="Ligne">Ligne</label>
<div class="container">
<select name="Ligne" id="ligne" class="selectpicker" title="Select something" data-live-search="true" multiple="multiple">
#foreach (var item in Model.allLignes)
{
<option value=#Html.DisplayFor(modelItem => item.CodeLigne)>#Html.DisplayFor(modelItem => item.CodeLigne)</option>
}
</select>
</div>
</div>
But when I send form, it saves only one value...
Can you help me?
my controller:
public ActionResult Create([Bind(Include = "Id,name,begin,end,Ligne")] RT TypeRT)
cmdPerso.CommandText = "insert into RT (name, begin, end, Ligne, CreateBy) VALUES ('" + TypeRT.name+ "','" + TypeRT.begin+ "','" + TypeRT.end+ "','" + TypeRT.Ligne+ "','" + User.Identity.Name + "')";
I want to use this select expression for edit form.
Thank you in advance.
First, change the type of your TypeRT.Ligne to List<string> (you can change string to another type if needed, like List<int>).
Now your controller will get the list of selected values, but then your query must save them. Ideally, this list should be saved in another table and you should save each value in a separate row. That's a good normalized design.
However, it seems from your code that you save the list in a single field of the same table, perhaps comma-separated? That's not a good design and it needs to be normalized like I mentioned above, but if you really want to do so then you have to join the array items using the separator that you want:
String.Join(",", TypeRT.Ligne);
If you're using .NET < 4, you have to use ToArray first:
String.Join(",", TypeRT.Ligne.ToArray());
Note: your query is vulnerable to SQL injection. Use a parameterized query instead.
Work with the tools that the ASP MVC framework provides:
In your ViewModel, the property to which the selected values are bound must be an array. Available options can be stored in the MultiSelectList class.
using System.Web.Mvc;
public class MultiSelectViewModel {
/// <summary>
/// Selected values of the multi select.
/// </summary>
public string[] SelectedValues { get; set; }
/// <summary>
/// Possible options.
/// </summary>
public MultiSelectList AvailableOptions { get; set; }
}
In your Controller (GET case), assign the available options:
MultiSelectList can be instantiated by passing a collection of options and the names of the properties that hold the Value and Text of an option (e.g. "CodeLigne").
Ligne[] allLignes = DbContext.Lignes.ToArray(); // all possible options
// for edit use case: the options that have been selected during create use case
string[] previouslySelectedLigneCodes = new { "1", "3" };
// your ViewModel containing the multiselect properties
var vm = new MultiSelectViewModel();
vm.AvailableOptions = new MultiSelectList(
allLignes, "CodeLigne", "NomLigne", previouslySelectedLigneCodes);
In your View, you can render the MultiSelect using ListBoxFor:
#model MultiSelectViewModel
<label for="#Html.IdFor(m => m.SelectedValues)">Ligne</label>
#Html.ListBoxFor(m => m.SelectedValues, Model.AvailableOptions,
new {#class = "selectpicker", title ="Select something"})
Further reading: Step-By-Step Implementation of MultiSelectList In .NET MVC.

MVC EditorFor Remove Indexed Name

I have model that is a list of another model such that ModelList : ModelSingle
In my razor view I am using
#model somenamespace.ModelList
#Html.EditorForModel()
This iterates though each ModelSingle and returns an EditorTemplate that is strongly typed to ModelSingle.
#model somenamespace.ModelSingle
#using(Html.BeginForm("Action", "Controller", FormMethod.Post, new { id = "formname" + Model.ID}))
{
#Html.AntiForgeryToken()
#Html.EditorFor(p => p.SomeField)
#Html.EditorFor(p => p.AnotherField)
}
Each of these templates contains a form that can be used to edit the single model. These are posted individually with my controllers method expecting
public ActionResult(ModelSingle model)
The problem I'm having is that the model is not binding correctly. With a Model as such
public class ModelSingle()
{
public string SomeField { get; set; }
public string AnotherField { get; set; }
}
the EditorTemplate is being told that it was part of a list so I get
<Form>
<input name="[0].SomeField"/>
<input name="[0].AnotherField"/>
<input type="submit" value="Update"/>
</Form>
I can't simply bind to the ModelList as it's not naming ModelList[0].SomeField and even if it was I don't think that would work for anything but the first item.
Is there anyway to make the EditorTemplate ignore the fact that it's model was part of a list or force a DropDownListFor, EditorFor etc.... to just use the field name without prepending the [i].
I know I can force a Name="SomeField" change but I'd rather have a solution that will reflect any changes made in the Model class itself.
EDIT - As Requested added a simplified example of the View and EditorTemplate being used.
The problem is related to a mismatch between the input names generated by your page model (which is a list), and the model expected by your action, which is a single item from your list.
When rendering a list, the default behavior is to render the indexed names like you've shown to us (the [#] notation). Since you want to be able to post any arbitrary item from the list, you won't know ahead of time what index is used. When the model binder looks at the request for your single object, it does not attempt to use the index notation.
I don't know what your requirements are from the user perspective - e.g. whether or not a page refresh is desired, but one way to accomplish this is to provide a jQuery post for the specific item being posted:
// pass jquery form object in
var postItem = function($form) {
var postBody = {
SomeField: $form.find('input selector') // get your input value for this form
AnotherField: '' // another input select for this item
}
$.ajax({
url:'<your action url>',
type: 'POST',
contentType:"application/json; charset=utf-8",
data: JSON.stringify(postBody),
dataType: 'json',
success: function(response) {
// do something with returned markup/data
}
});
}
You are manually serializing a single instance of your model with a json object and posting that. What you return from the action is up to you: new markup to refresh that specific item, json data for a simple status, etc.
Alternately, you can consider manually looping over the items in your collection, and using Html.RenderPartial/Html.Partial to render each item using your View template. This will short-circuit the name generation for each item, and will generate the names as if it's a single instance of ModelSingle.
Finally, a quick (but kind of ugly) fix would be to have your action method take a list of ModelSingle objects. I don't suggest this.
Edit: I missed some important aspects of posting json to an mvc action
Edit2: After your comment about hardcoded names, something like this could help:
var inputs = $form.find('all input selector');
var jsonString = '{';
$.each(inputs, function(index, element) {
var parsedName = element.attr('name').chopOffTrailingFieldName();
jsonString += parsedName + ":'" + element.val() + "',";
});
jsonString += '}';

How can I output a list of field names and values that were changed?

I'm still learning C# whilst building an MVC web app. Trying to find a way to create a list of values that were changed by a user during an edit operation.
Here's one way I have that would work:
public List<string> SaveVehicleTechnicalInformation(VehicleAssetTechnicalInformationViewModel editmodel)
{
// Create a list of fields that have changed
List<string> changes = new List<string>();
var record = db.VehicleAssetTechnicalInformations.Find((int)editmodel.RecordID);
if (editmodel.Make != null && editmodel.Make != record.Make)
{
changes.Add(" [Make changed from " + record.Make + " to " + editmodel.Make + "] ");
record.Make = editmodel.Make;
}
if (editmodel.Model != null && editmodel.Model != record.Model)
{
changes.Add(" [Model changed from " + record.Model + " to " + editmodel.Model + "] ");
record.Model = editmodel.Model;
}
return changes;
}
But... As you can tell, I am going to need to write an IF/ELSE statement for every single field in my database. There are about 200 fields in there. I'm also worried that it's going to take a long time to work through the list.
Is there some way to go through the list of properties for my object iteratively, comparing them to the database record, changing them if necessary and then outputting a list of what changed.
In pseudo code this is what I guess I am after:
foreach (var field in editmodel)
{
if (field != database.field)
{
// Update the value
// Write a string about what changed
// Add the string to the list of what changed
}
}
Because I'm still learning I would appreciate guidance/tips on what subject matter to read about or where I can independently research the answer. The gaps in my skill are currently stopping me from being able to even research a solution approach.
Thanks in advance.
You can try to use Reflection for your purposes. Something like this
var fields = editmodel.GetType().GetFields();
foreach (var item in fields)
{
if (item.GetValue(editmodel) == database.field)
{
// Update the value
// Write a string about what changed
// Add the string to the list of what changed
}
}
I think I have found the hint I was looking for...
System.Reflection
More specifically, the FieldInfo.GetValue() method.
I was previously unaware of what System.Reflection was all about, so I'll research this area further to find my solution.

Dynamic textbox generation with buttons to generate a child subset of textboxes

I'm working in MVC and was trying to use jquery to perform the following, if there is an easier way i'd be happy to hear it...
I'm looking to have my form create dynamic input to produce something for example that could look like this: *i won't know how many subcat's they are going to enter nor how many main categories.
category0 <btnInsertSubCat0>
subcat0.0
subcat0.1
subcat0.2
category1 <btnInsertSubCat1>
subcat1.0
subcat1.1
<btbInsertNewCat>
I can produce a button that appends textboxes one after another as well as a button next to it but even if I use the .live attribute I can't get the subcat button to fire. Changed code around a lot and looked at different tutorial to no avail, fig. this would be semi easy to do?
jQuery(document).ready(function () {
var procID = 0;
var orgID = 0;
$('.clickme').live('click', function () {
var newItem = $("<input type='text' name='Procedure[" + procID + "]' value='Procedure[" + procID + "] />");
var newLabel = $("<br /><label id='Label[" + procID + "]' >ProcedureID: [" + procID + "]</label>");
var newDiv = $("<div class='objective'><b>Insert Objective</b>[" + procID + "." + orgID + "]</div>");
$("#procedureHolder").append(newLabel);
$("#procedureHolder").append(newItem);
$("#procedureHolder").append(newDiv);
procID++;
});
$('.objective').live('click', function () {
var newObj = $("<input type='text' id='Objective[" + (procID - 1) + "." + orgID + "]' >ObjectiveID: [" + (procID - 1) + "." + orgID + "]</label>");
$("#procedureHolder").append(newObj);
orgID++;
});
});
I edited my post, figured it out on my own how to utilize jquery to create an unlimited number of child dynamic textboxes. They all post back in the form collection as well. I figured out why the objectives weren't showing up, turns out i was declaring ID rather than Name. Thanks!
I've used the following approach for a dynamic search screen using ASP.NET MVC. Search options where managed in a database mapped to a product. This allowed the marketing team to tweak searching results and the search options in the admin section of the website. Essentially the approach was the following:
public class FormFieldCollection : List<FormField>
{
public string FormFieldType { get; set; }
}
public class FormField
{
public string Name {get;set;}
public string Type {get;set;}
public string Value {get;set;}
public bool IsChecked {get;set;}
}
public class FormFieldModel
{
public FormFieldCollection PaymentOptions { get; set; }
}
In the view generated from either custom Helper or use a foreach.
In your Controller action something like:
public ActionResult SomeActionMethod(FormCollection formCollection)
{
//search through the results, map back to class or loop through key value pairs.
}
View Code, obviously include some nice html markup to format the form.
#Model FormFieldCollection
#{
View.Title = "Form";
Layout = "~/Views/Shared/_defaultMaster.cshtml";
}
#foreach(var formField in Model){
<input type="#formField.Type" id="#formField.Name" name="#formField.Name" value=""#formField.Value"" />
}
#using (Html.BeginForm("SomeActionMethodAdd", "ControllerName", FormMethod.Post))
{
//Submit form to
Field Name: <input type="text" value="" /> * must be unique
<input type="submit" value="Submit" />
}
Syntax mightn't be perfect just going of memory for razor.

Retrieving postback from Dynamically created controls in MVC without using FormCollection

I'm passing a List to an MVC view and generating checkboxes for each object in the list (The checkboxes are named t.Name).
I'd like to be able to tell which checkboxes were checked once the form is posted. However, I'd like to avoid using the FormCollection object. Is there any way to do this?
Set the name of your checkboxes to something like "MyObject[" + index + "].Checked", and for each checkbox also put a hidden input field named something like "MyObject[" + index + "].Name" with the value set to t.Name.
If you name your fields like that, the default model binder can take your form values and map them to a list of objects with a Name property and a Checked property.
I would try something like the following:
<% foreach(var t in Model)
{ %>
<div>
<%= Html.Hidden("MyObject[" + index + "].Name", t.Name, new { id = "MyObject_" + index + "_Name" }) %>
<%= Html.Checkbox("MyObject[" + index + "].Checked", false, new { id = "MyObject_" + index + "_Checked" }) %>
</div><%
} %>
I use the anonymous type with id property so that the MVC framework components don't generate HTML elements with invalid id values, but it isn't really necessary.
Your action handling the post would look something like this:
[HttpPost]
ActionResult MyAction(IList<MyObject> objects)
{
foreach (MyObject obj in objects)
{
if (obj.Checked)
{
// ...
}
else
{
// ...
}
}
return View();
}

Categories