Partial view call, call Index before if not opened - c#

i have a website like this simple image
Everything in the redbox is the Index page with a default layout, the blue shows my partial view.
I have a mvc controller for emails, via a ajax actionlink i change the partial view. So you don't have to reload the complete page if you want to switch the folder.
Everything works fine if you go the follow way:
-Click on Email in the Navbar and you comes to the Index page from Email which opens a automatic the Inbox Partial view.
This is my code in the Html to call the Inbox Partial:
<div id="emailBody">
#Html.Action("EmailPartial", new { module = "1", id = "1,2" })
</div>
The Partial Controller has the follow code:
public PartialViewResult EmailPartial(string module, string id = "1,2", int start = 0, int anzahl = 100)
{
if (module == "1")
{
List<Emailstatus> emailStati;
List<int> TagIds = id.Split(',').Select(int.Parse).ToList();
emailStati = Emailstatus.AllList().Where(x => x.Id.In(TagIds)).ToList();
List<Emailgesendetview> emails = Emailgesendetview.AllListByBenutzerIdAndEmailStatus(benutzerId, emailStati, start, anzahl);
if (id == "1,2")
ViewBag.position = 1;
else if (id == "1")
ViewBag.position = 2;
else
ViewBag.position = Int32.Parse(id);
return PartialView("EmailGesendet", emails);
}
}
A click on the middle-right navigation bar triggers the follow code:
<li id="menu2">
#Ajax.RawActionLink(string.Format("<i class='fa fa-circle'></i> Unread"),
"EmailPartial",
"Email",
new { id = "1", module = "1", start = 0, anzahl = 100 },
new AjaxOptions()
{
HttpMethod = "GET",
AllowCache = false,
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "emailBody"
},
new { #class = "class"})
</li>
Everything works fine.
Now i want to add some submenu items to the navbar on the left but i have no idea ( okay i have a bad one... ) how to call the index page before i call the Partialview.
Okay let me explain a litte bit more my problem...
If the user is one the Email page i could easy use the same code but if the user is on a different page (maybe support or something else) and he clicks now on the Outbox link. Now i should call the Index Email page with the outbox partial view.
So i see 2 little problems,
Before i call the Email index page, i must check on which page i am actually because i dont want to reload the complete page if i am at the Email index page with another partial view. At this moment i only want to reload the partial view with a new model. (That step is easy).
My idea to check on which page i am, is to set a "pagestatus" in my viewbag. Is there a better option?
So now if i am on a other page then Email, i cant call the partial view because i have to call the Email Index page before.
My (i think it is a bad idea) is to create different Email index pages (Index1, Index2, Index3). These Pages could have different partial view call like this:
Index1:
<div id="emailBody">
#Html.Action("EmailPartial", new { module = "1", id = "1,2" })
</div>
Index2:
<div id="emailBody">
#Html.Action("EmailPartial", new { module = "2", id = "1" })
</div>
Index3:
<div id="emailBody">
#Html.Action("EmailPartial", new { module = "3", id = "1" })
</div>
So my solution is:
Check on which page i am and if i am on a different Controller, call the Index which i need.
I think that is not a "clear" solution.
Has anyone a better solution? Creating pages for different partialview calls does not sound good...
Thank you

Related

Transform Querystring to RouteValue in Razor View

I'm stuck with a very basic detail in a view.
I want to be able to let the user filter the results in the Index view.
To do this I've created a dropdown list, which gets populated thourgh my viewmodel:
#using (Html.BeginForm("Index", "Captains", FormMethod.Get)) {
<div class="row">
<div class="dropdown">
#Html.DropDownList("Name", new SelectList(Model.SomeProperty), new { id = "FilterList" })
</div>
</div>
#* ... *#
}
Additionally I have a small jQuery snippet to submit the form on the change event:
$('#FilterList').on('change', function () {
var form = $(this).parents('form');
form.submit();
});
The route I have created for this looks like this:
routes.MapRoute(
name: "IndexFilter",
url: "{controller}/{action}/{Name}",
defaults: new { Name = UrlParameter.Optional}
);
After the submit event I get redirected to the url /Index?Name=ChosenValue
This is filtering totally correct. However I'd like to get rid of the querystring and transform the route to /Index/ChosenValue.
Note: "Name", "ChosenValue" & "SomeProperty" are just dummy replacements for the actual property names.
Instead of submitting the form, you can concatenate /Captains/Index/ with the selected value of the dropdown and redirect to the url using window.location.href as below
$('#FilterList').on('change', function () {
window.location.href = '/Captains/Index/' + $(this).val();
});
I think you're looking for the wrong routing behavior out of a form submit. The type of route resolution that you're hoping to see really only happens on the server side, where the MVC routing knows all about the available route definitions. But the form submission process that happens in the browser only knows about form inputs and their values. It doesn't know that "Name" is a special route parameter... it just tacks on all the form values as querystring parameters.
So if you want to send the browser to /Index/ChosenValue, but you don't want to construct the URL from scratch on the client, you need to construct the URL on the server when the view is rendering. You could take this approach:
<div class="row">
<div class="dropdown">
#Html.DropDownList("Name", new SelectList(Model.SomeProperty),
new {
id = "FilterList",
data_redirect_url = #Url.Action("Index", "Captains", new { Name = "DUMMY_PLACEHOLDER" })
})
</div>
</div>
Above you're setting the URL with a dummy "Name" value that you can replace later, then you'll do the replacement with the selection and redirect in javascript:
$('#FilterList').on('change', function () {
var redirectUrl = $(this).data('redirect-url');
window.location.href = redirectUrl.replace("DUMMY_PLACEHOLDER", $(this).val());
});
If you are wanting to drop the query string off the url because it looks weird, then change your FormMethod.Post.
However, to really answer your question, I've tried the following successfully (Note: this might be considered a hack by some)
In short: update the action url on the form element when the list changes, client side.
$('#FilterList').on('change', function () {
var form = $(this).parents('form');
var originalActionUrl = form.attr("action");
var newActionUrl = originalActionUrl + "/" + $(this).val();
form.attr("action", newActionUrl);
console.log(form.attr("action"));
form.submit();
});
You will need to change your controller's signature to match whatever optional param value you specify in your route config. In your example, "Name".

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

MVC4 WebGrid and Ajax Pagination

I have a simple question (may not be a simple answer!) with the WebGrid in MVC4.
I have a functional Grid like so
<div id="Submitted">
#Html.Grid(
Model.Submitted, displayHeader: false, fieldNamePrefix: "Submitted_", ajaxUpdateContainerId: "Submitted",
columns: new WebGridColumn[]{
new WebGridColumn(){ ColumnName = "Date",Header = "Date",Style = "",CanSort = false,Format = x => x.Date.ToString(Globals.Default.DATEFORMAT) },
new WebGridColumn(){ ColumnName = "ID",Header = "Review",Style = "", CanSort = false, Format = x=>#Html.ActionLink("Review","Review","Company",new{ID = x.ID },new{}) }
})
</div>
When reloading the "Submitted" div with Ajax when the next page button is clicked, it generates the next page fine - but it's going to the original action on the controller, which should be a full page.
How does it filter out everything other than the grid itself? with some clever C# code or jQuery?
EDIT:
To clarify, I'm not asking how to do the paging better, or myself, as far as I'm concerned the default paging with the webgrid is working perfectly as it should - I'm asking how the WebGrid does it's ajax paging when posting back to an action which is returning a FULL page.
It does this using jquery load() and functionality that allows it to select just the relevant incoming nodes. This is taken from http://msdn.microsoft.com/en-us/magazine/hh288075.aspx
To allow the script to be applied just to a WebGrid, it uses jQuery selectors to identify elements with the ajaxGrid class set. The script establishes click handlers for the sorting and paging links (identified via the table header or footer inside the grid container) using the jQuery live method (api.jquery.com/live). This sets up the event handler for existing and future elements that match the selector, which is handy given the script will be replacing the content.
You should put your grid in a partialview and update it by Ajax. In controller you should find the request's type. If it is a ajax request(by Request.IsAjaxRequest() ) you must return the partialview, otherwise you must return the original view.
If you are using ajax.beginform you must do something like this:
#using (Ajax.BeginForm("Index", new AjaxOptions { OnFailure = "searchFailed", HttpMethod = "POST", UpdateTargetId = "Submitted" }))
{
...
}
<div id="Submitted">
#Html.Partial("_partialviewname", Model)
</div>
rn
and in controller:
public ActionResult Index(int? page)
{
...
if (Request.IsAjaxRequest())
{
return PartialView("_partialviewname", db.model)
}
return View(db.model)
}

ActionLink with ajax enabled get's invoked as a normal GET

I have a page, that should update a "process" kinda window when an action is aplied. Kinda like a visible log. Click "start upload" and the div tag underneath will be updated with information.
The problem is, that when i click the "start upload" link it just jumps to that page and don't do a ajax call and update the div.
Controller
public ActionResult ProcessWindow(decimal id)
{
IMG_SETTINGS_FOLDERS img_settings_folders = db.IMG_SETTINGS_FOLDERS.
Single(i => i.SETTINGS_FOLDER_ID == id);
return PartialView("ProcessWindow", img_settings_folders);
}
View
#Ajax.ActionLink("Start upload", "ProcessWindow",
new { id = Model.SETTINGS_FOLDER_ID },
new AjaxOptions { UpdateTargetId = "processWindowssssss" })
<div id="processWindowssssss"></div>
Solved...
When you use unobtrusive ajax, you need to include "jquery.unobtrusive-ajax.min.js"...
If people who write toturials were to read this, please extend your "js" include info to contain this information too.
Answer found from Brad Wilson on Unobtrusive Ajax. http://bradwilson.typepad.com/blog/2010/10/mvc3-unobtrusive-ajax.html
\T

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