ASP.NET postbacks lose the hash in the URL - asp.net

On an ASP.NET page with a tabstrip, I'm using the hash code in the URL to keep track of what tab I'm on (using the BBQ jQuery plugin). For example:
http://mysite.com/foo/home#tab=budget
Unfortunately, I've just realized that there are a couple of places on the page where I'm using an old-fashioned ASP.NET postback to do stuff, and when the postback is complete, the hash is gone:
http://mysite.com/foo/home
... so I'm whisked away to a different tab. No good.
This is a webforms site (not MVC) using .NET 4.0. As you can see, though, I am using URL routing.
Is there a way to tell ASP.NET to keep the hash in the URL following a postback?

The problem is that the postback goes to the url of the current page, which is set in the action of the form on the page. By default this url is without #hash in asp.net, and its automatically set by asp.net, you have no control over it.
You could add the #hash to the forms action attribute with javascript:
document.getElementById("aspnetForm").action += location.hash
or, if updating an action with a hash already in it:
var form = document.getElementById("aspnetForm");
form.action = form.action.split('#')[0] + location.hash
just make sure you execute this code on window.load and you target the right ID

I tried to put the code from Willem's answer into a JS function that got called everytime a new tab was activated. This didn't work because it kept appending an additional #hash part to the URL every time I switched tabs.
My URL ended up looking like http://myurl.example.com/home#tab1#tab2#tab3#tab2 (etc.)
I modified the code slightly to remove any existing #hash component from the URL in the <form> element's action attribute, before appending on the new one. It also uses jQuery to find the element.
$('.nav-tabs a').on('shown', function (e) {
// ensure the browser URL properly reflects the active Tab
window.location.hash = e.target.hash;
// ensure ASP.NET postback comes back to correct tab
var aspnetForm = $('#aspnetForm')[0];
if (aspnetForm.action.indexOf('#') >= 0) {
aspnetForm.action = aspnetForm.action.substr(0, aspnetForm.action.indexOf('#'));
}
aspnetForm.action += e.target.hash;
});
Hope this helps someone!

I have another solution, implemented and tested with chrome, IE and safari.
I am using the "localStorage" object and it suppose to work all the browsers which support localStorage.
On the click event of tab, I am storing the currentTab value to local storage.
$(document).ready(function() {
jQuery('.ctabs .ctab-links a').on('click', function(e) {
var currentAttrValue = jQuery(this).attr('href');
localStorage["currentTab"] = currentAttrValue;
// Show/Hide Tabs
jQuery('.ctabs ' + currentAttrValue).show().siblings().hide();
// Change/remove current tab to active
jQuery(this).parent('li').addClass('active').siblings().removeClass('active');
e.preventDefault();
});
if (localStorage["currentTab"]) {
// Show/Hide Tabs
jQuery('.ctabs ' + localStorage["currentTab"]).show().siblings().hide();
// Change/remove current tab to active
jQuery('.ctabs .ctab-links a[href$="' + localStorage["currentTab"] + '"]').parent('li').addClass('active').siblings().removeClass('active');
}
});

Related

Client Callbacks With Master Pages

I'm following this example: http://msdn.microsoft.com/en-us/library/ms178210.aspx
And I can get it to work with just a single page and a code behind, but when I add a masterpage, the examples doesn't work properly. Within my master page, I have a head content section and a body content section. It's nothing fancy.
How do i do client callbacks with master pages?
A more scalable approach would be to use the following syntax (replace the ResultsSpan with an aspx Panel too)...
function LookUpStock()
{
var lb = document.getElementById('<%=ListBox1.ClientID%>');
var product = lb.options[lb.selectedIndex].text;
CallServer(product, "");
}
function ReceiveServerData(rValue)
{
document.getElementById('<%=ResultsSpan.ClientID%>').innerHTML = rValue;
}
This way, if the name (or actual page) of the MasterPage changes the code will still work.
Basically the ASP.NET process parses the page and replaces the <%=%> directives with the correct name of the control on the client.
This approach will also work if you have nested controls. In your example, if you had a control nested inside another panel the rendered id could look something like MASTERPAGEPREFIX_CONTAINERCONTOLNAME_ListBox1 and then your work around would fail.
As a general principle its normally considered a bad idea to "hard code" client side ids in your markup - let the ASP.NET process handle it for you
I got it to work.
Be sure that you amend this code to account for the MasterPage contentId prefix:
function LookUpStock()
{
var lb = document.getElementById("MASTERPAGEPREFIX_" + "ListBox1");
var product = lb.options[lb.selectedIndex].text;
CallServer(product, "");
}
function ReceiveServerData(rValue)
{
document.getElementById("MASTERPAGEPREFIX_" + "ResultsSpan").innerHTML = rValue;
}

asp.net mvc JavaScript in View User Controls rendered through regular browser request and AJAX request

I have this code in some of my ASCX files:
<%=Html.ActionLink(Resources.Localize.Routes_WidgetsEdit, "Edit", "Widget",
new { contentType = Model.ContentType, widgetSlug = Model.Slug, modal=true},
new
{
rel = "shadowbox;height=600;width=700",
title = Resources.Localize.Routes_WidgetsEdit,
#class = "editWidget"
})%>
Take note of that rel="shadowbox..." there. This is to wire up ShadowBox Lightbox clone for this ActionLink.
This works fine when user requests a page containing this User Control thru normal browser request. But I also render/build those View User controls trough AJAX requests. For instance, I would make request to /Widget/RenderToString/... using jQuery .ajax() method and it would return HTML code for that control. This works fine and it renders the code fine. I would then insert (append) the result to a DIV in a page from where the AJAX request was made. This also works fine and the returned HTML gets appended. The only problem is - ShadowBox is not wired up. Even though the code for it gets rendered.
It seems it requires page reload (F5) every time to wire ShadowBox up. Since I am doing AJAX GET and instant append to get rid of having to make a server roundtrip, I would also want ShadowBox to wire up without doing refresh.
Can someone help me with that? Thank you
UPDATE:
Yes, I have this in my Site.Master head:
<script src="<%=Url.Content("~/Scripts/shadowbox-build-3.0rc1/shadowbox.js") %>" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
// insert functions calls here that provide some default behaviour
externalLinks();
});
Shadowbox.init({
language: "en",
players: ["img", "html", "iframe"],
onClose: function() { location.reload(true) }
});
</script>
How do I init the Shadowbox again after AJAX call?
There are many shadowbox plugins... which one are you using? (I can't give you exact code without it.) In any case I imagine you have something in your $(document).ready(function () { ... }); that tells shadowbox plungin to bind itself. You need to call that again after the AJAX call.
Just found the solution here
// call this after adding the new HTML to the page
// set up all anchor elements with a "editWidget" class to work with Shadowbox
Shadowbox.setup("a.editWidget", {});

Active Index is not being persisted in jQuery accordion change event

I have an asp.net aspx page and on that page I have a hidden input field with and id of paneIndex. However, when I load the page, the alert shows index 1 which is correct on the first load, but if I open up pane 3 for example, the alert shows 1 still. Am I doing something wrong?
In a Custom.js file, I have the following code:
$(document).ready(function() {
$("#accordion").accordion({
active: 1,
collapsible: true,
autoHeight: false,
change: function(event, ui) {
var activeIndex = $("#accordion").accordion('option', 'active');
$("#paneIndex").val(activeIndex);
//alert(activeIndex);
}
});
});
In my server side button click, I have the following code:
string activeIndex = Request.Form["paneIndex"];
string script = string.Format(#"<script type=""text/javascript"">var paneIndex =
{0};</script>", activeIndex);
if(!ClientScript.IsStartupScriptRegistered("JSScript"))
ClientScript.RegisterStartupScript(this.GetType(),"JSScript", script);
I have just tested the jquery script locally here and it works fine for me without form submission / postback.
Therefore I assume your issue is related to the form submission / activeIndex variable not being set correctly.
If you use asp.net, do you need to submit the form instead of using postbacks?
I always try to do a postback to the server if possible instead of form submission.
If you use Visual Studio you could also try to set a breakpoint on the server-side code and investigate the Request.Forms collection contains the correct variables after submission.

Jquery code on load not firing

I have the following JQuery code in a external JS file linked into a
usercontrol in .Net 1.1 webapp.
The usercontrol is a timesheet.
When the page loads it calls MonthChange and works fine in one page.
But now I want to load the timesheet/usercontrol into aother
webpage that pops up a in a new browser window for printing.
Problem is my MonthChange is not firing.
Any ideas why???
$(function() {
MonthChange();
//TestData();
$('[class^=TSGridTB]').blur(function() {
var day = GetDay($(this).attr('id'));
var date = GetRowDate(day);
var bgcolor = GetInputFieldColor(date, false);
$(this).css("background-color", bgcolor);
$(this).parent().css("background-color", bgcolor);
//CalcHours($(this).get(0));
});
$('[class^=TSGridTB]').focus(function() {
var day = GetDay($(this).attr('id'));
var date = GetRowDate(day);
var bgcolor = GetInputFieldColor(date, true);
$(this).css("background-color", bgcolor);
$(this).parent().css("background-color", bgcolor);
});
$('[id$=lstMonth]').change(function() {
MonthChange();
});
});
without seeing further code, ensure that the selector is correct for the control in the new page.
The problem may be that the DOM has changed for the new page/window and JQuery does not yet know about it.
The change event
fires when a control loses the input
focus and its value has been modified
since gaining focus.
You might want to use the live event:
Binds a handler to an event (like
click) for all current - and future -
matched element.
When you bind a "live" event it will
bind to all current and future
elements on the page (using event
delegation). For example if you bound
a live click to all "li" elements on
the page then added another li at a
later time - that click event would
continue to work for the new element
(this is not the case with bind which
must be re-bound on all new elements).
Did you make sure that the new web page has jQuery script includes?
ensure you're using:
$(document).ready(
);
around your entire code block. The $ alone often does not do the trick.

Programmatically triggering events in Javascript for IE using jQuery

When an Event is triggered by a user in IE, it is set to the window.event object. The only way to see what triggered the event is by accessing the window.event object (as far as I know)
This causes a problem in ASP.NET validators if an event is triggered programmatically, like when triggering an event through jQuery. In this case, the window.event object stores the last user-triggered event.
When the onchange event is fired programmatically for a text box that has an ASP.NET validator attached to it, the validation breaks because it is looking at the element that fired last event, which is not the element the validator is for.
Does anyone know a way around this? It seems like a problem that is solvable, but from looking online, most people just find ways to ignore the problem instead of solving it.
To explain what I'm doing specifically:
I'm using a jQuery time picker plugin on a text box that also has 2 ASP.NET validators associated with it. When the time is changed, I'm using an update panel to post back to the server to do some things dynamically, so I need the onchange event to fire in order to trigger the postback for that text box.
The jQuery time picker operates by creating a hidden unordered list that is made visible when the text box is clicked. When one of the list items is clicked, the "change" event is fired programmatically for the text box through jQuery's change() method.
Because the trigger for the event was a list item, IE sees the list item as the source of the event, not the text box, like it should.
I'm not too concerned with this ASP.NET validator working as soon as the text box is changed, I just need the "change" event to be processed so my postback event is called for the text box. The problem is that the validator throws an exception in IE which stops any event from being triggered.
Firefox (and I assume other browsers) don't have this issue. Only IE due to the different event model. Has anyone encountered this and seen how to fix it?
I've found this problem reported several other places, but they offer no solutions:
jQuery's forum, with the jQuery UI Datepicker and an ASP.NET Validator
ASP.NET forums, bug with ValidatorOnChange() function
I had the same problem. Solved by using this function:
jQuery.fn.extend({
fire: function(evttype){
el = this.get(0);
if (document.createEvent) {
var evt = document.createEvent('HTMLEvents');
evt.initEvent(evttype, false, false);
el.dispatchEvent(evt);
} else if (document.createEventObject) {
el.fireEvent('on' + evttype);
}
return this;
}
});
So my "onSelect" event handler to datepicker looks like:
if ($.browser.msie) {
datepickerOptions = $.extend(datepickerOptions, {
onSelect: function(){
$(this).fire("change").blur();
}
});
}
I solved the issue with a patch:
window.ValidatorHookupEvent = function(control, eventType, body) {
$(control).bind(eventType.slice(2), new Function("event", body));
};
Update: I've submitted the issue to MS (link).
From what you're describing, this problem is likely a result of the unique event bubbling model that IE uses for JS.
My only real answer is to ditch the ASP.NET validators and use a jQuery form validation plugin instead. Then your textbox can just be a regular ASP Webforms control and when the contents change and a postback occures all is good. In addition you keep more client-side concerns seperated from the server code.
I've never had much luck mixing Webform Client controls (like the Form Validation controls) with external JS libraries like jQuery. I've found the better route is just to go with one or the other, but not to mix and match.
Not the answer you're probably looking for.
If you want to go with a jQuery form validation plugin concider this one jQuery Form Validation
Consider setting the hidden field _EVENTTARGET value before initiating the event with javascript. You'll need to set it to the server side id (replace underscore with $ in the client id) for the server to understand it. I do this on button clicks that I simulate so that the server side can determine which OnClick method to fire when the result gets posted back -- Ajax or not, doesn't really matter.
This is an endemic problem with jQuery datepickers and ASP validation controls.
As you are saying, the wrong element cross-triggers an ASP NET javascript validation routine, and then the M$ code throws an error because the triggering element in the routine is undefined.
I solved this one differently from anyone else I have seen - by deciding that M$ should have written their code more robustly, and hence redeclaring some of the M$ validator code to cope with the undefined element. Everything else I have seen is essentially a workaround on the jQuery side, and cuts possible functionality out (eg. using the click event instead of change).
The bit that fails is
for (i = 0; i < vals.length; i++) {
ValidatorValidate(vals[i], null, event);
}
which throws an error when it tries to get a length for the undefined 'vals'.
I just added
if (vals) {
for (i = 0; i < vals.length; i++) {
ValidatorValidate(vals[i], null, event);
}
}
and she's good to go. Final code, which redeclares the entire offending function, is below. I put it as a script include at the bottom of my master page or page.
Yes, this does break upwards compatibility if M$ decide to change their validator code in the future. But one would hope they'll fix it and then we can get rid of this patch altogether.
// Fix issue with datepicker and ASPNET validators: redeclare MS validator code with fix
function ValidatorOnChange(event) {
if (!event) {
event = window.event;
}
Page_InvalidControlToBeFocused = null;
var targetedControl;
if ((typeof (event.srcElement) != "undefined") && (event.srcElement != null)) {
targetedControl = event.srcElement;
}
else {
targetedControl = event.target;
}
var vals;
if (typeof (targetedControl.Validators) != "undefined") {
vals = targetedControl.Validators;
}
else {
if (targetedControl.tagName.toLowerCase() == "label") {
targetedControl = document.getElementById(targetedControl.htmlFor);
vals = targetedControl.Validators;
}
}
var i;
if (vals) {
for (i = 0; i < vals.length; i++) {
ValidatorValidate(vals[i], null, event);
}
}
ValidatorUpdateIsValid();
}
This is how I solved a simlar issue.
Wrote an onSelect() handler for the datepicker.
link text
In that function, called __doPostBack('textboxcontrolid','').
This triggered a partial postback for the textbox to the server, which called the validators in turn.

Resources