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")
Related
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.
This topic may have lots of code out there, BUT I seem to be looking for a variation that isn't based on history, is it possible...
So I have this code...
<script type="text/javascript">
var pages=new Array();
pages[0]="listItem1.html";
pages[1]="listItem2.html";
pages[2]="listItem3.html";
pages[3]="listItem4.html";
pages[4]="listItem5.html";
var i=0;
var end=pages.length;
end--;
function changeSrc(operation) {
if (operation=="next") {
if (i==end) {
document.getElementById('the_iframe').src=pages[end];
i=0;}
else {
document.getElementById('the_iframe').src=pages[i];
i++;}}
if (operation=="back") {
if (i==0) {
document.getElementById('the_iframe').src=pages[0];
i=end;}
else {
document.getElementById('the_iframe').src=pages[i];
i--;}}}
</script>
</head>
<body>
<ul id="menu" role="group">
<li>Welcome
<ul>
<li>Ease of Access Center</li>
</ul>
</li>
<li>Getting Started
<ul>
<li>Considerations</li>
<li>Changing Perspective</li>
</ul>
</li>
</ul>
<iframe id="the_iframe" scrolling="no" src="listItem1.htm" name="ifrm" style="width:540px;></iframe>
<input type="button" onClick="changeSrc('back');" value="Back" />
<input type="button" onClick="changeSrc('next');" value="Next" />
and if I click on the next or prev button, it does move somewhere,but...
let's say my iframe is showing listItem2, then I click on listItem4 in the menu (there is a tree menu involved), then I want to go to listItem3 and I hit the back button...instead of going to listItem3, it goes to listItem2 (or someplace that is not back a page from 4 to 3).
It appears that the buttons are navigating based on history?...but I just want a straight forward or backward movement...I don't want my buttons to have this browser-type functionality...If I'm on listItem4 and hit the next button, I want it to go to listItem5.
Many Thanks For Any Help!
Okay, I'll try the code, but don't down-rate it if its off.
This is the function that you could put before the changeSrc function:
function UpdateI(value) {i = value}
This the one click event that you would add to your links in the a tag. Off course, the 4 that is sent the function in this case, would be changed to whatever is appropriate for whatever ListItem is being referenced:
onClick ="UpdateI(4)"
Does this help you?
I don't understand your code here:
document.getElementById('the_iframe').src=pages[i];
i++;
If that is going to advance to the next element AND display it, you need to increase i first. Perhaps I'm just missing something, though.
I think your problem is that if the user clicks one of your direct links, rather than "next" and "previous" i in your code is not getting updated. So if your on page 2 and click the link for 4 and then click back, i is currently 2 and not 4. Hope that helps you. Be sure mark it as the answer if it does.
add an on click event to each of the link tags that would call a single function just like you have in the input tags. Have the function take an input of some number and assign that number to i. That should do it. Sorry I cant show any source code, JAVASCRIPT is not my language. I can read it but I wouldn't dare to write code from scratch. Hope this helps you.
I have a simple unordered list with links in it.
<body>
<div id="topMenu">
<ul>
<li><a class="selected" href="../Default.aspx">Start</a></li>
<li>Categories</li>
<li>Elements</li>
<li>Articles</li>
</ul>
</div>
<asp:ContentPlaceHolder ID="MainContentPlaceholder" runat="server">
</asp:ContentPlaceHolder>
</body>
I want to change the class of the link i click to the "selected" class, which is the easiest way to do this. I thought about making it into linkbuttons and saving the info in the session, but that seems overkill, there has to be an easier approach?
Well, you can just put the <a> to be runat="server" and then access them from codebehind:
hyperLink.Attributes["class"] = "opened";
You can have this in your master page with the menu, and you can have your child pages send the id of the hyperlink they need selected, something like
In your master (the linkWrapper can be your <ul> as runat server):
(HyperLink)linkWrapper.FindControl(childPageMenuAnchorLinkId);
In your child:
masterPage.ChildPageMenuAnchorLinkId = "link_Customers";
If you are using jQuery, the link from this other SO question may help you out.
jQuery add class .active on menu
EDIT:
Corrected script to use the 'selected' class, not 'active' according to OP's use.
So, if I "borrow" the script from the other answer, I believe this will work, again, if you are using jQuery...
$(function(){
var url = window.location.pathname,
urlRegExp = new RegExp(url.replace(/\/$/,'') + "$"); // create regexp to match current url pathname and remove trailing slash if present as it could collide with the link in navigation in case trailing slash wasn't present there
// now grab every link from the navigation
$('#topMenu ul li a').each(function(){
// and test its normalized href against the url pathname regexp
if(urlRegExp.test(this.href.replace(/\/$/,''))){
$(this).addClass('selected');
}
});
});
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.
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