Using Common Properties in Partial View - asp.net-mvc

I am creating a partial view to display a record's audit stamps...i.e.
CreatedOn
CreatedBy
ModifiedOn
ModifiedBy
I currently have created a new sub-class in all my viewmodels with .AuditStamps that contains these properties, populate that in my controller and pass it to the partial.
Here is an example of one of my view Models. Note the AudiStamps sub-class
public class ItemViewModel
{
public Item Item { get; set; }
public AuditStampsViewModel AuditStamps { get; set; }
}
Here is my partial, suing this sub-class
#model OTIS.AppServ.Shared.AuditStampsViewModel
<hr />
<div class="twoColumn floatLeft">
<div class="editor-field">
<b>Created By:</b>
#Html.DisplayFor(model => model.CreatedByName) on #Html.DisplayFor(model => model.CreatedOn)
</div>
</div>
<div class="twoColumn floatLeft">
<div class="editor-field" style="text-align:right;">
<b>Modified By:</b>
#Html.DisplayFor(model => model.ModifiedByName) on #Html.DisplayFor(model => model.ModifiedOn)
</div>
</div>
But I was wondering since all my classes will have these common properties, do I really need to go through adding and populating this sub-class. The only reason I am, is because I couldn't figure out how to declare a generic model in the partial...I was hoping the partial would just work if I didn't declare the model, but it doesn't. I get an error:
An expression tree may not contain a dynamic operation
How can I achieve this? Seems like something an Interface would solve just using classes, but not sure if the same concept applies using Partials (i.e. stating a partial implements an IAuditStamps inteface???)

In the end, I set the model of the partial to be an interface, and then make sure my classes also implemented an interface like so:
Create the IAuditStamps interface
public interface IAuditStamps
{
int CreatedById { get; set; }
string CreatedByName { get; set; }
DateTime CreatedOn { get; set; }
int ModifiedById { get; set; }
string ModifiedByName { get; set; }
DateTime ModifiedOn { get; set; }
}
My Class that implements the interface
public partial class Item : IEntityId, ICompanyId, IAuditStamps
The ItemViewModel (which uses the above class Item)
public class ItemViewModel
{
public Item Item { get; set; }
}
The Partial
#model OTIS.domain.IAuditStamps
<hr />
<div class="twoColumn floatLeft">
<div class="editor-field">
<b>Created By:</b>
#Html.DisplayFor(model => model.CreatedByName) on #Html.DisplayFor(model => model.CreatedOn)
</div>
</div>
<div class="twoColumn floatLeft">
<div class="editor-field" style="text-align:right;">
<b>Modified By:</b>
#Html.DisplayFor(model => model.ModifiedByName) on #Html.DisplayFor(model => model.ModifiedOn)
</div>
</div>
Calling the partial:
#Html.Partial("_AuditStamps", Model.Item)

That's a prime candidate for a ViewModel base class that all your view models can inherit from:
public abstract class ViewModel
{
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
public DateTime ModifiedOn { get; set; }
public string ModifiedBy { get; set; }
}
public class ItemViewModel : ViewModel
{
public Item Item { get; set; }
}
Your partial could then have the ViewModel as its model type to bind to the relevant properties. Any view requiring that partial should have its model inheriting from the base ViewModel, so the properties will all be available for model binding.

Related

Nested Object Form with Razor

Im trying to implement a way how to enter recipes correctly, so far I have the following:
Recipe.cs
public class Recipe
{
[Key]
public int RecipeId { get; set; }
[Required]
public string Name { get; set; }
public string Subtitle { get; set; }
public int Serving { get; set; }
public string Instructions { get; set; }
public virtual ICollection<Ingredient> Ingredients
}
Ingredient.cs
public class Ingredient
{
[Key]
public int IngredientId { get; set; }
public string Name { get; set; }
public string Amount { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
In my Form, I want to Add the Ingredients Inline, with JavaScript Rendering a new pair of fields if needed.
(The ViewModel is nothing else than a class holding an instance of Recipe)
Create.cshtml
#model Vineyard.WebUI.Areas.Admin.Models.RecipeViewModel
<div class="span9">
<h2>Create a new Recipe</h2>
#using (#Html.BeginForm("Create", "Recipes", FormMethod.Post))
{
<fieldset>
#Html.LabelFor(r => r.Recipe.Name)
#Html.TextBoxFor(r => r.Recipe.Name)
</fieldset>
<fieldset>
#Html.LabelFor(r => r.Recipe.Subtitle)
#Html.TextBoxFor(r => r.Recipe.Subtitle)
</fieldset>
<div id="ingredients">
#Html.EditorFor(r => r.Recipe.Ingredients, "Ingredient")
</div>
Add Ingredient
<fieldset>
#Html.LabelFor(r => r.Recipe.Serving)
#Html.TextBoxFor(r => r.Recipe.Serving)
</fieldset>
<fieldset>
#Html.LabelFor(r => r.Recipe.Instructions)
#Html.TextAreaFor(r => r.Recipe.Instructions)
</fieldset>
<input type="submit" value="Save Recipe" />
}
</div>
Shared/EditorTemplates/Ingredient.cshtml
#model Vineyard.Core.Entities.Ingredient
<div class="ingredient form-inline">
#Html.LabelFor(r => r.Amount)
#Html.TextBoxFor(r => r.Amount)
#Html.LabelFor(r => r.Name)
#Html.TextBoxFor(r => r.Name)
</div>
In the rendered HTML tough, I see the following:
<input id="Recipe_Ingredients_Amount" name="Recipe.Ingredients.Amount" type="text" value="">
Which leads my to believe, that it does not Add the Ingredient as a Member of the Collection, but as a full blown Object by itself. Shouldnt it have an index referencing to it? (Based of this http://jarrettmeyer.com/post/2995732471/nested-collection-models-in-asp-net-mvc-3)
When I look at the Model, that is being passed back to the controller, the Ingredients Part is null
So, Im wondering - what am I missing here or doing wrong, that can be changed to actually populate the Collection correctly? From there, I can handle it in the controller to be saved in the right format.
Change
public virtual ICollection<Ingredient> Ingredients
to
public IEnumerable<Ingredient> Ingredients { get; set; }
(lose the virtual and change ICollection to IEnumerable).
Also, in your controller, pre-populate the list of ingredients with a single item (for testing) otherwise nothing will appear in your HTML.
This answer was converted from my comment

How to Creating View And View Model For Two Related Entity

i have two entity:
1) student
and 2) address.
public class Student
{
Public Int StudentId { get; set; }
Public String FullName { get; set; }
Public virtual IList<Address> Addresses { get; set; }
}
public class Address
{
Public Int AddressId { get; set; }
Public Int StudentId { get; set; }
Public String FullAddress { get; set; }
Public virtual Student Student { get; set; }
}
each student may have zero or more address.
i want to create single view for this two entity. i know that must create a view model. this is view model.
public class StudentViewModel
{
Public Int StudentId { get; set; }
Public String FullName { get; set; }
public Address AddAddressModel { get; set; }
Public virtual IList<Address> Addresses { get; set; }
}
and i create a view for StudentViewModel. this is StudentViewModel:
#model MyProject.Models.StudentViewModel
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm ())
{
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.FullName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FullName)
#Html.ValidationMessageFor(model => model.FullName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AddAddressModel.FullAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AddAddressModel.FullAddres)
#Html.ValidationMessageFor(model => model.AddAddressModel.FullAddres)
</div>
<button id="add">add address to list</button>
<input type="submit" value="save in database" />
</fieldset>
please tell me how i can add one or more address on by one in Addresses Property of StudentViewModel and show after this operation to user. finally when i click on the "save in database" button student and his addresses must be inserted in database.
I have in the past submitted nested child entities, but that was always done with an API call and the form was serialized to a JSON object before submission.
Because you only really need multiple FullAddress values, you could change your model and view accordingly (not tested):
Model:
public class StudentViewModel
{
public int StudentId { get; set; }
public string FullName { get; set; }
public string[] Addresses { get; set; }
}
View:
In your view, when you click the 'Add' button, make sure that (through JavaScript) you end up with something like this:
<textarea name="Addresses[]">Some Address 1</textarea>
<textarea name="Addresses[]">Some Address 2</textarea>
etc...
you can try using:
#EditorFor(m => model.Addresses)
this will create template for your address model

ASP .NET MVC4 Adding new items to view and model binding

I create a website for my wife. She's a teacher and she would like to have a possibility to create exercises for their students. The case is that she would like to create for instance the following exercise:
Exercise 1: Fill the sentence using a correct word:
My wife is 30 ............. old
I live in this city for 30 .........
I have the following model:
public class Exercise
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ExerciseType Type { get; set; }
public DifficulityLevel DifficulityLevel { get; set; }
public List<ExerciseItem> Items { get; set; }
public DateTime TimeOfCreation { get; set; }
public DateTime TimeOfModification { get; set; }
}
public class ExerciseItem
{
[Key]
public Guid Id { get; set; }
public string Content { get; set; }
public List<ExerciseItemOption> Options { get; set; }
public ExerciseItemOption CorrectSelection { get; set; }
}
I creates a View for my Exercise. I can fill in the basic properties like Name, Description, Difficulity Level and Type. Then I would like to create a button "Add exercise item". When clicked, a partial view (or something else) should be added dynamically where new ExerciseItem can be provided.
I've tried to following:
I've added a button
#Ajax.ActionLink("Add exercise item",
"AddExerciseItem",
"Exercise", new AjaxOptions() { HttpMethod="GET", InsertionMode = InsertionMode.InsertBefore, UpdateTargetId="ExerciseItems"})
and the appropriate div:
<div id="ExerciseItems"></div>
My action method looks as follows:
public ActionResult AddExerciseItem()
{
return PartialView("ExerciseItem", new ExerciseItem());
}
and the partial view:
#model ElangWeb.Models.ExerciseItem
<fieldset>
<legend>ExerciseItem</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.DisplayNameFor(model => model.Content)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Content, new { style = "width:200px" })
</div>
</fieldset>
It works fine. However when I click button for creating a whole exercise, I do not have ExerciseItem collection in my model:
public ActionResult Create(Exercise exercise)
{
using (PersistanceManager pm = new PersistanceManager())
{
exercise.Id = Guid.NewGuid();
exercise.TimeOfCreation = DateTime.Now;
exercise.TimeOfModification = DateTime.Now;
pm.ExcerciseRepository.Add(exercise);
}
return RedirectToAction("Index");
}
How should I change the code in order to bind my list of added ExerciseItem objects to my model Exercise?
Check out this article about model binding. You basically need to create special names for the exercise items so that they get bound correctly.
e.g. partial:
#model ElangWeb.Models.ExerciseItem
<fieldset>
<legend>ExerciseItem</legend>
<label>content</label>
<input type="hidden" name="ExcersiseItem.Index" value="SomeUniqueValueForThisItem" />
<input type="text" name="ExcersiseItem[SomeUniqueValueForThisItem].Name" value="#Model.Content" />
</fieldset>
You can also look at my answer to this question MVC3 Non-Sequential Indices and DefaultModelBinder. Thanks Yarx for finding it, I was actually trying to find it :)

Edit foreign key table with asp.net mvc and entity framework

I have a entity Kajak:
public class Kajak
{
[HiddenInput(DisplayValue = false)]
[Key]
public int KajakID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<KajakImage> KajakImages { get; set; }
}
Which has a 1 to many relationship to KajakImages:
public class KajakImage
{
[Key]
public int ImageID { get; set; }
public string Name { get; set; }
public string Data { get; set; }
public string MimeType { get; set; }
public int FK_KajakID { get; set; }
[ForeignKey("FK_KajakID")]
public Kajak Kajak { get; set; }
}
I would like to create an edit view, where it is possible to do edit both in the same window.
I can't really wrap my head around it. So far I have come to this:
public ViewResult Edit(int kajakID)
{
Kajak kajak = _kajakRepository.Kajaks.FirstOrDefault(p => p.KajakID == kajakID);
return View(kajak);
}
But I have no idea on how to creater the "editorfor" kajakimages.
#model Timskajakker.Domain.Entities.Kajak
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<h2>
Redigér kajak</h2>
#using (Html.BeginForm("Edit", "AdminKajak"))
{
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
foreach (var kajakImage in Model.KajakImages)
{
#Html.EditorFor(model => model.KajakImages.Name) // doesn't work.. what to do here??
}
<input type="submit" value="Gem" />
#Html.ActionLink("Fortryd og vend tilbage", "Index")
}
I am using asp.net mvc 4 and Entity Framework.
I would like to create an edit option for the Kajak properties except id and the name property from all attached kajakimages
I think the best solution is to create a custom EditorTemplate. You have to create a partial view file with the name KajakImage.cshtml (must be the name of the model to be rendered) in a folder called EditorTemplates under Views/YourControllerName (or in a Shared folder):
#model Timskajakker.Domain.Entities.KajakImage
#Html.HiddenFor(model => model.ImageID)
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
And then just render this template in your Kajak edit view by using:
#using (Html.BeginForm("Edit", "AdminKajak"))
{
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
#Html.EditorFor(model => model.KajakImages)
<input type="submit" value="Gem" />
#Html.ActionLink("Fortryd og vend tilbage", "Index")
}
You don't need to write a foreach loop because ASP.MVC will render the partial view once per item automatically when you pass in a collection of KajakImages.
#Html.HiddenFor(model => model.ImageID) is here to ensure that you get KajakImage objects instantiated by the model binder on post request which have the correct original key value set together with the Name property. (The remaining properties of the KajakImage collection will be invalid/empty/null because you don't have form fields for them in your view.)

What is a better way to display data from multiple view models

I have two view models
public class DataImportViewModel
{
public WccrViewModel wccrVM { get; set; }
public string Status { get; set; }
public string ValidationResult { get; set; }
…
}
public class WccrViewModel
{
public double? WccrId { get; set; }
public string WccrDesc { get; set; }
}
and my view
#model List<Mass.ViewModels.DataImportViewModel>
#using (Html.BeginForm("Validation", "DataImport", FormMethod.Post))
{
<input id="ProcessReshop" type="submit" value="Reshop" />
for (int i = 0; i < 1; i++)
{
<div class="display-field">
#Html.DisplayFor(model=>model[i].wccrVM.WccrId)
</div>
<div class="display-field">
#Html.DisplayFor(model => model[i].wccrVM.WccrId)
</div>
}
A table (not shown) is populated with the DataImportViewModel. I want to have the WccrViewModel displayed on top of the Table. I want to be able to access the WccrViewModel without the for loop...but it shows as a good example. How can I use wccrVM.WccrId without the for loop? Thanks in advance...cheers
How about another viewmodel class that contains both a DataImportViewModel and a WccrViewModel?
Your viewmodel should be tailored to the view, and if the view calls for both those objects, why not create a view model with what it expects?

Resources