Model passed to Partial to View - c#

Been searching around but couldn't find a direct solution to what I'm trying to achieve.
I've tried to include as much as needed but it's a very large project so hopefully you'll get the gist.
Overview:
I have a view model that has several lists of objects within it. I am using two partial views for control over each of the list of objects, one for gathering the list of objects (which is held in a session), and the other for adding a list of said object into the list.
Update:
As per comment - what I am looking to do is as follows - in the Index, fill out the existingIp model info, which is displayed through the addToListPartialView, then it will post to the ListPartialView to update the list through the session, handled backend in the controller, which will in turn display on the Index - the question ultimately is, how do I achieve this?
Problem:
The issue I'm having is once I've added an object, through a partial view, to the object list, another partial view, how do I then pass this back to the main view?
Code:
Controller
public ActionResult AddExistingIp([Bind(Include = "Subnet, Cidr, Mask")]ExistingIp existingIp)
{
if(Session["pa_ipv4Session"] != null)
{
pa_ipv4 pa_ipv4 = (pa_ipv4)Session["pa_ipv4Session"];
if(pa_ipv4.ExistingIps == null)
{
pa_ipv4.ExistingIps = new List<ExistingIp>();
}
pa_ipv4.ExistingIps.Add(existingIp);
ViewBag.pa_ipv4 = pa_ipv4.ExistingIps;
return View("ExistingIpView", ViewBag.pa_ipv4);
}
else
{
pa_ipv4 pa_ipv4 = new pa_ipv4();
Session["pa_ipv4Session"] = pa_ipv4;
pa_ipv4.ExistingIps = new List<ExistingIp>();
pa_ipv4.ExistingIps.Add(existingIp);
ViewBag.pa_ipv4 = pa_ipv4.ExistingIps;
return View("ExistingIpView", ViewBag.pa_ipv4);
}
Index:
#model ViewModel
<div id="ExistingIpList">
#{Html.RenderPartial("ExistingIpView");}
</div>
<div id="addExisting">
#{Html.RenderPartial("AddExistingIp");}
</div>
List Partial
#model IEnumerable<ExistingIp>
#if (Model != null)
{
foreach (var ei in Model)
{
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<span>#ei.Subnet</span>
</div>
<div class="ui-block-b">
<span>#ei.Cidr</span>
</div>
<div class="ui-block-c">
<span>#ei.Mask</span>
</div>
<div class="ui-block-d">
#ei.Id
Delete
</div>
</div>
}
}
Add to list partial:
#using (Html.BeginForm("AddExistingIp", "PA_IPV4"))
{
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<span>
#Html.EditorFor(m => m.Subnet)
#Html.ValidationMessageFor(m => m.Subnet)
</span>
</div>
<div class="ui-block-b">
<span>
#Html.EditorFor(m => m.Cidr)
#Html.ValidationMessageFor(m => m.Cidr)
</span>
</div>
<div class="ui-block-c">
<span>
#Html.EditorFor(m => m.Mask)
#Html.ValidationMessageFor(m => m.Mask)
</span>
</div>
<div class="ui-block-d">
<span>
#Html.EditorFor(m => m.Id)
#Html.ValidationMessageFor(m => m.Id)
</span>
</div>
</div>
<div data-role="main" class="ui-content">
<div data-role="controlgroup" data-type="horizontal">
<input type="submit" id="addExistingIp" cssclass="ui-btn ui-corner-all ui-shadow" value="Add" />
</div>
</div>
}
ViewModel:
public Contact ContactDetails { get; set; }
[Required]
public bool ExistingAddress { get; set; }
public List<ExistingIp> ExistingIps { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string ExistingNotes { get; set; }

You can modify the AddExistingIp to just store the data. And to make a RedirectToAction Index. There you will take the data from Session and pass it to the Model.
[HttpPost]
public ActionResult AddExistingIp([Bind(Include = "Subnet, Cidr, Mask")]ExistingIp existingIp)
{
if(Session["pa_ipv4Session"] != null)
{
pa_ipv4 pa_ipv4 = (pa_ipv4)Session["pa_ipv4Session"];
if(pa_ipv4.ExistingIps == null)
{
pa_ipv4.ExistingIps = new List<ExistingIp>();
}
pa_ipv4.ExistingIps.Add(existingIp);
}
else
{
pa_ipv4 pa_ipv4 = new pa_ipv4();
Session["pa_ipv4Session"] = pa_ipv4;
pa_ipv4.ExistingIps = new List<ExistingIp>();
pa_ipv4.ExistingIps.Add(existingIp);
}
return RedirectToAction("Index");
}
The Index Action will look similar with this, where you take data from Session and use it in your Model
public ActionResult Index()
{
var viewModel = new ViewModel();
// take data from Session
pa_ipv4 pa_ipv4 = Session["pa_ipv4Session"] as (pa_ipv4);
// some verification
// add the list from Session to model
viewModel.ExistingIps = pa_ipv4.ExistingIps;
return View(viewModel);
}
Also, I think your Index View you should at ExistingIpView you should pass the Model to display.
#model ViewModel
<div id="ExistingIpList">
#{Html.RenderPartial("ExistingIpView", Model.ExistingIps);}
</div>
<div id="addExisting">
#{Html.RenderPartial("AddExistingIp");}
</div>

Related

Dynamic MVC Model Server side validation

I have the following view:
#model dynamic
<form class="form-horizontal" id="formDynamicItem" action="/DynamicItem/SaveItem" method="post">
#Html.AntiForgeryToken()
<div class="col-xs-12 buttonBar">
<button type="submit" value="Save" name="submitButton" class="btn">Save</button>
<button type="submit" value="Cancel" name="submitButton" class="btn">Cancel</button>
</div>
<div class="col-lg-6">
<div class="ibox ">
<div class="ibox-content">
#{
foreach (var obj in Model)
{
var kvpObj = (KeyValuePair<string, object>)obj;
var entityProp = (EntityAttributeProperties)kvpObj.Value;
<div class="form-group">
#if (entityProp.IsHiddenField)
{
<input type="hidden" class="form-control" data-val="true" id="#kvpObj.Key" name="#kvpObj.Key" value="#entityProp.Value" />
}
else if (entityProp.IsFormField)
{
var isReadOnly = entityProp.IsReadonly ? "readonly" : "";
IHtmlString validationRules = Html.Raw(string.Empty);
if (entityProp.ValidationRules != null)
{
validationRules = entityProp.ValidationRules;
}
#Html.Label(entityProp.Name, new { #class = labelClass })
<div class="#controlClass">
#switch (#entityProp.Type)
{
//... many cases
default:
<input type="text" class="form-control" id="#kvpObj.Key" name="#kvpObj.Key" value="#entityProp.Value" #isReadOnly #validationRules />
break;
}
</div>
}
</div>
}
}
</div>
</div>
</div>
</form>
#section Scripts {
<script>
$("#formDynamicItem").validate();
</script>
}
And in the controller I get my values using FormCollection:
public ActionResult SaveItem(FormCollection form)
{
...
newValue = typeConverter.ConvertFromString(form[entityAttribute.Name]);
...
}
}
My question is the following:
How can I establish Server-side validation on such dynamic model? Can I use FormCollection somehow? Possibly build dynamic view model somehow? If anyone has experience in this please consider giving a suggestion(answer).
Update: Making Detail page with ViewModel insted of dynamic model
So, After much refactoring I appear to be again stuck with server-side validation:
So now I have this ViewModel:
public class DynamicItemViewModel
{
public Guid Id { get; set; }
public List<EntityAttributeProperties> Properties { get; set; }
}
this detail page:
#model ExactDistillation.Models.DynamicItem.DynamicItemViewModel
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
#using (Html.BeginForm("SaveItem", "DynamicItem", FormMethod.Post, new { #class = "form-horizontal", #id = "formDynamicItem" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(x => x.Id)
<div class="col-xs-12 buttonBar">
<button type="submit" value="Save" name="submitButton" class="btn btn-primary pull-right">Save</button>
<button type="submit" value="Cancel" name="submitButton" class="btn btn-default pull-right cancel">Cancel</button>
</div>
<div class="col-lg-6">
<div class="ibox float-e-margins">
<div class="ibox-title text-webwonders">
<h5>Item</h5>
</div>
<div class="ibox-content">
#{
for (int i = 0; i < Model.Properties.Count; i++)
{
#Html.EditorFor(m => Model.Properties[i], "EntityAttributeProperties", "Properties[" + i + "]")
}
}
</div>
</div>
</div>
}
</div>
</div>
And this is how I define EntityAttributeProperties page:
#model EntityAttributeProperties
<div class="form-group">
#if (Model.IsHiddenField)
{
#Html.HiddenFor(x => x.Value)
}
else if (Model.IsFormField)
{
#Html.Label(Model.Name, new { #class = "col-sm-5 col-md-4 col-lg-3" })
<div class="col-sm-7 col-md-8 col-lg-9">
#switch (Model.Type)
{
--- Many cases
default:
#Html.DynamicTextBoxFor(m => m.Value, null, Model.IsReadonly, Model.ValidationRules)
break;
}
</div>
}
</div>
EntityAttributesProperties looks the following way:
public class EntityAttributeProperties
{
public string Name { get; set; }
public string DisplayName { get; set; }
public object Value { get; set; }
public EntityAttributeDataTypeEnum Type { get; set; }
public short Order { get; set; }
public bool IsFormField { get; set; }
public bool IsReadonly { get; set; }
public bool IsHiddenField { get; set; }
public Dictionary<string,object> ValidationRules { get; set; }
}
So, I am trying to make server-side validation for Model, but I am stuck, because I don't find any elegant solution to my problem, just solutions in which I have to do a lot of hardcoding (which I don't like).
Here is how I receive the form Submit:
public ActionResult SaveItem(DynamicItemViewModel model)
{
List<EntityAttributeExtendedView> entityAttributes = GetItemEntityAttributes();
DataObject dataObject = _dbContext.DataObjectCollection.FirstOrDefault(x => x.Id == model.Id);
if (dataObject != null)
{
JObject json = JObject.Parse(dataObject.Value);
dynamic dynJson = JsonConvert.DeserializeObject(dataObject.Value);
// Possibly loop through all entity attributes and separately make a custom validation ?
// Or somehow create custom Model State for validation ?
}
return View("Detail", model);
}
I will appreciate any suggestion on how to approach the problem with server side validation.
Thanks.
FormCollection is a very raw form of data and cannot be validated easily. I'd suggest you should refactor your view to be able to use ViewModels, else you will have a hard time working with the Data.
I cannot show you the full way but give you some hints:
Create a single View Model class for the View which contains a list of Items (of type EntityAttributeProperties). Let's call it MainViewModel.
Pass this View Model to the view rather then the Dictionary
In your view, use #Html.EditoFor(x => x.Items) to generate the correct HTML. ASP.NET MVC will use the Editor Templates for EntityAttributeProperties type
This is a good moment to create a new view EntityAttributeProperties.cshtml in your View folders EditorTemplates sub-folder
within this item view, you can do all your entityProp.Type switches, but be careful with ID generation, always use #Html.IdFor(...) etc. instead of generating own IDs to keep type safe with your View Model
After some tweaks, your Post Action should be able to receive your view model of Type MainViewModel. If everything went good, the Item's will be filled, even if you used different Controls (hidden fields, text fields, drop-downs...) to populate the Values
From my perspective, only this MVC-safe approach will lead to success in this case
I'm adding another Answer because the Question changed heavily.
A good approach is to use the IValidatableObject Interface. So you add this Interface to your EntityAttributeProperties class and have to override the Method Validate. for simple validation like required fields, you use so called validation Attributes.
Your EntityAttributeProperties class would be decorated like this:
public class EntityAttributeProperties : IValidatableObject
{
public string Name { get; set; }
[Required]
public object Value { get; set; }
...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (... /* some condition, e.g. specific EntityAttributeDataTypeEnum */)
{
// Do some validation
// some other random test
if (.../* something not right... */)
{
results.Add(new ValidationResult("your input was not valid!"));
}
}
return results;
}
}
You might need to also make your DynamicItemViewModel and IValidatableObject and loop through the Items, but sometimes MVC is smart enough to validate sub-items automatically, so you might need this:
public class DynamicItemViewModel : IValidatableObject
{
...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return Items.SelectMany(x => x.Validate(validationContext));
}
}
OK now in your Controller, you basically need to check your ModelState. The automatically generated ModelState Property contains all errors.

Model empty on post

When I submit, my model is empty on post.
Model
public QuizModel()
{
Questions = new List<QuizQuestionModel>();
}
public QuizModel(string quizName)
{
QuizName = quizName;
Score = 0;
IntranetEntities db = new IntranetEntities();
Quiz quiz = db.Quizs.Where(x => x.Name.Equals(quizName, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if (quiz != null)
{
IQueryable<Quiz_Question> questions = db.Quiz_Question.Where(x => x.QuizID.Equals(quiz.ID)).OrderBy(x => x.QuestionNo);
Questions = new List<QuizQuestionModel>();
foreach (Quiz_Question question in questions)
{
QuizQuestionModel q = new QuizQuestionModel();
q.ID = question.ID;
q.Question = question.Question;
q.UserAnswer = null;
q.SystemAnswer = question.Answer;
Questions.Add(q);
}
}
}
public string QuizName { get; set; }
public List<QuizQuestionModel> Questions { get; set; }
public int Score { get; set; }
Controller
[HttpPost]
public ActionResult OSHAQuiz(Models.QuizModel model)
{
if (ModelState.IsValid)
{
bool passed = false;
model.Score = model.Questions.Where(x => x.UserAnswer.Equals(x.SystemAnswer, StringComparison.InvariantCultureIgnoreCase)).Count();
if (!model.Score.Equals(0))
{
double percent = model.Score / model.Questions.Count();
if (percent >= .8)
{
passed = true;
}
}
if (passed)
{
return View("/Views/Quiz/Passed.cshtml");
}
else
{
return View("/Views/Quiz/Failed.cshtml");
}
}
else
{
return View("/Views/Quiz/Quiz.cshtml", model);
}
}
View
#model PAL.Intranet.Models.QuizModel
<script>
$(document).ready(function () {
$("input:checked").removeAttr("checked");
});
</script>
<div class="grid">
<h2>OSHA Quiz</h2>
<hr />
<div class="align-center">
#using (Html.BeginForm("OSHAQuiz", "Quiz", FormMethod.Post, new { id = "formShowLoading" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="row cell">
<div class="example bg-grayLighter" data-text="Directions">
<ul class="simple-list">
<li class="align-left">When you have made your selection for all 20 statements, click on the button 'Submit.'</li>
<li class="align-left">Mark 'True' or 'False' for each statement.</li>
<li class="align-left">You must score 80% (16 correct) to pass.</li>
<li class="align-left">You must fill in your full name to receive credit.</li>
</ul>
</div>
</div>
<div class="row cell">
<div class="row cell">
<div class="panel" data-role="panel">
<div class="heading">
<span class="title">Questions</span>
</div>
<div class="content">
<ul class="numeric-list">
#foreach (var question in Model.Questions)
{
<li>
<table class="table hovered" style="width: 100%;">
<tr>
<td align="left">#question</td>
<td align="right" width="150px">
<div class="align-center">
<label class="align-right input-control radio small-check">
#Html.RadioButtonFor(model => question.UserAnswer, true, new { Name = question.GroupName })
<span class="check"></span>
<span class="caption">True</span>
</label>
<label class="align-right input-control radio small-check">
#Html.RadioButtonFor(model => question.UserAnswer, false, new { Name = question.GroupName })
<span class="check"></span>
<span class="caption">False</span>
</label>
</div>
</td>
</tr>
</table>
</li>
}
</ul>
</div>
</div>
</div>
</div>
<div class="row cell">
<input type="submit" value="Submit" class="button info small-button" />
<input type="reset" value="Reset" class="button primary small-button" />
</div>
}
</div>
</div>
UPDATE
if I use HiddenFor on QuizName, it does come back over on post but the rest of the model is empty.
When iterating over a collection that you want to post back to your model, you can't use foreach; you must use a regular for statement with indexing in order for Razor to generate the correct field names.
#for (var i = 0; i < Model.Questions.Count(); i++)
{
...
#Html.RadioButtonFor(m => m.Questions[i].UserAnswer)
}
Then, your fields will have name attributes in the form of Questions[0].UserAnswer, which the modelbinder will recognize and bind appropriately to your model. As you have it now, with the foreach, the field name is being generated as question.UserAnswer, which the modelbinder has no idea what to do with and discards.
Also, FWIW, accessing your context from within your model entity is a hugely bad idea, and even worse if you're not injecting it. Move that logic out of your entity and utilize a utility class or service instead. Also, look into dependency injection, as your context is one of those things that you want one and only one instance of per request. If you start instantiating multiple instances of the same context, you will have problems.
The problem is model binding is going to attempt to bind your form values to properties on your model. It will not use the constructor on your model that takes the quiz name, it will use the default constructor to instantiate the QuizModel object.
I would consider refactoring this model to remove your EntityFramework dependency and find a new way to populate those values.
You should also call the Dispose() method on IDisposable objects when you're done using them.
My suggestion for how to solve this problem would be to use the QuizModel you currently have to help render your view (i.e. Your quiz questions and possible answer for each question).
Create a seperate ViewModel for quiz submission
public class QuizSubmission
{
public string QuizName { get;set; }
public List<QuizQuestionResponse> Responses { get;set; }
}
public class QuizQuestionResponse
{
public int QuestionId { get;set; }
public int AnswerId { get;set; }
}
In your controller action, you should be binding to a QuizSubmission model.
[HttpPost]
public ActionResult OSHAQuiz(Models.QuizSubmission model)
{
Then you'll be able to perform any actions you need for that quiz submission (ie. data access, validation ).
You'll also need to update your view so that your Html input elements have the correct name attributes that model binding can correctly bind each question and response pair to a QuizQuestionResponse item in your Responses list.

MVC Create returns null object

I'm having a problem in my MVC project. When trying to create an object to add it to the db, it always returns null.
public class ListsModel
{
public EntitiesList EntityList { get; set; }
public List<string> AllGroups { get; set; }
}
public ActionResult Create()
{
ListsModel model = new ListsModel();
model.EntityList = new EntitiesList();
model.AllGroups = managerLists.GetAllListsKeys(); //For droplist
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ListsModel model)
{
if (ModelState.IsValid)
{
model.EntityList.List_CreatedTime = DateTime.Now;
managerLists.AddNewObject(model.EntityList);
return RedirectToAction("Index");
}
return View(model);
}
And a simple cshtml:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>EntitiesList</legend>
<div class="form-group">
#Html.LabelFor(model => model.EntityList.List_EntitityName)
#Html.DropDownListFor(model => model.AllGroups, new SelectList(Model.AllGroups),
new { #class = "form-control" })
<p class="help-block">#Html.ValidationMessageFor(model => model.EntityList.List_EntitityName)</p>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EntityList.List_EntityValue)
<input class="form-control" value="#Model.EntityList.List_EntityValue"/>
<p class="help-block">#Html.ValidationMessageFor(model => model.EntityList.List_EntityValue)</p>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EntityList.List_OrderByNumber)
<input class="form-control" value="#Model.EntityList.List_OrderByNumber"/>
<p class="help-block">#Html.ValidationMessageFor(model => model.EntityList.List_OrderByNumber)</p>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EntityList.List_Comments)
<textarea class="form-control" rows="3">#Model.EntityList.List_Comments</textarea>
<p class="help-block">#Html.ValidationMessageFor(model => model.EntityList.List_Comments)</p>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When it's getting to the "model.EntityList.List_CreatedTime = DateTime.Now;" a null reference exception is thrown.
I tried to change the signature to "public ActionResult Create(ListsModel ListsModel)", as suggested here: Create view is posting null objects
But I got the same results.
Hope you can help me.
I think the problem is the way you define inputs like this:
<input class="form-control" value="#Model.EntityList.List_EntityValue"/>
For ASP MVC can collect form data, inputs should have an Name attribute corresponding with model fields.
Try to generate inputs using the standard:
#Html.TextBoxFor(model => model.EntityList.List_EntityValue)
I suggest you inspect the differences in the html generated (to see how is asp mvc generating inputs).

Passing multiple Collections of data from View to Controller (ASP.NET MVC 5)

In a View i have the next structure ( control of Subject*s for each *Group):
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#for (int i = 0; i < ViewBag.AllGroups.Count; i++)
{
<h4>#ViewBag.AllGroups[i].Code</h4>
<select id="e-#i" multiple="multiple">
#foreach (Subject subject in ViewBag.AllSubjects)
{
<option value="#subject.Name">#subject.Name</option>
}
</select>
}
<input type="submit" value="Generate" class="btn btn-default" />
</div>
}
The question is that how can I retreive this data (I want to receive (1)list of Groups and and I want to get a (2)list of all selected Subjects for each group in my list(1)) in my Controller?
Thank you in advance.
Recommended way is to use strongly typed View Model Object
public class GroupViewModel
{
public string Code { get;set; }
public List<Subject> AllSubjects { get; set; }
}
Pass List as the Model to the Razor view in the controller.
return new View(new List<GroupViewModel>()); // populated one.
Use this list in the View.
#model IList<GroupViewModel>
#for (int i = 0; i < Model.Count; i++)
{
<h4>Model[i].Code</h4>
<select id="e-#i" multiple="multiple">
#foreach (Subject subject in Model[i].AllSubjects)
{
<option value="#subject.Name">#subject.Name</option>
}
</select>
}
There is nothing especial to deal with this situation, except that you have missed the tag name of the select element.
To be exact, all html elements such as select you have used here, should have a name not id (id="e-#i") and all elements are serialized based their names and sent to server. On the other side, at server-side, you should get the posted values which are in a csv formatted (due to multiple ability added you have added to select element)
Solved my problem by simplifying the task. What i had to to: I created new ViewModel for this thing. I replaced tag <select></select> with #Html.ListBoxFor(m => m.Subjects, Model.SubjectItems). I had to create a SubjectItems list in my ViewModel.
Here's the code (Sorry for tons of code: I just want to make everything clear):
My View:
#using System
#using System.Linq
#using TimeTable.GenericRepository
#model TimeTable.Models.GroupViewModel
#{
//it's better to move the next line to a Controller later
ViewBag.GroupId = new SelectList(new GroupRepository().Get().ToList(), "Id", "Code", Model.GroupId);
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_LayoutBootstrap.cshtml";
}
<h2>Index</h2>
<hr />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.GroupId, "Group is: ", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("GroupId", String.Empty)
#Html.ValidationMessageFor(model => model.GroupId)
</div>
</div>
#Html.ListBoxFor(m => m.Subjects, Model.SubjectItems, new { #id = "e", #style = "width:80%; " })
<br /><br />
<input type="submit" value="Generate" class="btn btn-default" />
</div>
}
#* ReSharper disable once Razor.SectionNotResolved *#
#section Scripts {
#Styles.Render("~/Content/select2")
#Scripts.Render("~/bundles/select2")
<script type="text/javascript">
$(function () { $("#e").select2(); });
</script>
}
My Controller:
public ActionResult Generate()
{
return View(new GroupViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Generate(GroupViewModel gvm)
{
var subjects= gvm.Subjects; // <== selected subjects are here
return View();
}
My Model:
public class GroupViewModel
{
public int GroupId{ get; set; }
public Group Group {
get { return new GroupRepository().GetById(GroupId); }
}
public object Subjects { get; set; }
public IEnumerable<SelectListItem> SubjectItems
{
get
{
var items = new SelectList(new SubjectRepository().Get().ToList(), "Id", "Name");
return items;
}
}
}
P.S. Select2 is a custom replacement for selectBoxes, it's not necessary, but I like it: http://ivaynberg.github.io/select2/

Partial View to Add Data

So here, I need to add comment for video in one view.
I have the main view code like this that displaying video and comment for that video.
<!-- language: C# -->
#model AzureMediaPortal.ViewModels.ViewModelWatch
#{
ViewBag.Title = "Watch";
}
<div id="videoPlayer">
</div>
<h2>#Html.DisplayFor(model => model.media.Title)</h2>
<h3> By #Html.DisplayFor(model => model.media.UserId) at #Html.DisplayFor(model => model.media.UploadDate) </h3>
#Html.HiddenFor(model => model.media.Id)
#Html.HiddenFor(model => model.media.AssetId)
#Html.HiddenFor(model => model.media.FileUrl, new { id = "fileUrl" })
<div class="display-label" style="font-weight:bold">
#Html.DisplayNameFor(model => model.media.Description)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.media.Description)
</div>
<br />
<div class="display-label" style="font-weight:bold">
#Html.DisplayName("Category")
</div>
<div class="display-field">
#Html.DisplayFor(model => model.media.Category.CategoryName)
</div>
<h3>Comments</h3>
#foreach (var item in Model.comment)
{
<div class="display-label" style="font-weight:bold">
#item.UserId
</div>
<div class="display-field">
#item.Content
</div>
}
#Html.Partial("Post",new AzureMediaPortal.ViewModels.ViewModelWatch())
#section Scripts {
<script src="~/Scripts/playerframework.min.js"></script>
<script src="~/Scripts/media-player.js"></script>
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
mediaPlayer.initFunction("videoPlayer", $("#fileUrl").val());
</script>
}
and this the partial view
#model AzureMediaPortal.ViewModels.ViewModelWatch
#{
ViewBag.Title = "Post";
}
<h2>Add Comment</h2>
#Html.HiddenFor(model => model.cmnt.MediaElement.Id)
#using (Html.BeginForm("Post","Home",FormMethod.Post)) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Add Comment</legend>
<div class="editor-label" style="font-weight:bold">
#Context.User.Identity.Name
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.cmnt.Content)
#Html.ValidationMessageFor(model => model.cmnt.Content)
</div>
<p>
<input type="submit" value="Post" />
</p>
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
this is my ViewModel
public class ViewModelWatch
{
public MediaElement media { get; set; }
public List<Comment> comment { get; set; }
public Comment cmnt { get; set; }
}
and this is my controller
public ActionResult Watch(int id)
{
ViewModelWatch vm = new ViewModelWatch();
vm.media = _repository.GetMedia(id);
vm.comment = _repository.GetMediaComment(id);
return View(vm);
}
public ActionResult Post()
{
return View();
}
[HttpPost]
public ActionResult Post(Comment comment, int id)
{
if (ModelState.IsValid)
{
comment.UserId = User.Identity.Name;
comment.MediaElement.Id = id;
db.Comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Watch");
}
return View();
}
I need to pass data from partial view and save it to database include the media.Id to know that comment inserted for the video.
Thanks so muchhh
putting scripts on a partial view is generally bad practice. Script gets inserted into the middle of the view. For saving information from a partial view I use ajax calls. To do that add a class to your post button
<input type="button" class="btnSubmit" value="Post" />
then in your script on the main page
$(document).on('click', '.btnSubmit', function(){
$.ajax({
url: '#Url.Action("Action", "Controller")',
cache: false,
async: true,
data: {
//put the data that you want to save from the partial here
id: $('#hiddenID').val(),
content: $('#Content').val()
},
success: function (_result) {
//can add something here like an alert, close a popup something along those lines
}
});
});
just make sure the inputs on your controller match exactly the names that you have defined here
[HttpPost]
public ActionResult Action(int id, string content){
//database call to save the fields
//see here for an example of returning json http://stackoverflow.com/questions/1482034/extjs-how-to-return-json-success-w-data-using-asp-net-mvc
return Json(json);
}
I would suggest using #Html.Action and get the create form from there. Also, once the comments are stored you can redirect to the parent action. That should automatically update the comments list.
But that would be a bad idea.
Rather you can put get your comments by calling action from ajax ($.ajax) and use pure.js to replace the older ones with the new comment list.

Categories

Resources