Pass object to partial view - asp.net-mvc

I have this controller which fetches an object that I would like to render in a partial view:
public ActionResult EditPhoto(string id)
{
var photo = RavenSession.Load<ContentPage>(id);
return View("_editPhoto");
}
Its the photo I would like to pass to my partial view.
I have this in my view:
#{
Html.RenderPartial("_editPhoto" );
}
How will I go about to pass my photo into the partial view in order for it to render in its parent?
EDIT:
This is how I pass the object to the controller:
#foreach (var item in Model.Photographys)
{
<li class="span3" style="text-align: center">
<div class="thumbnail thumbnail-1">
#Html.ActionLink(item.Name,
"EditPhoto", // <-- ActionMethod
new { id = item.Id }, // <-- Route arguments.
null // <-- htmlArguments .. which are none. You need this value
// otherwise you call the WRONG method ...
// (refer to comments, below).
)
<h3 style="margin-bottom: 10px;">#item.Name</h3>
<div class="">
<div class="">
<img src="#item.ImgUrl" alt="" style="visibility: visible; opacity: 1;">
</div>
</div>
<section>
<p>#item.Description</p>
Read More
<p>#item.IsAccordion</p>
</section>
</div>
</li>
}
There seems a problem with this line however:
#{
Html.RenderPartial("_editPhoto" , Model);
}
Model gets underlined explaining that the Model passed into it (Photo) is not the right one..It seems that _editPhoto inherits the same Model as its parent maybe?
I managed to do this in the view:
#{
var i = new Photography();
Html.RenderPartial("_editPhoto", i);
}
The problem now is that the partialView gets rendered in a new window and not in its parent as I want it to.
UPDATE
Im gonna give this one last go, wont create a new question:
This is my controller passing a photo to a partial view:
public ActionResult EditPhoto(string id)
{
var photo = RavenSession.Load<ContentPage>(id) as Photography;
return PartialView("_editPhoto", photo);
}
My "mainview" contains this code to render the partial view with the photo getting sent to it:
<div class="form-control">
#{
var model = new Photography();
Html.Partial("_editPhoto",model);
}
</div>
This opens up a new window where my new photo shows up. I would like it to get rendered inside of its parents view the same way as it gets rendered automaticaly when i first visit the page...

Your controller action method should be:
public ActionResult EditPhoto(string id)
{
var photo = RavenSession.Load<ContentPage>(id);
return View("EditPhoto",photo);
}
and your "EditPhoto" view should be:
#{ Html.RenderPartial("_editPhoto",Model); }
Update
Your controller action method should be:
public ActionResult Photos()
{
return View();
}
public ActionResult EditPhoto(string id)
{
var photo = RavenSession.Load<ContentPage>(id);
return View(photo);
}
your "EditPhoto" should be view (not a partialview), on link click, "EditPhoto" action method is called and it returns the "EditPhoto" view

This questions seems related to one of your previous question.
Controller should be as follows;
public ActionResult EditPhoto(string id)
{
var photo = RavenSession.Load<ContentPage>(id);
return PartialView("_editPhoto", photo);
}
In your partial view _editPhoto, you can have the following code. I assume photo variable is a Photography object.
#model aPhoto_web.Models.AdminPages.Photography
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Photography</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
</div>
}
Thanks!

Related

MVC Variable Length Collection Model is Null

I have a partial view for editing a collection (most of which I've truncated here):
#using CustomSurveyTool.Models
#model Basiclife
<div class="editorRow">
#using (Html.BeginCollectionItem(""))
{
<div class="form-horizontal">
<div class="row">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="col-md-2">
#Html.LabelFor(model => model.Plantype, htmlAttributes: new { #class = "control-label" })
#Html.EditorFor(model => model.Plantype, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Plantype, "", new { #class = "text-danger" })
</div>
<div class="col-md-1">
X
</div>
</div>
</div>
}
</div>
Which is part of a wrapper view:
#using CustomSurveyTool.Models
#model IEnumerable<Basiclife>
#{
ViewBag.Title = "CreateBasicLifeResponse";
}
<h2>Basic Life Insurance Plans</h2>
<div id="planList">
#using (Html.BeginForm())
{
<div id="editorRows">
#foreach (var item in Model)
{
#Html.Partial("_BasicLifeResponse", item)
}
</div>
#Html.ActionLink("Add", "BasicLifeResponse", null, new { id = "addItem", #class = "button" });
<input type="submit" value="submit" />
}
</div>
Which posts back to this controller:
[HttpPost]
public ActionResult CreateBasicLifeResponse(IEnumerable<Basiclife> model)
{
foreach(var item in model)
{
string currentUserId = User.Identity.GetUserId();
Response targetresponse = db.response.FirstOrDefault(x => x.Userid == currentUserId);
int responseid = targetresponse.Id;
item.ResponseId = responseid;
db.basiclife.Add(item);
db.SaveChanges();
}
List<Basiclife> basiclife = new List<Basiclife>();
return View(model);
}
I am getting a NullReferenceException on the foreach(var item in model) after submitting the form. What could be causing this?
You need to follow the MVC naming convention which involves using a for loop with an index instead of foreach in your Razor view. Check out this article:
How to pass IEnumerable list to controller in MVC including checkbox state?.
I would recommend the Editor Template approach described in the article, it is most similar to your current PartialView approach.

How do I get form values to controller

I'm working on our new startpage that will show a filter and a list of news posts. Each news post, and each newslist can be tagged with several business areas.
The newslist will contain news according to:
the settings of the newslist that can be altered by admins.
then filtered by the filter form on the page.
and, if the filter is empty but the user is logged in, filtered by the users preferences.
When the view IndexSecond is loaded, the filter gets its choosable business areas according to its newslist. My problem is that I don’t know how to get the selected business areas from the EditorFor to end up in the model.filteredBAs that is passed to IndexSecondFiltered?
When I come to a breakpoint in IndexSecondFiltered, the model is allways null.
In the view IndexSecond
#model Slussen.BLL.Models.PostFilterListModel
...
#using (Html.BeginForm("IndexSecondFiltered", "Home", new { model = model }))
{
#Html.HiddenFor(model => model.filteredBAs)
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="col-sm-2">Business area</div>
<div class="col-md-10">
#Html.EditorFor(model => Model.newslistModel.BusinessAreas,
new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Go" class="btn btn-primary orange" />
</div>
</div>
}
In HomeController
public ActionResult IndexSecond()
{
//Known user?
int? uo = null;
if (User.Identity.IsAuthenticated)
uo = CurrentUser.UserOrganization.Id;
return View(
_queryDispatcher.Execute<PostFilterListModel>(
new GetFilteredNewsListById(1, uo, "", 1,
new System.Collections.Generic.List<int>(),
new System.Collections.Generic.List<int>())));
}
[HttpPost]
public ActionResult IndexSecondFiltered(PostFilterListModel model)
{
//Known user?
int? uo = null;
if (User.Identity.IsAuthenticated)
uo = CurrentUser.UserOrganization.Id;
return View(
_queryDispatcher.Execute<PostFilterListModel>(
new GetFilteredNewsListById(1, uo, "", 1,
new System.Collections.Generic.List<int>(), model.filteredBAs)));
}
I got help from a collegue.
I didn't need the [HttpPost] ActionResult IndexSecondFiltered at all.
When I replaced this
#using (Html.BeginForm("IndexSecondFiltered", "Home"), new { model = model })
with this
#using (Html.BeginForm("IndexSecond", "Home"))
the model was passed to the controller along with IsSelected-status

MVC controller.redirect doesn't redirect

I'm passing a string to Redirect but the controller is not sending the browser to the appropriate location.
The string is: "/Admin/SystemSecurity/_PermissionDetail/1"
The code is:
public ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
// Code get's here, but seems to go to /Submission/Index
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Submission");
}
In this case, the method that calls RedirectToLocal is _Login in the SubmissionController:
[ChildActionOnly]
public ActionResult _Login(string returnUrl)
{
if (Request.Cookies["UserName"] != null && !string.IsNullOrEmpty(Request.Cookies["UserName"].Value))
{
var loginModel = new Login { Email = Request.Cookies["UserName"].Value, ReturnUrl = returnUrl};
return PartialView(loginModel);
}
return PartialView();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> _Login(Login model, string returnUrl)
{
var isLoggedIn = UserLogin(model.Email, model.Password);
if (!isLoggedIn)
{
TempData["ErrorMessage"] = "Invalid email address or password.";
return RedirectToAction("Index", new { returnUrl = returnUrl });
}
// I make the call here, the values is correct here.
return RedirectToLocal(returnUrl);
}
Here's the Index method, also in SubmissionController:
public ActionResult Index(string message, string returnUrl)
{
IsAuthenticated();
if (!string.IsNullOrEmpty(message) )
AddMessage(message);
if (!string.IsNullOrEmpty((string)TempData["ErrorMessage"]))
{
AddError((string)TempData["ErrorMessage"]);
}
ViewBag.ReturnUrl = returnUrl;
return View();
}
After the POST _Login RedirectToLocal, the main Index method gets called again. Not sure who/what calls it. Probably something simple I'm missing.
For clarification I'm posting more of my View data here:
/Submission/Index:
#{
Layout = "~/Views/Shared/_Home.cshtml";
}
<div>
<p>...</p>
</div>
/Shared/_Home
#using PublicationSystem.ViewModels
#{
ViewBag.Title = "_Home";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container-fluid home-banner">
<!--Content from Index Page -------------------------------------->
<div class="clearfix"></div>
<div class="container">
<div class="row">
<div class="col-sm-4 col-md-3 col-lg-3">
<div class="left-side-blue">
...
</div>
</div>
<div class="col-sm-8 col-md-9 col-lg-9">
#{ Html.RenderPartial("_ErrorMessages"); }
#if (!ViewBag.IsAuthenticated)
{
Html.RenderAction("_Login", new { returnUrl = ViewBag.ReturnUrl });
}
else
{
<div class="hp-nav-boxes">...</div>
}
</div>
</div>
</div>
...
</div>
/Shared/_Login:
#model PublicationSystem.ViewModels.Login
<div class="login-box">
<div class="row">
#using (Html.BeginForm("_Login", "Submission", new { returnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "col-sm-6 col-md-4 col-lg-4 pull-right custm-login ipadsm4" }))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control", #placeholder = "Email" })
#Html.ValidationMessageFor(m => m.Email, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.PasswordFor(m => m.Password, new { #class = "form-control", #placeholder = "Password" })
#Html.ValidationMessageFor(m => m.Password, "", new {#class = "text-danger"})
</div>
<button type="submit" class="btn btn-default pull-right">
Login <span aria-hidden="true" class="glyphicon glyphicon-play"></span>
</button>
<div class="clearfix"></div>
}
</div>
The login logic works and the user can get logged in. It's just this redirect that's messing up.
You're calling _Login a partial, but partials don't have associated actions. It might be a child action, but you can't post to child actions. As result, _Login is just a standard action, and unless you post to it directly via something like:
#using (Html.BeginForm("_Login"))
{
...
}
And then submit that form on the resulting page, the action will never be hit.
Assuming you're actually doing it that way already, and just confused about the terminology, then the next place to look is at the inconsistency in return URL variables. Your action accepts returnUrl as a param, but you're only using that if the user is not logged in, where you redirect to Index with that return URL. However, if the user is logged in, then you're calling RedirectToLocal with model.ReturnUrl instead.

Pass model Id to partialView in order to edit

I have a list of Photography objects that I pass into a view:-
Public class Photography
{
public Photography()
{
Name = "";
Description = "";
Category = "";
ImgUrl = "";
IsAccordion = false;
}
public string Name { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public string ImgUrl { get; set; }
public bool IsAccordion { get; set; }
}
In my view I loop through the list like this:
#foreach (var item in Model.Photographys)
{
<li class="span3" style="text-align: center">
<div class="thumbnail thumbnail-1">
<h3 style="margin-bottom: 10px;">#item.Name</h3>
<div class="">
<div class="">
<img src="#item.ImgUrl" alt="" style="visibility: visible; opacity: 1;">
</div>
</div>
<section>
<p>#item.Description</p>
Read More
<p>#item.IsAccordion</p>
</section>
</div>
</li>
}
What I want to do is to have a partial view that lets me edit the properties of photo i click. I have created a partial view using the scaffoldoption "Edit"..It looks like this:
#model aPhoto_web.Models.AdminPages.Photography
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Photography</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
}
etc etc...
Most partialViews I read about gets rendered directly when the parentview gets rendered..That is not what I want...I only want the partialView to appear when I somehow pass my Photo-object to it.
I know this is a "big" question but if anyone can point me in the right direction it´d be great! Thanks.
EDIT to clarify:
Have a look at this code where I have added an "RenderPartial" at the bottom of the loop.
#foreach (var item in Model.Photographys)
{
<li class="span3" style="text-align: center">
<div class="thumbnail thumbnail-1">
<h3 style="margin-bottom: 10px;">#item.Name</h3>
<div class="">
<div class="">
<img src="#item.ImgUrl" alt="" style="visibility: visible; opacity: 1;">
</div>
</div>
<section>
<p>#item.Description</p>
Read More
<p>#item.IsAccordion</p>
</section>
</div>
#{
Html.RenderPartial("_editPhoto", item);
}
</li>
}
This renders a partial-view for every item in the loop of course. I would like a method that passes the object I click to the partial...
EDIT:
public ActionResult EditPhoto(string id)
{
var photo = RavenSession.Load<ContentPage>(id) as Photography;
return PartialView("_editPhoto", photo);
}
First you have to add a controller method which takes Photography Name/ID (It's better if you can add ID property to Photography class) as argument and returns the Partial view you created.
Then when you click the photo you can use Jquery to pass the Photography Name/ID to created controller method and you can display the result using popup or within a particular element (e.g. DIV) inside your page.
EXAMPLE
When you loop Photography objects inside your view you can add a class and ID property to your img tag as follows;
<img src="#item.ImgUrl" alt="" class="photography-image" data-imgId="#item.ID" style="visibility: visible; opacity: 1;">
Then using jquery and jquery UI you can open a dialog box to display the partial view. Check the sample code below.
$( ".photography-image" ).click(function() {
e.preventDefault();
$("<div></div>")
.addClass("dialog")
.appendTo("body")
.dialog({
close: function () { $(this).remove() },
modal: true,
height: 500,
width: 500
})
.load("/controler/method?photographyId=" + $(this).data("imgId"));
});
Thanks!

There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'CategoryID

I have found a lot of similar threads on this problem. But whatever I do, it never works for me.
The ONLY thing I want to achieve right now, is to fill a dropdown list with database values in a partial view, that's within a partial view. That is all, and this is driving me absolutely nuts. A user should be able to chose a category name in the dropdown list.
Here's my Controller Class:
public class AttributeController : Controller
{
private readonly IRepository<ClassLibrary.Entities.Attribute> _aRepository;
private readonly IRepository<Category> _cRepository;
public AttributeController() : this(new Repository<ClassLibrary.Entities.Attribute>(), new Repository<Category>())
{
}
public AttributeController(IRepository<ClassLibrary.Entities.Attribute> repo, IRepository<Category> repository)
{
_aRepository = repo;
_cRepository = repository;
}
//
// GET: /Attribute/
public ActionResult Index()
{
var attributes = _aRepository.GetAll();
var attributeViewModels = new List<AttributeViewModel>();
foreach (ClassLibrary.Entities.Attribute attribute in attributes)
{
var viewModel = new AttributeViewModel();
viewModel.Id = attribute.Id;
viewModel.AttributeName = attribute.Name;
attributeViewModels.Add(viewModel);
}
return View(attributeViewModels);
}
//
// GET: /Attribute/Details/5
public ActionResult Details(int id)
{
return View();
}
//
// GET: /Attribute/Create
public ActionResult Create()
{
ViewData["CategoryID"] = new SelectList(_cRepository.GetAll().ToList(), "Category_id", "Category");
IEnumerable<SelectListItem> items = _cRepository.GetAll().Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
});
ViewData["CategoryID"] = items;
return View();
}
//
// POST: /Attribute/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
And here's the index View:
#model IEnumerable<GUI.Models.AttributeViewModel>
#{
ViewBag.Title = "Attributes";
}
<div>
#Html.Partial("~/Views/Attribute/Create.cshtml", new GUI.Models.AttributeViewModel())
</div>
<h2>All existing Attributes</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.AttributeName)
</th>
<th></th>
</tr>
#if(Model != null)
{
foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.AttributeName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
}
</table>
Here's the partial View Within the index view:
#model GUI.Models.AttributeViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Create an attribute for this Category</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.Partial("~/Views/Attribute/CategoryPartial.cshtml", new GUI.Models.CategoryViewModel())
<div class="form-group">
#Html.LabelFor(model => model.AttributeName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AttributeName)
#Html.ValidationMessageFor(model => model.AttributeName)
</div>
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
And finally CategoryPartialView within the partial view that will contain the DropDown List where the user should be able to select a category Name.
#model GUI.Models.CategoryViewModel
#using GUI.Controllers
#using System.Collections.Generic
#{
//var categories = (IEnumerable<GUI.Models.CategoryViewModel>)ViewData["categories"];
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CategoryID", (IEnumerable<SelectListItem>)ViewData["CategoryID"])
</div>
</div>
}
The exception ALWAYS occurs when I'm debugging the application and it points to the #Html.DropDownList part. Claiming that "There is no ViewData item of type 'IEnumerable' that has the key 'CategoryID".
#Html.DropDownList("CategoryID", (IEnumerable<SelectListItem>)ViewData["CategoryID"]
I'm sorry for posting so many code blocks. But I would really like to solve this.I've beaten my head against the wall for hours with frustration, and any help would be greatly appreciated.
When you use #Html.Partial("~/Views/Attribute/CategoryPartial.cshtml", new GUI.Models.CategoryViewModel()) you are directly instantiating the partial view. The controller does not get called and is not responsible for generating the HTML markup string. Additionally, you are passing an empty view model (new GUI.Models.CategoryViewModel()). Since the controller does not get called, it does not get the data for the drop down list and, obviously, does not save it in your ViewData.
Use the #Html.Action helper instead:
<div class="form-group">
#Html.Action("Create", "Attribute")
<div class="form-group">
Change the Create action like this:
//
// GET: /Attribute/Create
public ActionResult Create()
{
ViewData["CategoryID"] = new SelectList(_cRepository.GetAll().ToList(), "Category_id", "Category");
IEnumerable<SelectListItem> items = _cRepository.GetAll().Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
});
ViewData["CategoryID"] = items;
return PartialView("CategoryPartial", new GUI.Models.CategoryViewModel());
}
Note: I don't know your entire structure, but I might be best to use this approach and to return a Lis<SelectListItem> model.
when calling:
#Html.Partial("~/Views/Attribute/Create.cshtml", new GUI.Models.AttributeViewModel())
you call the view directly with out passing throw the controller so the model you have there is an empty model!
try pass throw the controller to create you'r model and send it back as a view:
#Html.RenderPartial("YOUR CONTROLLER",model);

Resources