I'm playing with bootstrap 3 for the first time with asp.net. I've got a basic nav pill menu where you simply add "class='active'" on whichever nav pill you want to have the selected, like so:
<ul class="nav nav-pills">
<li class="active">Home</li>
<li>Profile</li>
<li>Messages</li>
</ul>
I'd like to know if there is a best practice for setting which LI get's the active class. I have a working solution but it feels clunky.
In the markup code, I am using literals:
<li <%=NavHomeItem%> id="HomeItem" >Home</li>
<li <%=NavProfileItem%> id="ProfileItem">Profile</li>
In the code-behind, I set the literal based on which page is requested:
public String NavHomeItem = "";
public String NavProfileItem = "";
public String NavMessagesItem = "";
private void SetNavigationHtml()
{
var url = Page.Request.Url.AbsolutePath;
switch (url.ToLower()) {
case "/profile.aspx":
NavProfileItem = "class=\"active\"";
break;
case "/messages.aspx":
NavMessagesItem = "class=\"active\"";
break;
default:
NavHomeItem = "class=\"active\"";
break;
}
}
Is there a more professional way to handle this? Open to any ideas.
What I do in one of my projects - which is a very simple solution, is that on page for every top-level view - so like 'Home', 'Profile' in your case, I just have a simple JQuery snippet that sets specific pill to active, something along those lines:
$("#HomeItem").addClass("active")
That way, setting active pill, is done completely client-side.
Related
I'm struggling with the async pipe, I need to wait for the async data to finish loading before trying to render the view of the component.
I tried looking at this question where the elvis operator is mentioned:
Wait for Angular 2 to load/resolve model before rendering view/template
I tried it like {{model?['name']}} but it doesn't seem to do anything because angular stills throws an error complaining about not being able to read name of undefined.
I initialize the model on ngOnInit but the data hasn't finished loading at this point (I think):
ngOnInit() {
// If no model is set and the select shouldn't be allowed empty, set the model to the first option
if (!this.model && !this.allowEmpty) {
this.model = this.options[0];
}
// If it should be allowed empty, set it to null which will add an empty class in the markup
else if (this.allowEmpty) {
this.model = null;
}
this.onModelChange.emit(this.model);
}
Then it tries to render this bit prematurely:
<div>
<ul>
<li>
<div>{{model?['name']}}</div>
<ul>
<li *ngFor="let option of options | async">{{option['name']}}</li>
</ul>
</li>
</ul>
</div>
So what would be the correct way of achieving this? Can I set something in the component class to allow async data to finish loading before rendering the view?
You can't use ? with []
If you use
*ngFor="let option of options | async">
then option in
{{option.name}}
always has a value because no <li ... is rendered when options hasn't yet emitted data.
{{option?.name}}
would be a valid use and do what you seemed to intend to do but as mentioned above, it shouldn't be necessary.
For my learning, I'm building a document management solution with ASP.NET MVC3. Below are pages I manage:
a search/result page (list of items)
a favorite page (list of items)
an edit page
a create page
I also have a Site.Master page where I show a treeview menu on the left side of the screen. So wherever the user is located in the website, the treeview menu is showing his location by underlining his location in the menu.
For building the treeview menu, I use the code below (cleaned for easy reading):
<ul id="treemenu1" class="treeview">
<li>Documents
<ul>
<%= Html.TreeviewMenu(TreeMenu.Create("Search", "Search", "Affaires", null))%>
<%= Html.TreeviewMenu(TreeMenu.Create("Favorite", "Favorite", "Affaires", null))%>
<%= Html.TreeviewMenu(TreeMenu.Create("New", "Create", "Affaires", null))%>
</ul>
</li>
</ul>
The problem is that I need to underline the active item in my menu. So if user is displaying the search page, my search menu entry must be underlined. How can I proceed? I was thinking about the integration of this information in the strongly typed viewmodel passed to each viewpage but it failed because each page is using a different viewmodel. I prefer not using a session variable because it is not a clean solution.
Any ideas?
A solution with a session variable: I save the "current menu item" in a session variable (from my controller). So, whenever the Site.Master page is reloaded, it recreate every treeview menu item. For each one, it checks if the item is equal to the session variable. If yes, the class "selected" is added to the item (css highlighted with blue).
I don't really like using session variables. Maybe there are more elegant solutions?
How about using a helper:
public static MvcHtmlString MenuItem(
this HtmlHelper htmlHelper,
string text,
string action,
string controller
)
{
var li = new TagBuilder("li");
var routeData = htmlHelper.ViewContext.RouteData;
var currentAction = routeData.GetRequiredString("action");
var currentController = routeData.GetRequiredString("controller");
if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
{
li.AddCssClass("active");
}
li.InnerHtml = htmlHelper.ActionLink(text, action, controller).ToHtmlString();
return MvcHtmlString.Create(li.ToString());
}
and then:
<ul>
<%= Html.MenuItem("Search", "Search", "Affaires") %>
<%= Html.MenuItem("Favorite", "Favorite", "Affaires") %>
<%= Html.MenuItem("New", "Create", "Affaires") %>
</ul>
which could yield the following if you navigate to /Affaires/Favorite:
<ul>
<li>Search</li>
<li class="active">Favorite</li>
<li>New</li>
</ul>
Well, there is one easy and effective solution: let each page signal whether it's the active page and set a css class with jQuery.
Assuming your rendered html looks like:
<ul id="treemenu1" class="treeview">
<li>Documents
<ul>
<li class="search"></li>
<li class="favorite"></li>
</ul>
</li>
</ul>
At the bottom of each page, do something like (this would sit on the search view):
<script type="text/javascript">
$(document).ready(function () {
// Set active nav
$('#treemenu1 li.search').addClass('selected');
});
</script>
UPDATE based on new info
A little cleaner than a session vraiable could be to use the ViewBag property from the controller.
public ActionResult Search(/*whatever*/)
{
// do things
// set the selevted view
ViewBag.SelectedMenuItem = "search";
return View();
}
Then in your master page you can check against <%: ViewBag.SelectedMenuItem %>
Note that SelectedMenuItem is a random name. The ViewBag property is of type dynamic so you can use any property name you like.
I've inherited some code which breaks a page up into tabs using divs. On the first page there are many required field and regex validators. The problem is a user can switch to another tab, trigger a postback and fail the validators on the first page, leaving things in a mess.
What I want to be able to do is perform the validation on the first page as a user selects another tab, thus preventing them from moving to a new tab until the first page is valid.
<ul>
<li>Tab 1 </li>
<li>Tab 2</li>
<li>Tab 3</li>
</ul>
Where isValid needs to fire off the validators.
Thanks!
UPDATE: The answer provided by codeka is pretty close, however, because I need to provide both href and onclick attributes (to avoid messing up display), the tab (anchor) switch is still taking place even if validation fails. Here's how I solved this. disclaimer: ugly code ahead
<ul>
<li><a id="tab1Tab" href="#tab1" style="display:none"/><a onclick="isValid('tab1');">Tab 1</a></li>
<li><a id="tab2Tab" href="#tab2" style="display:none"/><a onclick="isValid('tab2');">Tab 2</a></li>
<li><a id="tab3Tab" href="#tab3" style="display:none"/><a onclick="isValid('tab3');">Tab 3</a></li>
</ul>
function isValid(tab) {
var valid = Page_ClientValidate();
var tabId = (valid ? tab : "tab1") + "Tab";
$("#" + tabId).click();
}
Note use of jQuery for cross-browser compatibility with click event. And this only works if there are no validators on other tabs, as per Thomas' answer, I'll need to use validation groups and extra logic in isValid if any get added.
ASP.NET creates a global javascript function Page_ClientValidate that you can call to fire the validators:
function isValid() {
return Page_ClientValidate();
}
You might try using a validation group per tab. On clicking from one tab to another, you would call something like so where the tab names represent the validation groups:
function TabValidate( tabName ) {
if ( Page_ClientValidate( tabName ) ) {
//do stuff
}
}
ADDITION I mentioned this in a comment, but I figured I would add it to my post. If there aren't any other validators on other tabs, then another solution would be to simply mark the other .NET controls on the other tabs that can trigger a postback with CausesValidation="false" (e.g. DropDownLists with AutoPostBack="true")
When I navigate on a website utilizing MasterPages, does the application know what page I am on? If so, does it store it in an object I can access?
The reason I am asking is so I can replace this:
//masterpage
<div id="nav_main">
<ul><asp:ContentPlaceHolder ID="navigation" runat="server">
</asp:ContentPlaceHolder></ul>
</div>
//content page(s)
<asp:Content ContentPlaceHolderID="navigation" ID="theNav" runat="server">
<li>Home</li>
<li id="current">FAQ</li>
<li>Videos</li>
<li>Button 4</li>
<li>Button 5</li>
</asp:Content>
With a more elegant solution for the navigation, which highlights the link to the page by having the list item's ID set to "current". Currently each page recreates the navigation with its respective link's ID set to current.
I'd concur with Chris: use a control to handle display of this menu and make it aware of what link should be highlighted. Here's a method I use regularly. It may become more complex if you've got multiple pages that would need the same link styled differently, but you get the idea.
Dim thisURL As String = Request.Url.Segments(Request.Url.Segments.Count - 1)
Select Cast thisUrl
Case "MenuItem1.aspx"
lnkMenu1.CssClass = "Current"
Case "MenuItem2.aspx"
lnkMenu2.CssClass = "Current"
End Select
To get the current request URL from within the master page you would do:
string s = this.Page.Request.FilePath; // "/Default.aspx"
I also recommend moving your navigation into the master page instead of the content page. This will make it easier to maintain / access.
Yes, Use the below code in your master file. It will give you the content page name.
Page.ToString().Replace("ASP.","").Replace("_",".")
Alternatively you can search for page title if you have set an specific title to the child page instead of masterpage try:
this.Page.Title
Hope it helps.
this is in C#
string thisURL = Request.Url.Segments[Request.Url.Segments.Length - 1];
if (thisURL.ToLower()== "default.aspx") li1.Attributes.Add("class","yekan active");
if (thisURL.ToLower() == "experts.aspx") li2.Attributes.Add("class", "yekan active");
You should be able to get the page by accessing the Page property. IE:
string type = this.Page.GetType().Name.ToString();
You'd probably just use one of the Request path from within the master page to set the current. I'd probably also have a property on the master page to override it, so that pages without links or something could set it to something reasonable.
It worked for me this way - Thanks Jared
This is what I did to get our nav menu to highlight the current menu item for the page that we are viewing. The code is in the master page.
You basically get the filepath (Jared's way)
We use the "~" in our links so I had to strip that out.
Iterate the menuItems collection of the Menu control.
Compare the navigateUrl property.
(I'm not the best coder and even worse at explaining - but it works and I was quite chuffed with it!)
protected void HighlightSelectedMenuItem()
{
string s = this.Page.Request.FilePath; // "/Default.aspx"
string nav;
if (s.Contains("~"))
{
s = s.Remove(s.IndexOf("~"), 1);
}
foreach (MenuItem item in navMenu.Items)
{
if (item.NavigateUrl.Contains("~"))
{
nav = item.NavigateUrl.Remove(item.NavigateUrl.IndexOf("~"), 1);
if (s == nav)
{
item.Selected = true;
}
}
}
}
string s = this.Page.GetType().FullName;
string[] array = s.Split('_');
int count = array.Count<String>();
string currentPage = array[count - 2];
The navigation control, not the master page, should be in charge of what page is currently highlighted.
Either the page that is loaded should notify the navigation item who it is, or the nav control itself should keep track of it.
The point is that master pages are supposed to simply be a holder that content is displayed in. They aren't supposed to control anything.
try
this.Page.Master
It will get you the master page of the current page.
There's also the Request.RawURL
What do you find to provide the best menu for an ASP.Net 2.0 - 3.5 web application? Suggestions do not have to particularly be ASP.Net controls, but could be other menu's that work well within an ASP.Net web application.
I would like for the suggestions to be options that do not require purchasing or royalty fees. OpenSource suggestions would be even better.
I've found that the most flexible is to use CSS to style an unordered list like this:
<div id="nav_main" >
<ul>
<li id="current">Button 1</li>
<li>Button 2</li>
<li>Button 3</li>
<li>Button 4</li>
<li>Button 5</li>
</ul>
</div>
You'll find many CSS ways to style this kind of list with hover background images, etc.
Now if you are using Webforms and you want to use your sitemap, then I would suggest using a Repeater and NOT use the menu control. You will have the most control of your generating your list this way.
Similarly, if you are using ASP.NET MVC, you can do a foreach on your sitemap to create your list.
This of course is just for simple menus, but it can be expanded to include more complicated menus. I've found the following CSS-styled menu to be very good: http://www.lwis.net/free-css-drop-down-menu/
YUI buttons or menu controls (works with existing HTML):
http://developer.yahoo.com/yui/examples/button/btn_example07.html
An ASP.NET library that wraps these controls very nicely, released in December 2008:
http://www.codeplex.com/YUIAspNet/
JQuery suckerfish menu, works by using ul,li elements:
http://be.twixt.us/jquery/suckerFish.php
I use jQuery Superfish: http://users.tpg.com.au/j_birch/plugins/superfish/. Highly recommended by others as well.
I also like to create unordered lists. It lets the designer be flexible with the menus. They can use their own js+css solution to create drop down menus or style it nicely for static menus. The same html can easily become left hand, across the top, drop down, or even a full site map with css changes.
To that note, I like to store the site map data in a hierarchal data structure and use a recursive lambda to generate it. For an example see this small console app below with its output.
output html
<li>First<li>FirstSub</li><li><a hr
ef="/secondsub.aspx">SecondSub</a></li></li><li><a href="/second.aspx">Second</a
></li>
the source c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SiteMapDemo
{
class MenuItem
{
public Guid ID {get; set;}
public Guid? ParentID{get;set;}
public string Name { get; set; }
public string Path { get; set; }
public int Rank { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<MenuItem> menu = new List<MenuItem>(new[]{
new MenuItem{ID = Guid.NewGuid(), Name = "First", ParentID=null, Path="/", Rank=0},
new MenuItem{ID = Guid.NewGuid(), Name = "Second", ParentID=null, Path="/second.aspx",Rank=1},
});
menu.AddRange(new[] {
new MenuItem{ID = Guid.NewGuid(), Name = "FirstSub", ParentID=menu[0].ID, Path="/firstsub.aspx",Rank=0},
new MenuItem{ID = Guid.NewGuid(), Name = "SecondSub", ParentID=menu[0].ID, Path="/secondsub.aspx",Rank=1},
});
Func<List<MenuItem>, Guid?, string> renderMenu = null;
renderMenu = (menus, Parent) =>
{
var sub = menus.Where(m => m.ParentID == Parent).OrderBy(s=>s.Rank).ToList();
if (sub.Count > 0)
{
StringBuilder sb = new StringBuilder();
sub.ForEach(s => { sb.Append(String.Format("<li>{1}{2}</li>", s.Path, s.Name, renderMenu(menus, s.ID))); });
return sb.ToString();
}
return "";
};
Console.Write(renderMenu(menu, null));
Console.ReadLine();
}
}
}