MVC default to checked based on value in item - asp.net-mvc

I have an MVC 5 project and we are creating radio buttons from database records.
It works fine below but there has to be a way to NOT have the if check in there and either append the checked after the fact or even before.
As shown below, the only difference between the 2 radio button creations is: new { #checked = "checked" }
#{
foreach (var item in Model.PaintColor)
{
if (item.DefaultChoice == true)
{
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId.ToString(),
new { #checked = "checked" }
)
}
else
{
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId.ToString()
)
}
#Html.Label("Color" + item.ColorId, item.ColorDesc)
}
<br />
}

See comments from Stephen regarding fixing this. Basically setting it as checked as not required as since it checks the value that it is being bound to.
When the view model is initially created I set the field value to the Default value and the radio button is set as checked.
foreach (var item in Model.PaintColor)
{
var idVal = string.Format("PaintColor_{0}", item.ColorId);
<div>
<label>
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId,
new { id = string.Format("Rb{0}", idVal) }
)
<span>#item.Description</span>
</label>

Related

Delete By Id MVC 5

I'm using BeginCollectionItem with MVC 5 for adding and removing rows whenever.
One issue I'm having is with the delete function, I followed an online tutorial
which specified using #divId:first which seems to indicate deleting the first row whenever. This is no good for me, and wouldn't make sense to an end user.
As I'm using BCI I want to delete these from the html DOM so they won't have database Ids.
How do I delete by the Id of the model, this apparently (I think I read somewhere) is automatically generated by BCI?
Delete Function in the main view
$('#deleterow').live('click', function () {
$(this).parents('#newRow:first').remove();
return false;
});
Partial View with rows I want to delete by Id
#model Mvc.Models.Project
#using (Html.BeginCollectionItem("something"))
{
<div id="newRow">
#Html.LabelFor(m => m.Name)
#Html.EditorFor(m => m.Name)
Delete
</div>
}
Update 2
When viewing the rendered html the data-action attribute renders as 0 for all objects so the JQuery can't and won't delete a row/object from the view.
Update
Instead of the check box I want to use the Delete link button, I assume this is possible? Not very familiar with jQuery but it is something I intend to look at, fairly new to MVC too but this is what I have so far:
Main View
<h3>Students</h3>
<div id="newStudent">
#foreach(var Student in Model.students)
{
Html.RenderPartial("_Student");
}
</div>
<input type="button" id="addStudent" name="addStudent" value="Add Student"/>
<input type="submit" value="Submit"/>
#section Scripts
{
<script type="text/javascript">
$('#addStudent').on('click', function () {
$.ajax({
async: false,
url: 'School/AddNewStudent'
}).success(function (partialView) {
$('#newStudent').append(partialView);
});
});
$('#newStudent').on('click', '.deleteStudent', function () {
var id = $(this).data('id');
if (id === 0) { // assumes Id is integer
$(this).closest('.studentRow').remove();
}
else { // existing item - controller to delete from Db
var url = '#Url.Action("action", "controller")';
$.post(url, { ID: id }, function (response) {
if (response) {
$(this).closest('.studentRow').remove();
}
}).fail(function (response) {
// display error message
});
}
});
</script>
}
Partial View
#using (Html.BeginCollectionItem("students"))
{
<div id="studentRow">
#Html.HiddenFor(m => m.Id)
#Html.LabelFor(m => m.Name)
#Html.EditorFor(m => m.Name)
Delete
</div>
}
Controller
public class SchoolController : Controller
{
// GET: School
public ActionResult Index()
{
var newSchool = new School();
return View(newSchool);
}
public ActionResult AddNewStudent()
{
var student = new Student();
return PartialView("_Student", student);
}
[HttpPost, ActionName("DeleteStudent")]
public ActionResult DeleteStudent(School school)
{
foreach(var student in school.students.Where(s => !s.isDeleted))
{
return View(school.students);
}
return View();
}
}
What I have done is created a IsDeleted Property in Model/ViewModel, Put it in the Row as a Hidden Field, And also have a delete button against each Row
using (Html.BeginCollectionItem("Contacts"))
{
<div class="row mt-10">
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.isDeleted, new { data_is_deleted = "false" })
.......Removed HTML
<div class="col-md-1">
<span class="glyphicon glyphicon-trash" data-action="removeItem" title="remove" style="cursor:pointer"></span>
</div>
Then add this jQuery a JavaScript file. (Note: Don't add this to the Row Partial View, I add it in the View that calls the Row Partial View)
You might have to edit this jQuery to match your HTML structure, The goal in this jQuery is to update the IsDeleted field to either true or false and then Disable the other Input fields
$(document).on('click', '*[data-action="removeItem"]', function(e){
e.stopPropagation();
var btn = $(this);
var row = btn.closest('.row');
var parent = btn.parent();
var checkBox = parent.siblings('*[data-is-deleted]');
var checkBoxVal = checkBox.val();
if(checkBoxVal == 'False' || checkBox.val() == 'false'){
checkBox.val('true');
row.find('input, textarea, select').attr('readonly', 'readonly');
} else {
checkBox.val('false');
row.find('input, textarea, select').attr("readonly", false);
}
checkBoxVal = checkBox.val();
});
This is what your view will look like:
When post Back to Controller:
foreach (var contact in contacts.Where(s => !s.isDeleted))
{
// New and Updated Items
}
foreach (var contact in myModel.Where(s => s.isDeleted && s.Id!= 0))
{
// Deleted Items
// You don't have to delete Items where Id == 0, Bcz they are not in the DB.
// Just some Item added to the View and then deleted without Save
}
Deleted Items will be disabled: Note: You can Hide them by editing the above jQuery
EDIT A:
Actual controller code is something like this:
[HttpPost]
public ActionResult SaveStudent(Student model){
// Save model items
// Then Save the List of Items like this:
foreach (var contact in model.myListItems.Where(s => !s.isDeleted))
{
// New and Updated Items
}
foreach (var contact in model.myListItems.Where(s => s.isDeleted && s.Id!= 0))
{
// Deleted Items
// You don't have to delete Items where Id == 0, Bcz they are not in the DB.
// Just some Item added to the View and then deleted without Save
}
}
Firstly .live() was depreciated in jquery-1.7 and removed in 1.9. Use .on() instead. Next your generating invalid html by generating duplicate id attributes for the 'delete' link, which also means you will only ever be able to delete the first item and you never be able to delete newly added items because you are not using event delegation. Note also the BeginCollectionItem does not _ automatically generate the models ID_. All it does is add a prefix to the property name which includes an indexer value based on a guid so that the items can be bound to a collection on post back.
The link in your partial partial needs a class name and should store the Id value so it can be easily accessed in the script.
#using (Html.BeginCollectionItem("students"))
{
<div id="studentRow">
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.isDeleted) // not sure what the point of the data- attribute is
#Html.LabelFor(m => m.Name)
#Html.EditorFor(m => m.Name)
Delete
</div>
}
Then your script needs to be (note the id="newStudent" for the enclosing <div> is confusing since you foreach loop is generating the html for existing items)
$('#newStudent').on('click', '.deleteStudent', function() { // use event delegation
var id = $(this).data('id');
if (id == 0) { // assumes property Id is typeof int
// Its a new item so just remove from the DOM
$(this).closest('.studentRow').remove();
} else {
// Its an existing item so call controller to delete it from the database
var url = '#Url.Action(""DeleteStudent", "School")';
$.post(url, { ID: id }, function(response) {
if(response) {
// The student was successfully deleted
$(this).closest('.studentRow').remove();
}
}).fail(function (response) {
// Oops, something went wrong - display error message?
});
}
});
And the controller
[HttpPost]
public JsonResult DeleteStudent(int ID)
{
// delete the student from the database based on the ID and signal success
return Json(true);
}

How to add an arbitrary number of rows to a database in ASP.net MVC 4?

I'm trying to add an unspecified number of rows to my database. These rows are created dynamically by the user's request:
$(document).ready(function () {
$(document).on('click', '#dataTable .add', function () {
var row = $(this).closest('tr');
if ($('#dataTable .add').length <= 100) {
var clone = row.clone();
// Clear the values.
var tr = clone.closest('tr');
tr.find('input[type=text]').val('');
$(this).closest('tr').after(clone);
}
});
// Only delete row if there exists more than one.
$(document).on('click', '#dataTable .removeRow', function () {
if ($('#dataTable .add').length > 1) {
$(this).closest('tr').remove();
}
});
});
So far what I am using to try and achieve this is a list with a maximum value of 100 elements (which I'd prefer a different method than this, perhaps one with no upper limit but this will do in the meantime), and I pass this list that I've created to my View():
// GET: Questions/Create
public ActionResult Create(int questionnaireUID)
{
List<QUESTION> questions = new List<QUESTION>();
for (var i = 0; i < 100; i++)
{
questions.Add(new QUESTION { QuestionnaireUID = questionnaireUID, Question1 = "" });
}
return View(questions);
}
In my View() here is the pertinent code sample that I am using to populate the values of my list (I believe my problems lie here...):
<table id="dataTable" name="dataTable">
#if (Model != null && Model.Count() > 0)
{
<tr>
#Html.HiddenFor(model => model[i].QuestionnaireUID)
<td>
#Html.EditorFor(model => model[i].Question1, new { htmlAttributes = new { #type = "text", #name = "question", #class = "question_input" } })
#Html.ValidationMessageFor(model => model[i].Question1, "", new { #class = "text-danger" })
<input type="button" name="addRow[]" class="add" value="Add">
<input type="button" name="addRow[]" class="removeRow" value="Remove">
</td>
</tr>
i++;
}
</table>
When I try to save this created list to my database, using the code below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "QuestionnaireUID, Question1")] List<QUESTION> questions)
{
if (ModelState.IsValid)
{
foreach (var question in questions)
{
db.QUESTIONS.Add(question);
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(questions);
}
I am only able to save the first element of the list. The size of my list is always one, regardless of if the user dynamically generated 10 list elements.
Furthermore, if the first element has an error (e.g. it is empty) this error message is printed to all elements in the table. So, this being said although my list is able to generate in the view multiple elements/rows, for my database only one row is actually meaningful/used--which is the first row. How can I fix this?
As far as I know, MVC doesn't provide a built-in solution for handling dynamic/variable length lists. There are a few custom solutions that involve creating helpers to handle posting dynamic list objects. Here is one that I have used many times in the past, and is completely compatible with both old and current versions of ASP MVC (the blog uses old MVC markup, so you will need to update it to the RAZOR code for MVC3+).
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
The key component of this is the helper class "BeginCollectionItem", which handles the indexing of your list in a way that MVC will accept as a post-action parameter. Hope this helps.

DropDownListFor HTML helper not selecting option for Enum model property

I have an ASP.NET MVC 4 site and a domain model that uses an Enum. I'm able to generate a list of SelectListItem objects, but the proper item is not selected.
Domain Model
public enum ApplicationStatus
{
Unknown = 0,
Incomplete = 1,
Submitted = 2,
Error = 4
}
public class Application
{
public ApplicationStatus Status { get; set; }
// ...
}
The "Edit" View
#model Application
#using (Html.BeginForm("Edit", "Applications", new { ... }, FormMethod.Post, new { role = "form", #class = "form-horizontal" }))
{
#Html.Partial("_Form", Model)
<p>
#Html.ActionLink("Cancel", "Details", new { ... }, new { #class = "btn btn-default" })
<button type="submit" class="btn btn-primary">Save</button>
</p>
}
The "_Form" Partial
#model BWE.Models.Entity.BitWeb.Application
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Status, new { #class = "col-sm-2" })
<div class="col-sm-10">
#Html.DropDownListFor(model => model.Status, SelectHelper.GetApplicationStatusOptions(Model.Status))
#Html.ValidationMessageFor(model => model.Status)
</div>
</div>
SelectHelper
public static class SelectHelper
{
public static IEnumerable<SelectListItem> GetApplicationStatusOptions(ApplicationStatus currentStatus)
{
var items = new List<SelectListItem>()
{
new SelectListItem()
{
Text = "Select",
Value = string.Empty
}
};
IEnumerable<ApplicationStatus> statuses = Enum.GetValues(typeof(ApplicationStatus)).Cast<ApplicationStatus>();
foreach (var status in statuses)
{
if (status == ApplicationStatus.Unknown)
continue;
items.Add(new SelectListItem()
{
Text = status.ToString(),
Value = ((int)status).ToString(),
Selected = status == currentStatus
});
}
return items;
}
}
The "Select" option is always selected in the dropdown even though I can step through the code and see one of the SelectListItem objects get their Selected property set to true.
I've tried the solution recommended in My templated helper using a DropDownListFor does NOT reflect the model of enum. Why?, but this solution was geared towards MVC 3. I tried the solution (passing a SelectList object as the second argument to Html.DropDownListFor) and all I got was a dropdown list with 4 options whose display text was "System.Web.Mvc.SelectListItem" and no values for the <option> tags.
Furthermore, I tried other solutions that created an #Html.EnumDropDownListFor(...) helper function, which behaved the same way. It seems that all though the proper SelectListItem is getting selected, maybe the current Model.Status value is overriding it?
Update #1
I added an integer property called StatusId which gets and sets the Status property as an integer, and this works when calling Html.DropDownListFor(model => model.StatusId, ...) however I was hoping for a solution that allows me to use the enum value directly, not as a proxy through another property.
For some crazy reason, enum values are rendered as their string-based names by Razor, rather than their integer value counterparts. Regardless, my guess is that your SelectHelper is returning options with values as integers, which is why converting your enum value to an int allowed the selection to work. Since this is a custom component you have anyways, I would suggest simply modifying your helper to return the enum string names as the option values instead of ints. Then the property value and the option values will line up properly.

ng-model on hidden razor input not working

In my MVC 5 Razor view, I created a hidden field, like this:
#Html.HiddenFor(x => x.FormData.UserId,
new { ng_model = "selectedEmployee.userId" })
When I perform the necessary angular action to fill the selectedEmployee.userId property, the hidden input's value is not filled.
But, when I change it to
#Html.TextBoxFor(x => x.FormData.UserId,
new { ng_model = "selectedEmployee.userId" })
It's working and the data is posted to the server.
And
<input type="hidden" name="FormData.UserId" value="{{selectedEmployee.userId}}">
is working, but
#Html.HiddenFor(x => x.FormData.UserId,
new { value = "{{selectedEmployee.userId}}" })
is not. (which probably has to do with Razor overwriting the HTML value)
What's the reason that in Razor a hidden input with an ng-model is not working?
Please change value into ng_value
#Html.HiddenFor(x => x.FormData.UserId,
new { ng_value = "{{selectedEmployee.userId}}" })
Here is how to assign a value to Hidden field. 'V' character of value attribute must be Capital letter.
<div data-ng-controller="MyFunction">
#{Html.BeginForm("SchoolMedium", "BasicSetup", FormMethod.Post, new { });
#Html.HiddenFor(s => s.SchoolMediumId, new { Value = "{{mascotmedium.SchoolMediumId}}" });
Html.EndForm();
}
and in controller
$scope.EditWhenAdded= function(row){
$scope.mascotmedium = row.entity;
};

ASP.NET MVC Get Id (value) from DropDownListFor

I have a series of DropDowns that I want the user to add to and edit. I found a helper extension from StackOverflow to build an action image link.
#Html.DropDownListFor(model => model.Entry.ParadigmId, ((IEnumerable<Pylon.Models.Paradigm>)ViewBag.PossibleParadigms).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.Name),
Value = option.ParadigmId.ToString(),
Selected = (Model != null) && (option.ParadigmId == Model.Entry.ParadigmId)
}), "Select")
#Html.ActionImage("ParadigmEdit", new { id = ? }, "~/Content/Images/Edit_Icon.gif", "ParadigmEdit")
I am not sure how to reference the selected id value in the DropDownList where the question mark is in the above code.
You can't reference the selected value from the dropdown using server side code (which what HTML helpers represent) because the selection is done by the user on the client side. Your problem stems from the fact that you are trying to generate an anchor which should send a value which is known only by the client. You can do this only using javascript. Or another possibility is to simply use a form with an image submit button:
#using (Html.BeginForm("ParadigmEdit", "ControllerName"))
{
#Html.DropDownListFor(
model => model.Entry.ParadigmId,
// WARNING: this code definetely does not belong to a view
((IEnumerable<Pylon.Models.Paradigm>)ViewBag.PossibleParadigms).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.Name),
Value = option.ParadigmId.ToString(),
Selected = (Model != null) && (option.ParadigmId == Model.Entry.ParadigmId)
}),
"Select"
)
<input type="image" alt="ParadigmEdit" src="#Url.Content("~/Content/Images/Edit_Icon.gif")" />
}
and of course after you move the ugly code where it belongs (the mapping layer or the view model) your code would become:
#using (Html.BeginForm("ParadigmEdit", "ControllerName"))
{
#Html.DropDownListFor(
model => model.Entry.ParadigmId,
Model.ParadigmValues,
"Select"
)
<input type="image" alt="ParadigmEdit" src="#Url.Content("~/Content/Images/Edit_Icon.gif")" />
}

Resources