Call an action method from layout in ASP.NET MVC - c#

I have one layout and one partial view which are in the Shared folder. Partial view presents top menu items which are not static. So I need to call an action method to get menu items from database. To do this, I created a controller and add an action method in it.
When I try to browse the page in web browser, this error occured:
The controller for path '/' was not found or does not implement IController.
Note:
I tried Html.RenderAction, Html.Partial methods too...
And I tried to create another view folder, and create a new partial view and new controller that named with "folder name + Controller" suffix.
Layout:
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
</head>
<body>
<div id="header">
#Html.Action("~/Views/Shared/_TopMenu.cshtml", "LayoutController", new {area =""}); //Here is the problem.
</div>
<div>
#RenderBody();
</div>
</body>
</html>
_TopMenu.cshtml:
#model IList<string>
#foreach (string item in Model)
{
<span>item</span>
}
LayoutController (in Controllers folder):
public class LayoutController : Controller
{
//
// GET: /Shared/
public ActionResult Index()
{
return View();
}
[ChildActionOnly]
[ActionName("_TopMenu")]
public ActionResult TopMenu()
{
IList<string> menuModel = GetFromDb();
return PartialView("_TopMenu", menuModel);
}
}

What happens if you put this in your view?
#{ Html.RenderAction("TopMenu", "Layout"); }
(And comment this out until everything works: //[ChildActionOnly])

Change this line,
#Html.Action("~/Views/Shared/_TopMenu.cshtml", "LayoutController", new {area =""});
to,
#Html.Action("_TopMenu", "Layout", new {area =""});
and check.

exist differents ways, for this case I like use html.action in layout, and in control I will create a string Menu, the string contains the html code I need, the controller end with return Content(menu);
for example
Layout:
<body>
<nav>
#Html.Action("_TopMenu", "Layout")
</nav>
the controller
public class LayoutController : Controller
{
public ActionResult _TopMenu()
{
IList<string> menuModel = GetFromDb();
string menu = "<ul>";
foreach(string x in menuModel)
{
menu +="<li><a href='"+x+"'>+x+"</a></li>";
}
menu+="</ul>";
return Content(menu);
}
}
I like that because I can use many options to create menus dinamics more complexes.
other way use ajax to recovered the data and use handlebars or other template for the code

You are using the wrong overload of the Action-Method. The 2nd parameter in the variation is not the controllername but the actionname.
You can check the correct Method overloads on this page
Also: If you specify Controllers in the Html.Action Method (which you can do for example with this variation of the Method), you dont need to write the suffix "Controller" even if thats your Classname. So Instead of using the string "LayoutController" you would write simply "Layout".
At this point the framework is convention-based.

This is how I did it:
Layout
#Html.Action("GetAdminMenu", "AdminMenu")
Admin Menu Controller
public PartialViewResult GetAdminMenu()
{
var model = new AdminMenuViewModel();
return PartialView(model);
}
GetAdminMenu.cshtml
#model ReportingPortal.Models.AdminMenuViewModel
<div class="form-group">
<label class="col-md-4 control-label" for="selectbasic">School Name</label>
<div class="col-md-8">
#Html.DropDownListFor(model => model.SelectedID, new SelectList(Model.DataList, "Value", "Text", Model.SelectedID), "", new { #class = "form-control", #required = "*" })
</div>
</div>

Related

Update partial view from drop down list selection using MVC 5 EF 6

I am working on my third year project and I'm struggling with this one section so badly, I have looked around and found some answers but I really done know how to implement in my code because it always just doesn't work. So I don't know what I'm doing wrong.
What i would like is for the partial view to change when the drop down selected item has changed.
This is what was generated in the view for this section.
<div class="form-group">
#Html.LabelFor(model => model.TypeID, "TypeID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("TypeID", String.Empty)
#Html.ValidationMessageFor(model => model.TypeID)
</div>
</div>
Most solutions I have seen use a #html.dropdownlistfor().
Any help would be appreciated even if you could just point me to the right place.
The drop down list is filled from the database relationships.
I have got this to work if i use labels in an "a" tag with an href but they are hard coded onto the page. I want to use the drop down list so that if i update the database it will have the updated list instead of me going to change it in the code, its also more user friendly in my eyes.
Thanx in advance
You could retrieve the data from the server and construct/change the DOM via JQuery, or you could use a partial view that is appropriate for each question type, attaching an event handler to the change event of the drop-down via JQuery.
One approach, loading partial views:
yourNamespace.yourScript.js file (include the file in your main view via the <script> tag with the src attribute):
(function ($) {
if (!window.yourNamespace) {
window.yourNamespace = {};
}
if (!window.yourNamespace.yourPageScript) {
window.yourNamespace.yourPageScript = function () {
var populateView = function (dropDown) {
if (dropDown && dropDown.length) {
var value = dropdown.val();
$.ajax({
method: "GET",
cache: false,
url: "some.url/PopulateType",
dataType: "HTML"
data: { selectedValue: value }
})
.done(function (response) { // jQuery 1.8 deprecates success() callback
var div = $('#partialViewDiv');
div.html('');
div.html(response);
});
}
};
return {
populateView: populateView
};
};
}
}(jQuery));
Your main view could have something like this:
<script type="text/javascript">
// put the script section somewhere appropriate in the page
$(document).ready(function () {
var dropDown = $('#TypeId'); // assuming the ID of this element is 'TypeId'
dropDown.change(function () {
yourNamespace.yourPageScript.populateView(dropDown);
});
});
</script>
<div id="partialViewDiv">
#Html.RenderPartial("path to initial partial view", model);
</div>
partial view example (adjust to be proper for any particular dropdown selection):
#model namespaceName.QuestionTypeModel
<div class="form-group>
#* put controls appropriate to the particular partial views here, such as radio buttons for the multiple choice question type, etc. *#
<div>
<div class="form-group">
#Html.LabelFor(model => model.TypeID, "TypeID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("TypeID", Model.QuestionTypeValues)
#Html.ValidationMessageFor(model => model.TypeID)
</div>
</div>
Part of the controller:
[HttpGet]
public ActionResult Index()
{
var model = new MainViewModel();
// populate the model with data here for the initial display, including the initial drop-down values,
// presumably the same way you do now
// into model.QuestionTypeValues
return View(model); // the main view
}
[HttpGet]
public ActionResult PopulateType(int selectedValue) // could use an enum for the selectable values
{
var model = new QuestionViewModel();
string partialViewName = null;
// populate with data appropriate to the partial views
switch (selectedValue)
{
case 0:
partialViewName = "partial view name for item 0";
// populate "model" with the appropriate data
break;
case 1:
partialViewName = "partial view name for item 1";
// populate "model" with the appropriate data
break;
// and so on...
default:
throw new ArgumentException("unknown selected value", "selectedValue");
break;
}
return PartialView(partialViewName, model);
}
The approach to use jQuery to build the DOM elements instead of using partial views is left as an exercise.

Drop down list at layout page - MVC

My problem: drop down list at layout page.
I've read this post: ASP.NET MVC Razor pass model to layout it's more or less similar to my problem.
In one of comments Mattias Jakobsson wrote that: "But a common solution is to use RenderAction to render parts that need their own data in the layout page".
So ok I've created layout page with #Html.Action() that render my drop dwon list with a date from the db. Everything's perfect. But...
I have two pages, for example: 'Home', 'About' and my drop down list (ddl) at layout page
How to achive that when I'm at 'Home' and I changed selection in ddl it refresh 'Home' page and when I'm at 'About' it refresh 'About' page.
How to store selected ddl value through pages?
Part of Layout.cshtml code:
.
.
<body>
<header id="top" class="grid-full-margin">
<strong id="logo" class="grid-304"><img src="/images/logo.png" ></strong>
#Html.ActionLink(#Resources.Resource.BackToIntranet, "Index", "Home", null, new {#class = "link link-home grid-position-left"})
<h1>#Resources.Resource.SiteTitle</h1>
#Resources.Resource.LayoutHelp
<nav clss="grid-896">
<ul>
<li>#Html.ActionLink(Resources.Resource.LayoutMenuItem1, "Index", "Home")</li>
<li>#Html.ActionLink(Resources.Resource.LayoutMenuItem2, "Index", "ClimaticStation")</li>
<li>#Html.ActionLink(Resources.Resource.LayoutMenuItem3, "Index", "ClimaticPoint")</li>
<li>#Html.ActionLink(Resources.Resource.LayoutMenuItem4, "Index", "IcewaterExchanger")</li>
<li>#Html.ActionLink(Resources.Resource.LayoutMenuItem5, "Index", "Pipeline")
<ul>
<li>#Html.ActionLink("Zestawienie", "YearsLength", "Pipeline")</li>
</ul>
</li>
</ul>
<div class="mod-select-list tbl-actions">
#Html.Partial("~/Views/Shared/Partials/LoginPartial.cshtml")
</div>
</nav>
</header>
<form action="#">
#Html.Action("VariantsDdl", "MyBase")
</form>
#RenderBody()
.
.
Part of MyBaseController.cs
public class MyBaseController : Controller
{
[ChildActionOnly]
public ActionResult VariantsDdl()
{
var dataFromDb = GetDataFromDB(); // it's not importstn right now
return this.PartialView("~/Views/Shared/Partials/VariantsDdlPartial.cshtml", dataFromDb);
}
.
.
}
Regards,
Marcin
ok I've managed to solve this problem and I want to know your opinion abut my solution.
_Layout.cshtml looks the same way like at first post, so belowe is only most important part for this question (drop down list at layout)
<div style="float: right;">
#Html.Action("VariantsDdl", "MyBase")
</div>
Action: VariantsDdl is implemented at MyBaseController. This action loads selected variant id from session or if it's null then from web.config (in this situation it's project requirement that at least one variant must be present at db and its id must be specified in config):
[ChildActionOnly]
public ActionResult VariantsDdl()
{
long defaultVariantID;
long.TryParse(System.Configuration.ConfigurationManager.AppSettings["DefaultVariantId"], out defaultVariantID);
if (System.Web.HttpContext.Current.Session["mySelectedVariant"] != null)
{
long.TryParse(System.Web.HttpContext.Current.Session["mySelectedVariant"].ToString(), out defaultVariantID);
}
var variants = this.db.warianties.ToList();
var items = new List<SelectListItem>();
foreach (var variant in variants)
{
var selectedItem = false;
if(variant.id == defaultVariantID)
{
selectedItem = true;
}
items.Add(new SelectListItem { Selected = selectedItem, Text = variant.nazwa, Value = variant.id.ToString() });
}
return this.PartialView("~/Views/Shared/Partials/VariantsDdlPartial.cshtml", items);
}
Partial view and post action that stores selected variant id to session:
#model IEnumerable<SelectListItem>
<label for="field">Current variant</label>
#Html.DropDownList("Varaints", Model, new { id = "variantsDdl" })
<script type="text/javascript">
$(function () {
$('#variantsDdl').change(function () {
var val = $('#variantsDdl').val()
$.ajax({
type: "POST",
url: '#Url.Action("ChangeVariant", "MyBase")' + '/' + val,
success: function (result) {
location.reload();
},
error: function (data) { alert('Error'); }
});
});
});
Partial View post action 'ChangeVariant', saves selected variant id to session:
[HttpPost]
public ActionResult ChangeVariant(long id = 0)
{
System.Web.HttpContext.Current.Session["mySelectedVariant"] = id;
return null;
}
This is solution for my requirements:
1. DDL at layout
2. Refresh current page at DDL 'onchange'
3. Keep selected DDL value through pages
Please comment if it's appropriate solution or maybe should I go different way?
Regards,
Marcin

Using Html.Action causes query string too long error

I have a _LayoutView:
#{
Layout = "~/Views/Shared/_NavLayout.cshtml";
}
#section styles
{
#RenderSection("styles", required: false)
}
<div class="container" style="padding-top: 60px;">
<div class="row">
<div class="col-md-12">
#Html.Action("AccountNavigation")
</div>
</div>
#RenderSection("InlineTitle", required:false)
#RenderBody()
</div>
#section scripts {
#Scripts.Render("~/bundles/jqueryval")
#RenderSection("scripts", required: false)
}
That renders fine if I remove the
#Html.Action("AccountNavigation")
Otherwise I get:
The action method is:
[ChildActionOnly]
public ActionResult AccountNavigation()
{
var roles = UserManager.GetRoles(User.Identity.GetUserId());
ViewBag.IsAdmin = roles.Contains("Admin");
return PartialView("_AccountNavigatorPartial");
}
And Ive tried stripping it back to just:
[ChildActionOnly]
public ActionResult AccountNavigation()
{
ViewBag.IsAdmin = false;
return null;
}
But it makes no difference.
One of the child views that uses the layout is Login.
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.OidLoginFailed = false;
ViewBag.ReturnUrl = returnUrl;
return View();
}
If I put a break piont in there I can see its being called multiple times per request and building up the ReturnUrl until it fails hence the error message. This is why I stripped back the AccountNavigation ActionMethod.
I thought maybe an anon request was causing a post back via some config setting that says if Anon redirect to Login and round and round it would go but I cant see where that is being triggered.
The account _AccountNavigatorPartial is just:
<ul class="nav navbar-nav navbar-Left">
<li>#Html.ActionLink("Manage Credentials", "Credentials", "Account",
routeValues: null, htmlAttributes: new { id = "credentialsLink" })</li>
<li>#Html.ActionLink("Manage Profile", "Profile", "Account",
routeValues: null, htmlAttributes: new { id = "credentialsLink" })</li>
</ul>
So all I'm trying to do is inject some html for account navigation. I'm using ASP.Identity for membership but I cant see how that makes any difference as I'm requesting an Anon accessible page.
It's hard to tell without seeing the controller and knowing how you are handling authorization, however, since your login page is using that Layout, you may be experiencing a circular call due to failure of authorization on you child action.
Have you tried adding the attribute [AllowAnonymous] to your child action?
We were getting similar UriFormatException: Invalid URI: The Uri string is too long. - we found the solution was to pass the model in as an explicit parameter instead of just the typical approach.
Not Working
#Html.Action("MyChildAction", "Path", Model)
Working
#Html.Action("MyChildAction", "Path", new { viewModel = Model} )
This seems to be triggered by using a very large view model - this must wrap it and bypass the encoding issue UriHelper.EscapeString which overflows the buffer.

New to ASP.NET MVC, having trouble with render action containing a form

I am used to ASP.NET web forms, and am slowly learning ASP.NET MVC.
My website has a little login form on the homepage. My natural thought is that this login form may be useful in other places, and it is not the primary action of the homepage, so I want to separate it off into a partial view. And because it is related to accounts, I want the login in my AccountController not my HomepageController.
Login form is a pretty basic strongly typed partial view:
#model Models.Account.AccountLogin
<h2>Login Form</h2>
#using (Html.BeginForm("_Login", "Account")) {
#Html.ValidationSummary()
<div>
<span>Email address:</span>
#Html.TextBoxFor(x => x.EmailAddress)
</div>
<div>
<span>Password:</span>
#Html.PasswordFor(x => x.Password)
</div>
<div>
<span>Remember me?</span>
#Html.CheckBoxFor(x => x.RememberMe)
</div>
<input type="submit" value="Log In" />
}
</div>
On the homepage, I have this:
#Html.Action("_Login", "Account")
Finally, in the account controller, this:
[HttpGet]
public PartialViewResult _Login()
{
return PartialView();
}
[HttpPost]
public PartialViewResult _Login(AccountLogin loginDetails)
{
// Do something with this
return PartialView();
}
Now when I load my homepage, it looks OK and contains the form. When I click the Log In button, it takes me to myurl/Account/_Login, which contains the form, but not within the _Layout master page, just basic plain HTML and it doesn't do anything at all when I click Log In.
I am pretty sure that I have just missed some fundamental aspect of what I am supposed to be doing here, can someone please point me in the right direction?
It's because you're returning a partial view, which strips away the master page and just returns the main content. Often actions starting with an underscore are used for partials (e.g. ajaxing in a bit of a page, but not the full page). It sounds like you want a full action, and not a partial, so
[HttpPost]
public ActionResult Login(AccountLogin loginDetails)
{
// Do something with this
return View();
}
The issue here is that you are doing a fullpage postback.
You have two options, really.
Firstly, you can use a full page postback, and then call Html.Partial to display your Partial.
Something like
[HttpGet]
public ActionResult Login()
{
return View();//this typically returns the view found at Account/Index.cshtml
}
And then create a View along the lines of
#{
ViewBag.Title = "Index";
}
<h2>Title</h2>
#Html.Partial("PartialNameGoesHere")
Your partial is then rendered where indicated, but this is done when the page loads (if you look at the generated HTML, it appears exactly as though you had written it inline).
Or you can use jQuery/AJAX to load the partial on demand. Let's say you have a homepage of some description
public ActionResult Home()
{
return View();
}
public ActionResult Login()
{
return PartialView("_Login");
}
Create the view
#{
ViewBag.Title = "Index";
}
<h2>Home</h2>
<div>
<p>Hey welcome to my pretty awesome page!</p>
</div>
Show me the login!
<div id="container">
</div>
You can then load the PartialView into the container div whenever you need it, using some JS.
$(function() {
$('.my-login-link').click(function() {
$.ajax({
url: 'account/login',
success: function(data) {
$('#container').html(data);
}
});
return false;//cancel default action
});
});
In that instance, the page loads as normal without the login part. When the user clicks the link, the Login on the controller Account is called using AJAX/jQuery. This returns the HTML of the PartialView, which you can then add to the page using jQuery in the Success handler.

Razor view engine - How can I add Partial Views

I was wondering what, if it is possible, is the best way to render a partial using the new razor view engine. I understand this is something that wasn't finished completely by the time
Right now I am using RenderPage to render the user control:
#RenderPage("~/Views/Shared/LocaleUserControl.cshtml",ViewData.Model)
The page calling RenderPage uses a layout (master) page with three sections defined: TitleContent, HeadContent and Maincontent. When I attempt to render my locale control from this page it appears that these sections are also required - they should only be required in the calling page and are present. I receive the following message, regardless of whether or not I include the sections in my partial view (obviously I dont want to include these sections but it seemed like an interesting debugging point...).
The following sections have been
defined but have not been rendered on
the layout page
'~/Views/Shared/LocaleUserControl.cshtml':
TitleContent; HeadContent; MainContent
My partial view is as follows (adapted from the following link):
#inherits System.Web.Mvc.WebViewPage<LocaleBaseModel>
#using System.Web.UI;
<p>
#Html.LabelFor(model => Model.CountryName)
<br />
#Html.DropDownListFor(model => Model.CountryName,null, string.Empty, new { #class = "text", accesskey="u"})
</p>
<p>
#Html.LabelFor(model => Model.StateProvince)
<br />
#Html.DropDownListFor(model => Model.StateProvince, null, string.Empty, new { #class = "text", accesskey="t" })
</p>
<script type="text/javascript">
$(function () {
var countries = $("#CountryName");
var statesprovinces = $("#StateProvince");
countries.change(function () {
statesprovinces.find('option').remove();
var url = '#Url.Action("GetStatesProvinces", "Base")';
$.getJSON(url, { countryId: countries.val() }, function (data) {
$(data).each(function () {
$("<option value=" + this.ID + ">" + this.Name + "</option>").appendTo(statesprovinces);
});
});
});
});
</script>
You partial looks much like an editor template so you could include it as such (assuming of course that your partial is placed in the ~/views/controllername/EditorTemplates subfolder):
#Html.EditorFor(model => model.SomePropertyOfTypeLocaleBaseModel)
Or if this is not the case simply:
#Html.Partial("nameOfPartial", Model)
If you don't want to duplicate code, and like me you just want to show stats, in your view model, you could just pass in the models you want to get data from like so:
public class GameViewModel
{
public virtual Ship Ship { get; set; }
public virtual GamePlayer GamePlayer { get; set; }
}
Then, in your controller just run your queries on the respective models, pass them to the view model and return it, example:
GameViewModel PlayerStats = new GameViewModel();
GamePlayer currentPlayer = (from c in db.GamePlayer [more queries]).FirstOrDefault();
[code to check if results]
//pass current player into custom view model
PlayerStats.GamePlayer = currentPlayer;
Like I said, you should only really do this if you want to display stats from the relevant tables, and there's no other part of the CRUD process happening, for security reasons other people have mentioned above.

Categories