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.
Related
I'm trying to implement dropboxes that can appear prefilled, and can grow dynamically when you press a button. I started with a basic dropdown box implementation that doesn't grow dynamically. This is my controller+DTO code snippet:
public class TaskDTO
{
public string TaskTemplateName { get; set;}
}
public IActionResult Create()
{
ViewData["TaskTemplateId"] = new SelectList(_context.TaskTemplates, "Id", "Name");
return View();
}
public async Task<IActionResult> Create([Bind("Id,TaskTemplateName")] TaskDTO task)
{
if (ModelState.IsValid)
{
//Do some stuff
}
ViewData["TaskTemplateId"] = new SelectList(_context.TaskTemplates, "Id", "Name", task.TaskTemplateName);
return View(task);
}
This is my create.cshtml razor code:
<h2>Create</h2>
<h4>TemplateTask</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="TaskTemplateName" class="control-label"></label>
` `<select asp-for="TaskTemplateName" class="form-control" asp-items="ViewBag.TaskTemplateId"></select>
</div>
Then I looked around and found this link that shows how to create dynamic forms that can grow. I tried to combine that idea with dropdown boxes. Based on that tutorial, here's my new DTO+controller code:
public class TaskTemplateDTO
{
public string TaskTemplateName { get; set; }
}
public class TaskDTO
{
public List<TaskTemplateDTO> TaskTemplateNames { get; set; }
}
public IActionResult Create()
{
var vm = new TaskDTO() { };
ViewData["TaskTemplateId"] = new SelectList(_context.TaskTemplates, "Id", "Name");
return View(vm);
}
public async Task<IActionResult> Create([Bind("Id,TaskName,TaskTemplateNames,ParentTasks,IsBasicTask,EstimatedTime,RequiredResources")] TaskDTO task)
{
if (ModelState.IsValid)
{
//Do some stuff
}
ViewData["TaskTemplateId"] = new SelectList(_context.TaskTemplates, "Id", "Name", task.TaskTemplateNames);
return View(task);
}
here's my extra EditorTemplates razor, TaskTemplateDTO.cshtml:
#model Namespace.TaskTemplateDTO
<select asp-for="TaskTemplateName" class="TaskTemplate" asp-items="ViewBag.TaskTemplateId"></select>
This is my create.cshtml razor code:
<h2>Create</h2>
<h4>TemplateTask</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<table class="table table-bordered" id="TaskTemplateTable">
<tr>
<th>Task Templates</th>
<th><button type="button" name="add" id="btn_AddTaskTemplate" class="btn btn-success btn-sm add"><span class="glyphicon glyphicon-plus"></span></button></th>
#Html.EditorFor(f => f.TaskTemplateNames)
</tr>
</table>
</div>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$("#btn_AddTaskTemplate").click(function () {
var i = $(".TaskTemplate").length;
var html = '';
html += '<tr>';
html += '<td><select type="text" name="TaskTemplateNames[' + i + '].TaskTemplateName" class="TaskTemplate" /></td>';
html += '<td></td></tr>';
});
$('#TaskTemplateTable').append(html);
</script>
}
The code above adds the new dropdowns on click, but the dropdowns aren't prefilled with data, what have I done wrong?
You will need to have the select options that you want in the new controls to be available to your javascript method. There are a few ways to do it, the most straightforward would be to hide it in your html page in a hidden select control.
Your CSHTML:
<h2>Create</h2>
//Added the hidden HTML control here
#Html.DropDownList("hidden-select",
new SelectList((IEnumerable) ViewData["TaskTemplateId"]),
null, new Dictionary<string, object>
{
{ "id", "task-template-names" },
{ "style", "display: none" }
})
<h4>TemplateTask</h4>
<hr />
<div class="row">
<div class="col-md-4">
////other things you were doing
This will render in your html as a prepopulated hidden template from which you can build new dropdowns with options on button click.
$("#btn_AddTaskTemplate").click(function () {
var i = $(".TaskTemplate").length;
var html = '';
html += '<tr>';
html += '<td><select type="text" name="TaskTemplateNames[' + i + '].TaskTemplateName" class="TaskTemplate">';
html += document.querySelector("#task-template-names").innerHTML;
html += '</select></td>';
html += '<td></td></tr>';
});
I wasn't able to test this code because the setup is beyond my scope here, so you might need to tweak it a little bit. If you encounter any difficult errors, let me know.
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>
Situation: In my C#/MVC 4 solution I am employing a view with a partial view within. The view is a form with a submit button. The partial view is with a div that is hidden, but can be displayed if the checkbox is selected.
Issue: If the partial view is hidden, the submit works normally. If the partial view is not hidden the submit causes the page to become unresponsive, if one waits the 3 plus minutes or so the submit eventually works as expected.
The code is below. Thank you in advance for your consideration. I am a novice developer, therefore all comments, suggestions and critiques are welcome.
Code:
Model
namespace MyModels
{
public class MainModel
{
public SelectListItem Things { get; set;}
public IEnumerable<OtherModel> MoreThings { get; set;}
}
}
View
//named MyView
#model MyModels.MainModel
#using MyModels
#if (Model != null){
using (Html.BeginForm("MyViewName", "MyControllerName", FormMethod.Post, new { id = "view-form" }))
{
#Html.LabelFor(model => model.things)
#Html.DropDownList("", (Selectist)ViewBag.things)
#Html.ValidationMessageFor(model => model.field1)
#Html.CheckBoxWithLabel("aNameAttribute", Model.valueAttribute.ToString(), "anIdAttribute", Model.valueAtttribue ==1, "aLabel", "a_Toggle_Class")
<div class="treeview" style="display: none;">
<fieldset>
<legend>Title</legend>
//view causing issues replaces the div below
<div id="replacedDiv"></div>
</fieldset>
</div>
<p>
<input type="submit" value="Submit" />
</p>
}
}
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: "/MyController/MyPartialView",
contentType: "application/html; charset=utf-8",
cache: "false",
type: "GET",
datatype: "html"
})
.success(function (result) {
$('#replacedDiv").html(result);
})
});
</script>
Partial View
//named _MyPartialView
#model MyModels.MainModel
#using MyModels
#foreach (var moreThings in ViewBag.moreThings)
{
<div id="replacedDiv">
<label>
<input type="checkbox" id=#moreThings.id value=#moreThings.name />#moreThings.name </label>
</div>
}
Controller
namespace Main.Controllers
{
public class MyController
{
[HttpGet]
public ActionResult Index(MainModel model)
{
return View(model);
}
public ActionResult MyView()
{
var model = new MainModel();
return View(model);
}
public ActionResult MyPartialView(MainModel model)
{
<OtherModel> moreThings = BLotherModel.GetMoreThings();
ViewBag.moreThings = moreThings;
return PartialView("_MyPartialView", promotion);
}
[HttpPost]
public ActionResult MyView(FormCollection collection)
{
MainModel model = new MainModel();
return SaveModel(model);
}
}
}
In your ajax you are using:
$('#replacedDiv").html(result);
But your partial view contains <div id="replacedDiv"> that are generated in a loop
replace your partial view code with :
#foreach (var moreThings in ViewBag.moreThings)
{
<label>#moreThings.name </label>
<input type="checkbox" id=#moreThings.id value=#moreThings.name />
}
and it should be OK
I've created a page that uses entity framework to retrieve/Display info in the database I've also got create functionality in a partial view on the same page but it doesn't save/insert data.
Here is my Home View:
#model IEnumerable<TerminalHost.Models.buildingInfo>
#{
ViewBag.Title = "Home Page";
}
<h3>We suggest the following:</h3>
<ol class="round">
<li class="one">
<h5>Please begin creating your network structure:</h5> <button id="modal-opener">Add a new building</button>
</li>
<li class="two">
</li>
</ol>
<div class="Container">
<div class="Box1">
#foreach (var item in Model)
{<div>
<h4 class="Heading">#item.buildingName</h4>
<h4 class="Heading">#item.buildingNumber</h4>
<p>#item.buildingDesc1</p>
<p>#item.buildingDesc2</p>
<p>#item.buildingDesc3</p>
</div>
}
</div>
</div>
<div id="Modal" title="Building Details">
#Html.Partial("buildForm", new TerminalHost.Models.buildingInfo())
</div>
Here is my partial View:
#model TerminalHost.Models.buildingInfo
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>buildingInfo</legend>
<div class="editor-label">
#Html.LabelFor(model => model.buildingNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.buildingNumber)
#Html.ValidationMessageFor(model => model.buildingNumber)
</div>
....
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
And This is my controller:
public class HomeController : Controller
{
thDB _db = new thDB();
public ActionResult building()
{
var buildingModel = _db.buildings.ToList();//you have to remove the FirstOrDefault() extension method
return View(buildingModel);
}
public ActionResult buildForm()
{
return PartialView();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult buildForm(buildingInfo buildinginfo)
{
if (ModelState.IsValid)
{
_db.buildings.Add(buildinginfo);
_db.SaveChanges();
return RedirectToAction("Index");
}
return PartialView(buildinginfo);
}
protected override void Dispose(bool disposing)
{
if(_db != null)
{
_db.Dispose();
}
base.Dispose(disposing);
}
}
I've attempted to replicate the same code as the scaffold template that is created to do the CRUD functions but they are separated out into different views whereas I want them all on one page.
What you need it's to tell your partial view the Action to call.
Could you change this line:
#using (Html.BeginForm())
To
#using (Html.BeginForm("builForm","Home"))
And in your Action:
HttpPost]
[ValidateAntiForgeryToken]
public ActionResult buildForm(buildingInfo buildinginfo)
{
if (ModelState.IsValid)
{
_db.buildings.Add(buildinginfo);
_db.SaveChanges();
return RedirectToAction("Index");
}
return PartialView(buildinginfo);//instead of return this you can redirect to Index so you can see the update. Just a suggestion
}
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/