I give data to the ViewBag in the IAuthorizationFilter.OnAuthorization process, and it puts for the _Layout.cshtml-in, but when I use a partial view in normal view the data in the ViewBag is null.
This is normal behavior, or I am doing something wrong?
public class MyAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
void IAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
filterContext.Controller.ViewBag.Name = this.name;
filterContext.Controller.ViewBag.Menus = user.GetMenu(this.role);
}
...
}
The _Layout.cshtml:
<section id="login">
Hello, <span class="username">#ViewBag.Name</span>!
</section>
<nav>
#Html.MenuLink(ViewBag.Menus as List<Entity.Models.MENU>, (string)ViewBag.Name)
</nav>
The view:
#model Web.ViewModels.RegistrationEntryViewModel
#Html.Partial("_RegistrationEntry", Model)
The action:
[MyAuthorize("ADMINISTRATORS")]
[HttpGet]
public ActionResult NewRegistrationEntry()
{
//The viewbag is already null here
...
}
Please stop using ViewBag like that. It is not designed for this. And reason ViewBag is null in controller (I suspect) that these are different ViewBag objects in filter and in controller.
If you need to do repetitive actions in your view, there are better ways to do that. For your menu I would create an action that reads menu information, puts that in a ViewModel (not in a ViewBag) and outputs a partial view. Then in your Layout I'd render that partial action. (Also add some caching on this partial view)
Related
I have a .NET MVC project with Razor views and I would like to implement search functionality that consists of a dropdown list, a text box and a search button.
The problem is that I would like to implement this search snippet in my _Layout.cshtml file outside of the #RenderBody() call. This means that the search functionality would be accessible on every page (it would be located at the very top right corner).
I'm trying to find out what is a good way of implementing this. I can get it to work but it would involve adding same code (do get dropdown values) to all controllers and actions.
ViewBag.States = new SelectList(db.States, "Id", "Name");
Is there a better way to implement this? It feels very repetitive to do it this way.
You can have a child action method which returns the partial view needed for your header and call this action method in your layout.
Create a view model for the properties needed.
public class AllPageVm
{
public int SelectedItem { set; get; }
public List<SelectListItem> Items { set; get; }
}
Now create an action method in any of your controller. Mark this action method with ChildActionOnly decorator.
public class HomeController : Controller
{
[ChildActionOnly]
public ActionResult HeaderSearch()
{
var vm = new AllPageVm()
{
Items = db.States
.Select(a => new SelectListItem() {Value = a.Id.ToString(),
Text = a.Name})
.ToList()
};
return PartialView(vm);
}
Now in the HeaderSearch.cshtml partial view, you can render whatever markup you want for your search header. Here is a simple example to render the dropdown. You may update this part to include whatever markup you want (Ex : a form tag which has textbox, dropdown and the button etc)
#model AllPageVm
<div>
<label>Select one state</label>
#Html.DropDownListFor(a => a.SelectedItem, Model.Items, "Select")
</div>
Now in your layout, you can call this child action method
<div class="container body-content">
#Html.Action("HeaderSearch", "Home")
#RenderBody()
<hr/>
<footer>
<p>© #DateTime.Now.Year - My ASP.NET Application</p>
</footer>
</div>
Make sure you are calling PartialView method from the HeaderSearch child action method instead of View method. If you call View method, it will recursively call the same method and you will get a StackOverflow exception
I have an MVC5 app, and in the HomeController, I have an ActionResult defined like this:
public ActionResult BlogRSS()
{
var model = new BlogModel();
string strFeed = "http://webmysite.com/feed";
using (XmlReader reader = XmlReader.Create(strFeed))
{
SyndicationFeed rssData = SyndicationFeed.Load(reader);
model.BlogFeed = rssData;
}
return View(model);
}
Then, for this ActionResult, I created a partial view named BlogRSS, which looks like this:
#model MyApp.Models.BlogModel
#{
if (Model.BlogFeed != null)
{
<ul>
#foreach (var post in Model.BlogFeed.Items.ToList().Take(3))
{
<li><a href='#post.Links.First().Uri' target='_blank'>#post.Title.Text</a></li>
}
</ul>
}
}
And my model is defined simply like this:
public class BlogModel
{
public SyndicationFeed BlogFeed { get; set; }
}
So, the point is that I want to call that partial view in my _Layout.cshtml file, but when the website opens I get the error message specified in the title. I guess it is not calling my BlogRSS method at all. I'm calling it in the _Layout.cshtml like this:
<div class="col-md-4">
Blog
<br />
#Html.Partial("BlogRSS")
</div>
How can I solve the problem, and make sure that the corresponding ActionResult is also called before rendering the View?
The problem is that you're putting a call to a partial view which just renders the view without calling the controller and the model passed to that view is null.
There are couple ways how to fix this:
1) use Action instead of Partial
#Html.Action("BlogRSS", "Blog")
2) Define a base ViewModel which you will pass to the each view and put your feed into it.
In "ASP.NET MVC3, Html.TextAreaFor without encoding?"
Darin Dimitrov had the answer, but I can't make it work.
Probably because I'm confused about what the #model statement at the top of a strictly typed View means.
My statement is:
#model TestTinyMCE.Models.TestBlog
When I type in his answer:
You will need to roll your own:
<textarea cols="100" id="PostBodyText" name="PostBodyText" rows="10">
#MvcHtmlString.Create(Model.PostBodyText)
</textarea>
It fails because Model is null.
How do I associate Model with the model my View is using?
I'm in a Create() view, btw, if that matters. This will also have to work in the Edit(int id) View.
Can you show us your Controller code?
It should look like:
using TestTinyMCE.Models;
public class Mycontroller : Controller
{
public ActionResult Create()
{
return View(new TestBlog());
}
public ActionResult Edit(int id)
{
var model = new TestBlog();
// Fill the model value with the entity from your datastore
return View(model);
}
}
You must set the model of your View in your controller actions. This way, you won't receive a null value in your Razor view.
In the view, I would write this:
#model TestTinyMCE.Models.TestBlog
<textarea cols="100" id="PostBodyText" name="PostBodyText" rows="10">
#Html.Raw(Model.PostBodyText)
</textarea>
How do I prevent renderaction from rendering the masterpage and giving it back to me? I only want it to render 1 section, for eg.
The controller
public ActionResult PaymentOptions()
{
return View(settingService.GetPaymentBanks().ToList());
}
The PaymentOptions View:
#model IEnumerable<Econo.Domain.PaymentBank>
<h2>Payments</h2>
<!-- Stuff here -->
The View
<div class="grid_10">
</div>
<div class="grid_14">
#{Html.RenderAction("PaymentOptions", "Administrator");}
</div>
In grid_14, the header, footer and everything else gets rendered. Is there a way to prevent this?
public ActionResult PaymentOptions()
{
return PartialView(settingService.GetPaymentBanks().ToList());
}
In Razor, partial views and full views have the same extension, so you need to explicitly use the PartialViewResult result type to specify a partial view.
This:
return View(settingService.GetPaymentBanks().ToList());
Has to use the overload so you can specify a master:
return View("PaymentOptions", "", settingService.GetPaymentBanks().ToList());
I've created a PartialView which I render with Html.RenderPartial, passing the name of the view and the strongly-typed data item to bind to (below):
<% Html.RenderPartial("SearchViewUserControl", ViewData["SearchData"]); %>
The partial view has a form containing a submit button:
<% using (Html.BeginForm("Search", "Home"))
{ %>
...
<div>
<input type="submit" value="Search" />
</div>
<% } %>
I've set a breakpoint in my controller's action method (below) but nothing is set in searchData. What am I doing wrong?
public ActionResult Search(SearchDomain searchData)
{
if (ModelState.IsValid)
{
}
return View();
}
You need to post the actual form elements for anybody to know whats wrong.
The form html is what sets the binding to SearchDomain. You want to have your form elements named like this:
<input name="searchData.SomeProperty">
For them to bind to your action parameter.
In order to pull a SearchDomain object out of your view from a controller method, your view has to either inherit from System.Web.Mvc.ViewPage<Models.SearchDomain>, or a custom ViewModel class that contains a SearchDomain object.
The other way to do it is to have your view inherit from System.Web.Mvc.ViewPage, and use UpdateModel to cast the view data to a SearchDomain object. Something like this:
public ActionResult Save()
{
SearchDomain domain = new SearchDomain ();
UpdateModel(domain , new[] { "Name", "Email", "Phone", ... });
return View(domain);
}
To be honest, I think RenderAction is much easier to use.