Note: I am new to jQuery and tabs.
I am attempting to implement nested tabs with jQuery tabs in an MVC 5 web application. For some reason, the implementation is not working correctly and I assume there is a bug based on the behavior on the website and I suspect it is with how the active tab is being set.
When the user logs in, they are taken to a page for MainAppTabs. The two top tabs are Client and Account. The Client tab has nested tabs Client Info, Billing Selections, and About whereas the Account tab currently has only one nested tab called Account, which should only display a list of accounts.
With the current implementation below, the Account tab is the first tab to be displayed, as opposed to the Client tab, along with the nested Account tab. When I click on the Client tab then it will display fine with its nested tabs. However, when I click on the Account tab again then the page clears out and I must refresh the page (F5) in order to get the Account tab and its nested tabs to display. Also, the nested tab appears to be displayed twice where it is offset to the right and has a duplicate border, but the nested tab border and data spill outside of the parent tab border.
<div id="MainAppTabs">
<ul>
<li>#Html.ActionLink("Client", "ClientTabs", "ClientSetup")</li>
<li>#Html.ActionLink("Account", "AccountTabs", "AccountSetup")</li>
</ul>
</div>
<script>
$(function () {
$("#MainAppTabs").tabs({ active: 1 });
});
</script>
ClientTabs:
<div id="ClientSetupTabs">
<ul>
<li>#Html.ActionLink("Client Info", "Edit", "ClientSetup")</li>
<li>#Html.ActionLink("Billing Selections", "BillingSelections", "ClientSetup")</li>
</ul>
</div>
<script>
$(function ()
{
$("#ClientSetupTabs").tabs({ active: 1 });
});
</script>
AccountTabs:
<div id="AccountSetupTabs">
<ul>
<li class="active">#Html.ActionLink("Accounts", "Index", "AccountSetup")</li>
</ul>
</div>
<script>
$(function ()
{
$("#AccountSetupTabs").tabs({ active: 1 });
});
</script>
This appears to be an issue with nested tabs, possibly because of the way the ajax calls are made and the scripts within your partials (scripts should not be in partials). To make this work, you can provide placeholder elements for the content of each tab and have the links reference those elements using id attributes.
The html would be
<div id="main"> // main (parent) tabs
<ul>
<li>Client</li>
<li>Account</li>
</ul>
<div id="client"> // client tabs
<ul>
<li>Client Info</li>
<li>Billing Selections</li>
</ul>
<div id="client-info">
// content for client information
</div>
<div id="billing-selections">
// content for billing selections
</div>
</div>
<div id="account"> // account tabs
<ul>
<li>Accounts</li>
</ul>
<div id="accounts">
// content for accounts
</div>
</div>
</div>
and to initialize the tabs
$('#main').tabs({ ... }); // set options as required
$('#client').tabs({ ... });
$('#account').tabs({ ... });
To display the content, use #Html.Partial() if the model in the view contains the data need to generate the partial, or #Html.Action() if you want to call a server method that returns the partial. For example, if the Edit() method of ClientSetupController returns the partial view to show in your Client Info tab, then
<div id="client-info">
#{ Html.RenderAction("Edit", "ClientSetup"); } // or #Html.Action("Edit", "ClientSetup")
</div>
Where the controller method is
[ChildActionOnly]
public PartialViewResult Edit
{
var model = ... // initialize you model for the view
return PartialView("_Edit", model);
}
and _Edit.cshtml is a partial view and contains the html you want to display in the tab
Related
I see many websites such as gitHub changing it's html content and URL without refreshing pages.
I find one possible way to do this in HTML Histroy API.
Here is the code.
HTML
<div class="container">
<div class="row">
<ul class="nav navbar-nav">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
<div class="row">
<div class="col-md-6">
<div class="well">
Click on Links above to see history API usage using <code>pushState</code> method.
</div>
</div>
<div class="row">
<div class="jumbotron" id="contentHolder">
<h1>Home!</h1>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p>
</div>
</div>
</div>
</div>
home.html
This is home page
about.html
This is about page
contact.html
That one is content page
JAVASCRIPT
<script type="text/javascript">
jQuery('document').ready(function(){
jQuery('.historyAPI').on('click', function(e){
e.preventDefault();
var href = $(this).attr('href');
// Getting Content
getContent(href, true);
jQuery('.historyAPI').removeClass('active');
$(this).addClass('active');
});
});
// Adding popstate event listener to handle browser back button
window.addEventListener("popstate", function(e) {
// Get State value using e.state
getContent(location.pathname, false);
});
function getContent(url, addEntry) {
$.get(url)
.done(function( data ) {
// Updating Content on Page
$('#contentHolder').html(data);
if(addEntry == true) {
// Add History Entry using pushState
history.pushState(null, null, url);
}
});
}
</script>
This code is working fine even you go back or forward in browser.
But the problem is that when you refresh page it only shows the file which is being refreshed. For example, if you refresh the about.html then only the following will show: This is the about page.
Unlike the gitHub it can't show the complete page. As you see in gitHub, even you refresh a page it will show the page same as how it was before refreshing.
How can I do that?
Thanks...
You may use Routie or Director to do the routing. And within their callback functions write the code to update the part of your HTML page, for this you may use Fragment.js.
You can change DOM anytime you want without loading the page.
Use fundamental XMLHTTPRequest or Ajax to contact the server without refreshing the browser.
There are many frameworks which offer convenient url routing which can change content automatically without page refreshes. Angular JS is my favorite such framework which offers great routing capability among many other things.
You have to check/set the value of your variable on the event onload of the page.
Your code does not work - when you click on a particular link the page does refresh. correct me if i am wrong.
I have 2 html page .. the first page is Header.html with 2 links (addressbook, mybook). My 2nd page is the Main-body.html has tabs with Addressbook and mybook. Since they are both different page, how can I manage to open the tabs using the links on the first page ..
example : header.html > (link)addressbook > main-body.html >(open tab addressbook)
header.html > (link)myorder > main-body.html > (open tab mybook)
using jquery ..
You can use hashchange event, and use hash to navigate directly to your content tab, like this:
$(function() {
// First hide all content element
$("#xxx,#yyy").hide();
$(window).on("hashchange", function(e){
// Hide all content elements
$("#xxx,#yyy").hide();
// Show only the desired element
$(location.hash).show();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
xxx
yyy
<p id="xxx">
xxx
</p>
<p id="yyy">
yyy
</p>
As I see, you would like to have a tabbed design on main-body.html. In this case I would use anchors:
<ul>
<li>Address Book</li>
<li>My Order</li>
<ul>
Then, you need to handle the achor with JavaScript. Depending on the value, you can show a different tab.
I currently have a modal that deletes an item from my list of data. After selecting delete, I want to refresh the web page. At this point, it is refreshing the entire page and re-directing me to the Searches tab. I want to refresh just the Lists tab. How can I do this?
My HTML:
<div class="row zero-margin">
<div class="col-xs-12">
<div id="tabstrip">
<ul id="tab-strip-options">
<li id="listItem1" class="k-state-active">
Searches
</li>
<li id="listItem2">
Lists
</li>
</ul>
<div class="saved-search-content">
<div id="gridSearch"></div>
</div>
<div class="saved-list-content">
<div id="gridList"></div>
</div>
</div>
</div>
</div>
My JavaScript Function:
function deleteItemsFromList(data) {
if(data.IsSuccess) {
window.location.reload();
CloseModal("deleteSearchListModal");
showSuccessMessage('List was successfully deleted!');
}
}
Solution for server side data store:
The easiest solution for partial update on your web page is to use AJAX requests. You must load list content from web source, when you update it.
$( "#gridList" ).load( "ajax/getListContent" );
Of course your ajax/getListContent must generate proper HTML substructure (without html, body tags) like:
<div>...</div>
Generally create a data source on address ajax/getListContent which return you your data structured in HTML, then in your code you can do like this:
function deleteItemsFromList(data) {
if(data.IsSuccess) {
$( "#gridList" ).load( "ajax/getListContent", function() {
CloseModal("deleteSearchListModal");
showSuccessMessage('List was successfully deleted!');
}
}
}
I completely ignore if you are using some kind of plugin, but if you want to refresh just part of the whole HTML document, you have to use AJAX and remove the window.location.reload() from your script. Something like this:
function deleteItemsFromList(data) {
if(data.IsSuccess) {
$.ajax(/*params here*/).done(function(data){
//refresh just the #gridList part
CloseModal("deleteSearchListModal");
showSuccessMessage('List was successfully deleted!');
}
}
}
Maybe this is not the correct order, but the important thing is AJAX :)
i have a 3-tier list that at the end goes to another page...so when i come back from the another page i want the list item to be open and indicate which item was clicked on.
how can this be done
<div data-role="collapsible" >
<h2>Header</h2>
<ul data-role="listview">
<li>item-1</li>
<li>item-2</li>
<li>item-3<span class="ui-li-count">12 set</span></li>
</ul>
</div>
-----------------------------------------------------------------
<div data-role="page" id="item1">
<div data-role="header">
Back
<h1>item1</h1>
</div>
<div data-role="content">
<div class="container">
<ul data-role="listview">
**<li>Set 1</li>**
<li>Set 2</li>
<li>Set 3</li>
</ul>
</div>
</div>
<div data-role="footer">
<h4>Footer</h4>
</div>
</div>
-----------------------------------------------------------------
now from the main list when the uset click on item-1 it is presented with another list of set-1,set-2,set-3 etc now on clicking on set-1 the user is taken to another "external page".
when the user click the back button from the external page it show indicate that the set-1 was clicked and the collapsible set should be open..currently i getting the collapsible set collapsed and there is no indication where the user was
Its very easy to acchieve. One way to do this is to use cookies to store the list item you click as you navigate to other pages.
If you decide to use this method you will need the jquery cookies plugging -- https://github.com/carhartl/jquery-cookie
I didnt have much time for the demo its a quick one but you can easy see whats happening from the demo. All i did was to give the list items an id and the (a) classes id so we know which one was clicked and which one to turn the background color to indigate that it was clicked.
If you have multiple lists that expand then store the id of the expandable listview to another cookie and expand the correct one like i did in the demo with the items.
Demo
http://jsfiddle.net/wgt88h3n/
$("#listview").on("click", ">li", function(event, ui) { // this function gets the id from the list items
var id = $(this).closest("li").attr('id');
$.cookie('item', id); // store the id of the list item to a cookie
});
$("#topage2").on("click", function(event, ui) {
var item = $.cookie('item'); // lets get the item of the cookie
$(".item1, .item2, .item3").css({backgroundColor: '#f6f6f6;'}) // lets set the background color back to normal
$.mobile.changePage( "#page2" , { transition: "pop" }) ///change to page 2
});
$("#topage1").on("click", function(event, ui) {
$.mobile.changePage( "#page1" , { transition: "pop" })
$( "#mylist" ).collapsible( "expand" ); // expand the collapsible list. if you have more lists,,, to expand the correct one, use another cookie and use the same method when we stored the list item.
var item = $.cookie('item'); /// read the cookie item
$("." + item ).css({backgroundColor: 'rgba(72, 121, 49, 0.38)'}) ///set the background color of the last item clicked to green
});
I have three tabs on the footer: home, promotions and events. I have notification badge implemented such that when a new promotion is loaded in the database, the footer of the mobile page shows 1 and if more are inserted, the number gets incremented in real time. Now, say, if I started with home page, and I see real time notifications on the promotions and events tab. But, when I navigate to the events tab, the notifications go away, but if I click back to the home tab, the notifications show up. I have tried "pageinit" and "pageshow" separately, with the alert message for test as suggested in other stackoverflow questions. Alert messages show up at each click on the tabs. But, when I tried to modify with the append or html, it does not output me with notification badge. I tried adding id to data-role=page for home, promotions and events, alerts show up, but the append or html functions do not give me results as expected. What I am missing here?
<div data-role="page">
<div data-role="header">
........
</div>
<div data-role="content">
.......
</div>
<div data-role="footer" data-position="fixed" >
<div data-role="navbar" >
<ul>
<!---- The notification badge from jquery script get appended in these IDs----->
**<li><div id="home"></div>...........</li>**
**<li><div id="promotion"></div>......</li>**
**<li><div id="event"></div>..........</li>**
</div>
</div>
</div>
I figured that if my first page is the home page, JQuery Mobile stores it in its history, and any other pages visited will be the ajax calls. And, I have been saving notification badges in the page that they belong to. for eg. if a new promotion is being added, it is being appended to promotion.mobile.erb and if a new event is being added, it is being appended to a div element in event.mobile.erb. I would be able to see it only if my current page is the home page. This was my the issue earlier.
How I resolved it?
I unDRY'ed my footer and page elements from the application.mobile.erb and added page ids to home , promotions and events pages. Then, I added different ids to the div elements where the notification badges go.
for div element in the home_page , I added 0 (you can choose any thing that makes it unique)
for div element in the promotion_page I added 1 and
for div element in the event_page I added 2 like in the code below...
<div data-role="footer" data-position="fixed" >
<div data-role="navbar" >
<ul>
<!---- The notification badge from jquery script get appended in these IDs----->
**<li><div id="home0"></div>...........</li>**
**<li><div id="promotion0"></div>......</li>**
**<li><div id="event0"></div>..........</li>**
</div>
</div>
Now, when any real time notification comes up, add it to either promotion0 or event0 div so that whenever we navigate away, we can get the old badge from the that div, and repost it to the navigated page div element using pageinit like in the example below...
$('#promo_page').live('pageinit',function(event){
$('#promotion2').badger(oldBadge0);
$('#event2').badger(oldBadge_e0);
});