Editing a collection in a partial view - asp.net-mvc-3

I was having problems updating child collections of my object ( Foreign key constraint, EF with collection of childobjects ) which was solved using this guide: http://www.codetuning.net/blog/post/Binding-Model-Graphs-with-ASPNETMVC.aspx
When cleaning up my code, I moved the collection editing to a partial view.
#Html.Partial("_AttendeeInformationFields", Model.CaptureAttendeeInformationFields)
The partial view looks like this
#model ICollection<EventModel.Models.AttendeeInformationField>
<table id="CaptureAttendeeInformationFields">
<tr>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().Name)</th>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().Required)</th>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().FieldType)</th>
#*<th>#Html.GetDisplayName(model => model.FirstOrDefault().InputType)</th>*#
</tr>
#Html.EditorForModel()
</table>
#Html.LinkToAddNestedForm("Lägg till", "#CaptureAttendeeInformationFields", ".AttendeeInformationField", "CaptureAttendeeInformationFields", typeof(EventModel.Models.AttendeeInformationField))
#Html.ValidationMessageFor(model => model)
Then I have a EditorTemplate for AttendeeInformationField that looks like this
#model EventModel.Models.AttendeeInformationField
<tr class="AttendeeInformationField">
#using (Html.BeginCollectionItem("CaptureAttendeeInformationFields"))
{
<td>#Html.TextBoxFor(model => model.Name) #Html.HiddenFor(model => model.MagnetEventId) #Html.HiddenFor(model => model.Id)</td>
<td>#Html.CheckBoxFor(model => model.Required)</td>
<td>#Html.DropDownListFor(model => model.FieldType, new SelectList(Enum.GetValues(typeof(EventModel.Models.FieldType)), Model.FieldType))</td>
#*<td>#Html.TextBoxFor(model => model.InputType)</td>*#
}
</tr>
The BeginCollectionItem is from this guide: http://ivanz.com/2011/06/16/editing-variable-length-reorderable-collections-in-asp-net-mvc-part-1/
This helps me with two things
1. The index is no longer a sequential 0-based integer series, and I can reorder my items, as well as add/delete without worrying about breaking the sequence.
2. The partial seems to loose the context, and my controls get names like "[0].Required" where it should be "CaptureAttendeeInformationField[0].Required". The BeginCollectionItem takes care of this.
The current problem is that these fixes doesn't seem to be compatible. I suppose it might have something to do with this disclaimer in the first article:
within this implementation we assume that the index is an integer starting at 0
I'm hoping that someone can point me in the right direction.
Adding items in this solution works.
Ugly solution
I sure hope this is not the only way to do this, but for now I've solved the problem like this:
foreach (var attendeeInformationField in viewModel.AttendeeInformationFields)
{
var attendeeInformationFieldId = attendeeInformationField.Id;
var originalAttendeeInformationField = original.CaptureAttendeeInformationFields.FirstOrDefault(aif => aif.Id == attendeeInformationFieldId);
if (originalAttendeeInformationField==null)
{
original.CaptureAttendeeInformationFields.Add(attendeeInformationField);
}
else
{
if (originalAttendeeInformationField != attendeeInformationField)
{
originalAttendeeInformationField = attendeeInformationField;
originalAttendeeInformationField.FieldType = attendeeInformationField.FieldType;
//originalAttendeeInformationField.InputType = attendeeInformationField.InputType;
originalAttendeeInformationField.Name = attendeeInformationField.Name;
originalAttendeeInformationField.Required = attendeeInformationField.Required;
}
}
}
I don't like it at all, but it works. There must be a better way of doing this.

Related

How to alternate row style without javascript for DisplayFor/EditorFor bound to a list of model

How can I pass the index of the current element to the View for DisplayFor/EditorFor so that I can decide if the row should show the alternate style or not?
My main view looks like this:
<table>
#Html.EditorFor(model => model.MyListOfItems)
</table>
The view used for the EditorFor looks like:
#Html.HiddenFor(model => model.IdOfTheItem)
<tr class="shouldBeChangedDependingOnRowEvenOrNot">
<td>#Html.CheckBoxFor(model => model.MarkForBatchEdit)</td>
<td>#Html.DisplayFor(model => model.NameOfThisItem)</td>
<td>#Html.DisplayFor(model => model.StateOfThisItem)</td>
</tr>
Well I am aware of this similar question and the suggested solution: alternating row color MVC
But I can't apply them to this case.
Any suggestions?
The EditorFor is overloaded to take an additionalViewData parameter, so you could pass the index in the ViewData, which is a collection of key/value pairs.
#Html.EditorFor( model => model.MyListOfItems , new { CurrentIndex = SomeNumber } )
In your view you would get the value using ViewData["CurrentIndex"].
Also, instead of passing the element index, why not do the calculation in your controller and pass whether you have an even or odd row in your ViewData.
bool isEvenRow = ((CurrentElementIndex % 2) == 0);
ViewData["isEvenRow"] = isEvenRow;
Then you will just toggle your CSS in the view based on whether the value is true or false.

Receiving an attempt was made to remove a relationship between x and x however one of the relationship's foreign keys

I have an MVC project and I have a case where i need to update a parent and multiple child entities at the same time. In the post action I am receiving "an attempt was made to remove a relationship between x and x however one of the relationship's foreign keys" which is strange, all I'm doing is update, I'm not droping any entity whatsoever. I am using Linq to SQL and MVC3. The pseudocode is like the following:
#model Project.Models.ParentModel
...
#using (Html.BeginForm()) {
#Html.Label("Parent property")
#Html.EditorFor(model => model.ParentProperty)
#foreach (var child in Model.Childs)
{
Html.RenderPartial("_EditChild", child)
// Which is nothing more than a label and an editor for a property like:
// #model Project.Models.ChildModel
// #Html.Label("Child property")
// #Hteml.EditorFor(model => model.ChildProperty)
}
...
}
The Action looks like:
public ActionResult Edit(int id, FormCollection collection)
{
var parent = new Parent();
TryUpdateModel(Parent()); // which updates the parent and the child properties correctly
dataContext.SubmitChanges();
}
Can anybody explaing this behavior. Once again, I'm not removing or dropping any child entities!
The binding over a list can be pretty nasty, I had some problems with it myself. I modified my list editing code to work with childs and tested it, it worked and the data is correctly bound and visible in the post action:
#model MvcApplication2.Models.Parent
#using (Html.BeginForm())
{
<table>
#{
<tr>
<td>
#Html.TextBoxFor(m => m.Text)
#Html.HiddenFor(m => m.ID)
</td>
</tr>
for (int i = 0; i < Model.Children.Count; i++)
{
<tr>
<td>
#Html.TextBoxFor(x => x.Children[i].Title)
#Html.HiddenFor(x => x.Children[i].ID)
</td>
</tr>
}
}
</table>
<div class="button">
<input class="submit" type="submit" name="btnSave" id="btnSave" value="Save" />
</div>
}
Controller for my test looks like this:
[HttpGet]
public ActionResult EditingChildren()
{
Parent parent = new Parent() { Text = "" };
parent.Children = new List<Child>();
parent.Children.Add(new Child() { Title = "" });
parent.Children.Add(new Child() { Title = "" });
parent.Children.Add(new Child() { Title = "" });
return View(parent);
}
[HttpPost]
public ActionResult EditingChildren(Parent parent)
{
// save parent with children
}
Editing my post about saving data back with linq to sql:
If you are not binding the ID on the view, it will be left empty in the object in the post method. That gives you troubles saving back the data.
I usally bind therefore the ID to an hidden field so it will not be empty any more (view code above edited and added HiddenFor beneath TextBoxFor).
Have also a look about updating data with linq to sql on this website:
http://davedewinter.com/2009/04/07/linq-to-sql-updating-entities/ (under Attach an Entity, Change Properties, Update)
and this post:
enter link description here

Conditionally display an item property on view in ASP.NET MVC

New to ASP.NET MVC 3. This seems like it should be a really simple issue, but it's actually got me stumped. What I want to do is display a certain DateTime value if a value has been entered, or leave the space empty if no value has been entered. The code in my view is simply this:
<td>
#if (item.TimeReturned > DateTime.MinValue)
{
Html.DisplayFor(modelItem => item.TimeReturned);
}
</td>
which seems really basic and straightforward. Also, though it seems like overkill, TimeReturned explicitly defaults in the model to DateTime.MinValue.
No values are ever displayed. Just to make sure it wasn't some operator funkiness, I also tried
<td>
#if (DateTime.Compare(item.TimeReturned,DateTime.MinValue) != 0)
{
Html.DisplayFor(modelItem => item.TimeReturned );
}
</td>
which also displays nothing. I must be missing some fundamental insight. I could write a method in the model class to accomplish this, I guess, but it seems intuitive to do it this way. Any guidance would be greatly appreciated!
Try,
<td>
#if (item.TimeReturned > DateTime.MinValue)
{
#Html.DisplayFor(modelItem => item.TimeReturned)
}
</td>
You can use it as follows
#if (condetion)
{
#:#Html.DisplayFor(model => model.property)
}

Can't set SelectedItem in DropDownList in my MVC3 view

I know that I can set the SelectedItem in my controller, but I can't figure out how to set it in my view. I'm working on a sort of flashcard (study guide) application and I have imported about 400 test questions. Now I want to write a page for the instructor to be able to select a "Category" for each question. I'd like them to be able to update the category for all the questions on one page. My model has a question entity that contains a foreign key field to the category entity (the field is called QuestionCategory). So, my view is based on the Question entity, but I'm sending over the list of Categories (there are 14) in the ViewBag (so I don't have to send a full SelectList over with each of the 400 questions. As my view is iterating thru the items in my View, I just want to add a SelectList that contains the 14 categories in my ViewBag and then set the SelectedItem based on the value of item.QuestionCategory. I can't make it work.
Here's my controller action:
public ActionResult Index()
{
var context = new HBModel.HBEntities();
var query = from q in context.tblQuestions.Include("tblCategory") select q;
var questions = query.ToList();
ViewBag.Categories = new SelectList(context.tblCategories, "CategoryID", "CategoryName");
return View(questions);
}
Here's some of the things I've tried in the view (with associated error messages in the comments)
#model IEnumerable<HBModel.tblQuestion>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Question
</th>
<th>
Answer
</th>
<th>
AnswerSource
</th>
<th>
Category
</th>
<th>
Action
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#item.Question
</td>
<td>
#item.Answer
</td>
<td>
#item.AnswerSource
</td>
<td>
#item.tblCategory.CategoryName
#*This one works, but cannot initialize the selected item to be current database value*#
#Html.DropDownList("Categories")
#*compile error - CS0200: Property or indexer 'System.Web.Mvc.SelectList.SelectedValue' cannot be assigned to -- it is read only*#
#*#Html.DropDownListFor(m => item.QuestionCategory, (ViewBag.Categories as SelectList).SelectedValue = item.QuestionCategory)*#
#*error {"DataBinding: 'System.Web.Mvc.SelectListItem' does not contain a property with the name 'CategoryId'."}*#
#Html.DropDownListFor(m => item.QuestionCategory, new SelectList(ViewBag.Categories, "CategoryId", "CategoryName"))
#*error - {"DataBinding: 'System.Char' does not contain a property with the name 'CategoryId'."}*#
#Html.DropDownListFor(m => item.QuestionCategory, new SelectList("Categories", "CategoryId", "CategoryName"))
)
</td>
<td style="width: 100px">
#Html.ActionLink("Edit", "Edit", new { id = item.QuestionID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.QuestionID })
</td>
</tr>
}
</table>
Of course, if I can get this to work, I'll need to try and add an action to go back to the controller and update all the records, but I'll just be happy to resolve my current issue.
I would really appreciate any help on this - Thanks!
You need to explicitly create the options in the select tag, using #Html.DropDownList, as follows (taken from a working app):
#Html.DropDownListFor(model => model.IdAccountFrom, ((IEnumerable<FlatAdmin.Domain.Entities.Account>)ViewBag.AllAccounts).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.AccountName),
Value = option.AccountId.ToString(),
Selected = (Model != null) && (option.AccountId == Model.IdAccountFrom)
}), "Choose...")
#Html.ValidationMessageFor(model => model.IdAccountFrom)
You obviously need to change to the properties on your #Model.
NOTE:
This code was auto-generated by the MvcScaffolding NuGet package when I scaffolded a controller.
This package requires you to use Entity Framework Code First POCO classes for your entities. These are easy to generate from an existing database using the Entity Framework Power Tools CTP.
With MVC, you need to spend some time researching the tooling that is out there to help you generate the stuff that you need. Using these tools is a great way to get started and to see how to do things. You can then tweak the output to your heart's content.

MVCContrib Grid MVC 3 Razor .RowStart

Having trouble with the examples for the .RowStart method.
Comparing the 2 syntaxes: http://www.jeremyskinner.co.uk/2009/03/01/mvccontrib-grid-part-5-the-action-syntax/
In this
.RowStart(row => string.Format("<tr{0}>", row.IsAlternate ? "style=\"background-color:#CCDDCC\"" : ""))
row.IsAlternate throws an error as row isnt the GridRow, its actually your model (well the row's data item).
The second syntax (ActionSyntax) :
.RowStart((p,row) => {
if (row.IsAlternate) { %>
<tr style="background-color:#CCDDCC">
<% } else { %>
<tr>
<% }
}).Render(); %>
doesnt seem to translate to Razor
.RowStart((x, row) => string.Format("<tr class='{0}'>", row.IsAlternate ? "grid-row" : "grid-row-alt"))
Passes ok, but doesn't emit any row changes.
Any had this working?
I've just noticed some convention stuff that Html.Grid is putting in for you...
Given the Following
Html.Grid(Model.Results).Attributes(#class => "grid")
results in a table with class = "grid",
even rows with a class = "gridrow",
and odd rows with a class = "gridrow_alternate"
Not sure if this will help but one thing I've been doing lately is using:
.RowAttributes(x => new Dictionary<string, object> { { "class", x.value == myValue ? "highlight" : "" } })
This allows me to do a lot with css values for the attributes. then for supporting "Zebra striping" I use pure css (browser compatibility could be an issue here, but it's graceful just doesn't render on old browsers) looks something like
tr:nth-child(odd) {
background-color: #eee;}
Gives u great control over the table. More info on the selectors Sitepoint child selectors
Other wise you could try the google groups for mvccontib Jeremy is usually sharp of the mark to help out.
Hope this helped.

Resources