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
Related
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.
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.
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!
I have the following model:
public class Photo
{
public int PhotoId { get; set; }
public byte[] ImageData { get; set; }
public DateTime DateUploaded { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
}
I would like the user to be able to enter the details for the photo then post the model the the controller. My controller action is as follows:
[HttpPost]
public ActionResult Create(WilhanWebsite.DomainClasses.Photo photo)
{
if (ModelState.IsValid)
{
photo.DateUploaded = DateTime.Now;
_context.Photos.Add(photo);
_context.SaveChanges();
return RedirectToAction("Index");
}
//we only get here if there was a problem
return View(photo);
}
My view is as follows:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Photo</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.ImageData, new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="uploadImages" class="input-files" />
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateUploaded, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateUploaded)
#Html.ValidationMessageFor(model => model.DateUploaded)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IsActive, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.IsActive)
#Html.ValidationMessageFor(model => model.IsActive)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
The view is displayed ok and allows the user to select a file from their local disk and enter the other model details.
My problem is that although the model is posted to the controller ok, the Description, Date and IsActive flags are populated ok - the Image data is null.
Could anyone please let me know what I need to change so that the byte array for the photo is included in the model posted to the controller?
The file input in your view has a name uploadImages. I can't see a property with this name in your view model. You seem to have some ImageData property which is a byte array, but there doesn't seem to be a corresponding input field with this name in your view.
This explains why you get null. You could make this work by respecting the convention. So for example if you intend to have such an input field in your view:
<input type="file" name="uploadImages" class="input-files" />
then make sure that you have a property on your view model with the same name. And of course of type HttpPostedFileBase.
public HttpPostedFileBase UploadImages { get; set; }
Also in your view make sure you are setting the proper content type of multipart/form-data:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
You probably might want to go through the following blog post to better familiarize yourself with the basics of how uploading of files work in ASP.NET MVC. I've also written a similar answer here that you might consult.
So once you add the HttpPostedFileBase property with the UploadImages name in your view model you could adapt your controller action to read the byte array and store it your ImageData property:
[HttpPost]
public ActionResult Create(WilhanWebsite.DomainClasses.Photo photo)
{
if (ModelState.IsValid)
{
photo.DateUploaded = DateTime.Now;
photo.ImageData = new byte[photo.UploadImages.ContentLength];
photo.UploadImages.Read(photo.ImageData, 0, photo.ImageData.Length);
_context.Photos.Add(photo);
_context.SaveChanges();
return RedirectToAction("Index");
}
//we only get here if there was a problem
return View(photo);
}
Now bear in mind that this is an absolutely awful solution. Never do that in a real world application. In a correctly designed application you will have a view model that your controller action will take as a parameter. You're never gonna directly use your autogenerated EF model as parameter to your controller action. You will have a view model with the HttpPostedFileBase property which will be mapped to your domain model.
So in a properly designed application you will have a PhotoViewModel view model class that your controller action will take.
Change this line:
#using (Html.BeginForm())
To this:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
Then change:
<input type="file" name="uploadImages" class="input-files" />
To:
<input type="file" name="ImageData" class="input-files" />
Then change this line:
public byte[] ImageData { get; set; }
To this:
public HttpPostedFileBase ImageData { get; set; }
Finally, use some code like this to read the image into a byte array:
var bs = new byte[ImageData.ContentLength];
using (var fs = ImageData.InputStream)
{
var offset = 0;
do
{
offset += fs.Read(bs, offset, bs.Length - offset);
} while (offset < bs.Length);
}
View:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
<input type="file" id="ImageFile" name="ImageFile" .../>
...
}
Controller:
[HttpPost]
public ActionResult Create(Photo photo, HttpPostedFileBase ImageFile)
{
byte[] buf = new byte[ImageFile.ContentLength];
ImageFile.InputStream.Read(buf, 0, buf.Length);
photo.ImageData = buf;
...
}
#Html.ActionLink("Edit", "EditArticle", new { ArticleID = article.ArticleID })
I retrieved Article by ArticleID and return to edit page like this:
public ActionResult EditArticle(Guid ArticleID)
{
AddArticleModel AddArticleModel = new AddArticleModel();
AddArticleModel.Categories = entity.TBL_CATEGORIES.Select(a => a);
AddArticleModel.Article = dbo.SelectArticleById(ArticleID);
return View(AddArticleModel);
}
There is no problem until here.
And in my editing page I'm changing some attributes of article (not all attributes).For example I'm changing title, content, and updateddate. Like this:
#model DunyaYazilim.Models.AddArticleModel
#{
ViewBag.Title = "EditArticle";
Layout = "~/Views/Shared/_LayoutAuthor.cshtml";
}
#using (Html.BeginForm((string)ViewBag.FormAction, "Author"))
{
#Html.ValidationSummary(true, "Makale gönderilirken bir hata oluştu. Lütfen daha sonra tekrar deneyin.")
<div>
<div class="label_header">#Html.Label("Kategori Seçiniz:")</div>
<div>#Html.DropDownList("CategoryID", new SelectList(Model.Categories, "CategoryID", "Name"),Model.Article.TBL_CATEGORIES.Name)</div>
<div class="label_header">#Html.Label("Makale Başlık:")</div>
<div>#Html.TextBoxFor(m => m.Article.Title, new { #class = "my_textbox" })</div>
<div class="label_header">#Html.Label("Makale Açıklama:")</div>
<div>#Html.TextAreaFor(m => m.Article.Description, new { #class = "my_textarea" })</div>
<div class="label_header">#Html.Label("Makale İçerik:")</div>
<div>#Html.TextAreaFor(m => m.Article.ArticleContent, new { #class = "my_textarea" })</div>
<div><input type="submit" value="Gönder" class="my_button" /></div>
</div>
}
And then I post it to:
[HttpPost]
public ActionResult EditArticle(AddArticleModel AddArticleModel, String CategoryID)
{
//TODO: update database...
return View(AddArticleModel);
}
But unchanged attributes are return null(ArticleID, UserID, etc).So I cant Update the database, Because I dont have ArticleID after posting. What is the reason for this?
Thanks.
MVC doesn't maintain anything for you between requests. When you post to your action, it will post only the values that you have set up in your form. As you don't have your article id or user id in the form (or anywhere else, e.g. route or query string), MVC won't know about them during model binding for your EditArticle action.
If you want the extra details to be sent through with your post, you can put hidden fields in the form, e.g.
#Html.HiddenFor(m => m.Article.Id)