Vertical Navigation - How can I keep the nav menu contents static, even on child pages? - razor

We are using Sitefinity 11 and MVC. We are trying to use just one template for the main heading pages that reside just under the root of the site. On that template, we'd intend to use a vertical navbar (using the included Sitefinity navbar widget set to the Vertical view, its Bootstrap 4) which will reside on the left side of the page.
We want the navigation to show the top most page of that section (which we have enabled by turning "ShowParentPage" to True, and display child pages up to 3 levels deep (including the top page...so child and grand child pages). The tricky part is that we'd like for it to stay the same, even when the user navigates into one of the child pages and always show that top level with the two sub levels. There is no built in functionality for this currently, and the closest option to use is to set the widget to always show current page and children. But again, this results in it setting the current page to the top instead.
There are only going to be three or four of these top level pages under the home page, so if push comes to shove, we could create a different template for each section of the site, and then just set the navigation widget to always default to the top page of that section which is another built in option on the navigation widget, but this seems like it would result in duplicated effort.
I've been trying to modify the NavigationView.Vertical razor html view to achieve our desired result, but I am having no luck. My thought, is that if I could check to see if the parent page, or parent of the parent page has a certain page title (which should remain pretty static over time, although yeah I know this can lead to risk of the page name is changed), then set that as the active node, and then have the code do what it is doing already, which is to loop through the child pages and write them to the navigation menu.
This is a snippet of the default code on the view:
#model Telerik.Sitefinity.Frontend.Navigation.Mvc.Models.INavigationModel
<div class="#Model.CssClass">
<nav>
<ul class="nav flex-column navbar-child-custom">
#foreach (var node in Model.Nodes)
{
<li class="nav-item">
<a class="nav-link #GetClass(node)" href="#node.Url" target="#node.LinkTarget">#node.Title</a>
#RenderSubLevelsRecursive(node)
</li>
}
</ul>
</nav>
</div>
If I could somehow set that node in the for-each loop to the top most parent page, then I could just let it do it's thing and it would work, but I don't know of a proper way to do this.
Any help or suggestions are much appreciated!

Related

Unable to pass model into partial view inside layout pages

I have a layout page within that layout I have used a partial view, the partial view contains a menu feature that I have built, I split the menu into a partial view to ensure it is easy to maintain. The menu has been purposely placed in the layout because it is used across every page, however there are conditional elements in the menu whereby some options only appear on certain pages.
I have stumbled upon what will be an issue for me moving forward, the menu uses ajax calls to render partial views containing the content (reducing page loads) I was just working on a page which contains a company, the company contains a list of contacts, the menu option when clicked should display the list of contacts. I have already loaded the contact list under the company model, but! I can’t access it from my new partial view that should render the contact list because the menu is a partial view that is contained within the layout page and as such cannot accept a model, so I cannot pass a model into the partial view I am trying to load because the menu partial view sits in the layout page.
This is a sticky situation, I obviously could change the layout to render a new section to contain the menu so I can pass a new view model into it but then every single page I build needs to reference the menu (what a pest!) I must be missing something here (considering this is my first MVC3 application that is likely). Any suggestions?
Edit: I took this further on my own, in short my layout page will always be able to access the model of the page that consumes it, as such my partial view which contains the menu can also access that data. I wrote some conditional logic in my menu partial view that checks the page and then passes in data as required.
<div class="menu">
<ul>
<li><a href="#Url.Action("Create", "Contact")">New Contact </li>
<li>Contact List </li>
</ul>
#if (Request.Url.PathAndQuery.Contains("/Contact/Details/"))
{
<ul>
<li>#Html.ActionLink("New Activity", "Create", "Activity", new { companyid = 0, contactid = Model.contact.id }, null)</li>
</ul>
}
</div>
The above is a small sample of the menu partial view but contains one example where the menu is built for the contact/details page and is able to pass in the model.contact.id. It works in that my menu and my layout do not explicitly contain a model, but it does not feel very tidy.
If I'm understanding your question correctly, your issue is that you don't think your Partial View can have a model because you don't want your layout to have a model. So the question is how can you get a model into your layout without needing every single action to extend this base model type your layout would use.
1) Instead of using Html.Partial in your layout for the menu use Html.Action where you'll then have an action method that fetches the menu data.
2) Write a custom WebViewPage and include a property that has something like
return ((BaseController)ViewContext.Controller).MenuData;
now you don't even need a model in your partial view, it can access the data directly.
Both of these require having a Menu property containing all menu information available in your base model, but if every page in your websites going to need to access this data, then that seems appropriate.
Edit: In response to your tidiness concern, it sounds like you want sections, which give you the ability to customize pieces of your menu either in their appropriate view page or sub layout.
See http://weblogs.asp.net/scottgu/archive/2010/12/30/asp-net-mvc-3-layouts-and-sections-with-razor.aspx for an overview of sections and http://blogs.msdn.com/b/marcinon/archive/2010/12/15/razor-nested-layouts-and-redefined-sections.aspx for information of nested layouts/sections.

Layout based on Page - OrchardCMS

I have encountered an issue when using Orchard that I am sure there should be a fairly simple fix / solution for, but I have yet to find it.
I am trying to establish a specific width content area for my home page (580px), and a larger width for content pages (800px).
Layout.cshtml Snippet:
<div id='content'>
#Zone(Model.Content)
</div>
Style:
#Content
{
[...]
width: 580px;
}
Currently - the Content div wraps all of my content regardless of the page (either Home Page or Content). I am wondering if it is possible to use a different div to wrap the content based on the Page, as shown:
Layout.cshtml Idea:
#if(Model.Page != "Home")
{
<div id='fullcontent'>
#Zone(Model.Content)
</div>
}
else
{
<div id='content'>
#Zone(Model.Content)
</div>
}
I'm unsure if the above suggested method is possible (or I am unsure how to check for the current Page) - but any other suggestions would be appreciated.
You can use the Designer Tools module (built-in all recent Orchard versions) and enable the URL alternates feature. You'll then be able to create a layout-url-homepage.cshtml alternate for your layout.
You can use the Layout Selector Module to assign a custom layout to any content item.
http://gallery.orchardproject.net/List/Modules/Orchard.Module.Orchard.DesignerTools
You could use the Vandelay.Classy module to add custom tags to the page that represents your homepage, although it does add a lot of fields to the Page content editor.

EPiServer, how to link to an anchor/hashtag

I need a way to link to a section within my page, (using an anchor/hashtag).. the property that is supposed to handle this link is of the type LinkItemCollection, but however.. when I add a new link to the collection I get prompted with a new "dialog" where I can select the type of link I would like to create.. for instance I have the choices Page, Media, External link and Email..
My first thought was to use the option "External link" and then just simply type /#services, but EPiServer seams to "correct" me and change this to "http:slashslashslash#services"(slash = /)... so.. is there anyway to actually use the LinkItemCollection property and to be able to create internal links/anchor and links/hashtag-links?
Br,
Inx
Marija Jemuovic has aa aptly named blog post that covers this: Workaround for extending the LinkItemCollection to support anchors.
Unfortunately it's a bit involved:
Instead of trying to override EPiServer components, I've created a block that contains a Url property and anchor on page property, which are interdependent.
Basically, she creates a block which has a property Anchor and then uses that format the link inside a controller:
You should be able to adapt this if you need to set an anchor within a page itself.
I've done something similar when I needed to create an EpiServer Parallax website. Instead of using the link item collection I used a content area and then created a section block.
On page render I looped through all the blocks in the content area and use the friendly url and manually render the link in the menu. Would that work for you ?
private IList<NavigationItem> CreateMenu(StartPage startPage)
{
var list = new List<NavigationItem>();
return _blockHelper.GetContentsOfType<BaseTabBlock>(startPage.MainContentArea)
.Select(x => new NavigationItem() { Name = x.TabName, Link = x.TabName })
.ToReadOnlyList();
}
and this for the view
<ul class="nav navbar-nav navbar-right">
#foreach( var item in Model.Layout.Menu)
{
<li>
<a class="page-scroll" href="##item.Link">
#item.Name
</a>
</li>
}
</ul>
I have the whole project on my github if you want to download it Parallax EpiServer Sample Site

ASP.NET basic site structure for navigation

I have now browsed many forums for answer to a quite basic question, but to my surprise I can't find a good example of a very basic design that most sites uses. I am not new to programming but it was a while ago I worked more substantial with it and I am totally new to ASP.NET. I am currently working with VS2010 and building an ASP.NET MVC 3 site from scratch. The site is AJAX enabled and I am currently using both an According control for left pane navigation and a TabContainer for top tab navigation.
Everything so far is included in one master page, and the basic problem is that when selecting navigation links in the left/top menus, the entire page reloads and destroys states for the navigational menus. Also, there is totally unnecessary flickering and I see no reason for reloading the entire page, I only want to change contents of the "main area" without touching the navigation menus.
The site structure is currently built up with <DIV> elements which makes the design quite straight forward and a structure of CSS files provides great flexbility and control. From recommendations I would like to do whatever I can to avoid obsolete solutions such as IFrames. I want to make a foundation for how this can be done in a modern and flexible way, with as few compromises as possible. It is important since the system I have started might be worked on by an entire team after having passed a number of decision forums.
I have as I as said browsed quite extensively for solutions, and found many explanations on the concept of master pages that gives me input to what my problems seems to be: my navigation menus are bound to a master page, a master page is really only a "user control" that connects tightly to the content pages and these merged pages reload when I select a navigation link. And that is not supposed to happen, it is not acceptable.
I have also tried to wrap the loading of the navigational panels in "!IsPostBack" conditions, but I've come to the conclusion that the problem is not PostBack:s, the problem is that a click on a link/treelink/accordion pane in the left menu really requests a totally new page and so everything reloads and the former state is lost. Of course I could write code to store states and provide the "new page" with the last states, this would be a solution if nothing else is possible; but I really don't want to reload the entire page; I only want to reload the "main area" to decrease bandwidth need and also avoid flickering.
So I have searched for ways of only update the "main content contentplaceholder" and I see many questions on this subject but very few good answers, and especially not when I want to change the entire aspx page in the main area, not just update a number of controls (or dynamically create new ones/delete old ones etc). The reason for the urge to change the entire aspx page is that different pages will provide different functionality and it is not enough by simply manipulating some controls, but the main area should be loaded with another aspx page - from a development organisation perspective it is also good since different developers can then work on different areas of functionality in the full blown application.
I am willing to abandon the entire master page strategy, or used nested master pages, or wrap master pages in non-master pages, or use updatepanels, or use cross page postings or whatever. But I find no example of something that actually works that I can use as a start.
And, since this is basically at this point only client side updates, I can't really understand why this should become such a tricky thing to straigthen out.
So, very simply put my question is this: what would you consider to be the basic strategy for designing a site where I have navigational menus (top and left) that can load new aspx pages into a certain area (main area) and ONLY update this area and NOT the entire site.
If you can provide me with some description of this and of course preferably some demo site where only a portion of a site is updated with a new page but not the full page - it is as simple as that! - when this would be extremely welcome. Spent three days now browsing for a good recommedation and I just get more and more confused... :-(
Would be very grateful for directions.
/DT
I would go with a master page layout. Have a ContentPlaceHolder for the navigation (as many as you need) and another for the main content. You can then use something like JQuery.ajax() or ASP.NETs built in UpdatePanel to load content into the main ContentPlaceHolder when required.
With regards to the content itself I would use UserControls (.ascx files). You can pass a command to your default page to tell it which Control to load and then just load it.
I would personally use ASP.NETs UpdatePanel as you can force it to load new content using UpdatePanel.Update();.
One thing to note though is if this is for an actual website that requires search engine optimisation it may be difficult to do because the Url won't be changing so SEO will suffer.
It's a broad example but HTH.
Edit:
I would do 2 things:
Add a PlaceHolder to default.aspx (within an UpdatePanel) and expose it in the code behind as a public var so it can be access externally.
Expose the UpdatePanel so it can be accessed externally;
So on default you would have something like:
<asp:Content id="Content1" runat="server" contentplaceholderid="cphContent">
<asp:UpdatePanel id="updMain" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:PlaceHolder id="phMain" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
</asp:Content>
On Navigation Control do this for all the navigation links (changing the OnClick to suit the control to be loaded - this could be done in a repeater and dynamically):
<asp:LinkButton id="lbItem" runat="server" onClick="LoadControl1" />
Then in the codebehind (of the navigation) you could have:
protected void LoadControl1(object sender, EventArgs)
{
MyControl Con = (MyControl)LoadControl("~/MyControl.ascx");
((Default)Page).phMain.Controls.Add(Con);
((Default)Page).updMain.Update();
}
Note, you could achieve the same thing without the complexity of Master Pages but I prefer them. If you wanted to do it without a master page then you just have PlaceHolders on default and do the same thing.
Edit 2:
You could also do this using JQuery() by loading content into a div. Have your page setup (MasterPage or Normal page structure). Then have your navigation links like so:
Link 1
Then have some Javascript (using JQuery):
$(function(){
$("a.ajax_link").click(function(e){
ajaxLink(this, e);
});
});
function ajaxLink(item, e) {
var container = $("#ajax_container_wrapper");
var link;
if (e != null) {
e.preventDefault();
}
link = $(item).attr("href") + " #ajax_container_wrapper"; //Omit the + " #ajax_container_wrapper"; if you want to drag all the content in.
container.load(link);
}
And have:
<div id="ajax_container_wrapper"></div>
Where you want the new pages to appear.
This will basically load a new page specified in each of the links into the div.
Well, perhaps a step taken - your code works in the sense that it changes pages. But as far as I can tell, the entire page is still reloaded and not only the main content area which is what I struggle to do.
What I did was that I placed the javascript code - unaltered- you provided in the master page.
Edit Just realized that the java functions seem not to be called. So the anchors provide "standard" functionality, which might be a clue :-) How can I make the javascript to be called (as stated below also from code behind)? I leave the rest of this answer as is just to check for additional errors. /Edit
Then I placed two anchors on the bottom of the left nav menu (still in the master page)
<div id="navcol">
<%-- other left menu stuff --%>
Link 1
Link 2
</div>
I placed the target div in the main content section, also in the master page. I also tested to have the div id="ajax_container_wrapper" inside the contentplace holder and the contentplaceholder inside div id="ajax_container_wrapper" the unfortunately I saw no difference in behaviour.
<div id="contentcol" runat="server">
<div id="mpcpholder">
<asp:ContentPlaceHolder ID="ContentPlaceHolderMainArea" runat="server">
</asp:ContentPlaceHolder>
</div>
<div id="ajax_container_wrapper"></div>
</div>
I also tried to omit the ContentPlaceHolder but this seem not to be allowed; it gave a parser error.
So probably I done something wrong, but it is crystal clear that the full page is reloading including the top and left menus.
Also; if I get it to work with ONLY reloading the main content area; I need to do this both from e. g. treeview link and even more important from code behind. I would appreciate some advice on how to that as well.
Thanks
/DT

Partial View within a partial view design

I am stuck with a more fundamental question of how to approach the web page that I want to build; hence the title may not be very accurate.
The layout of my page is like below:
What I plan is to have a main view that contains the buttons "Page 1" and "Page 2".
Within "Page 1" user can select option 1 (default selected) or "Option 2". Each option selection displays different data in the grid. So the grid I have is a partial view. And I render it so in Page 1:
<div id="gridContainer" class="gridContent">
#Html.Partial("_DynamicGrid", Model)
</div>
I want to render Page 1 & Page 2 as partial pages within the main page. The code I have is:
<div id="pagesPartial" class="span10">
#{ Html.RenderPartial("Page1", Model);}
</div>
The page works find on load.
What I do not know how to do next is
a.) How to remove the grid and load a new one on "Option2" click
b.) How to load "Page2" view on "Page 2" button click.
Any ideas?
Thanks.
For what it's worth, I'll post what I ended up doing.
I created a partial view containing the navigation buttons on the left in the screenshot. (Page1 & Page2). And then created views for each of the pages separately rendering the navigation partial view within those views.
It was this simple and still took me time to figure out.

Resources