Client Callbacks With Master Pages - asp.net

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;
}

Related

fill DropdownList from xml using ajax

I have on one page a dropdownlist which I would like to use AJAX in order to populate it from a XML file. Is there a way to tell AJAX to run only certain asp.net method without using WebServices? Any other solution is welcome but the only restriction is that it would be made on the server side (and not with js for example)?
thanks!
This is possible through a variety of means - one approach is to use jQuery on the client-side to generate the AJAX request like so (binding to the page ready here, but it could be bound to the SELECT change event):
$(document).ready( function () {
$.get('/target-url.aspx?someparam=somevalue', function(data) {
// process the returned data - dependant on the format - assuming JSON here.
var items = data['items'];
// may wish to clear the contents of the SELECT box.
// spin through and add OPTION elements
for(var i = 0; i < items.length; i++) {
$('#selectid').append('<option>'+items[i]+'</option>');
}
}
}
Where selectid is the ID of the dropdownlist element (use the ClientId if in ASP.NET).
Then you need to write some code in ASP.NET to respond to the AJAX request with your desired logic.
Some useful links:
http://api.jquery.com/jQuery.get/
http://api.jquery.com/append/
See here for an example of using jQuery and ASP.NET with JSON:
http://encosia.com/use-jquery-and-aspnet-ajax-to-build-a-client-side-repeater/

ASP.NET postbacks lose the hash in the URL

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');
}
});

ASP.NET MVC Submitting Form Using ActionLink

I am trying to use link to submit a form using the following code:
function deleteItem(formId) {
// submit the form
$("#" + formId).submit();
}
Basically I have a grid and it displays a number of items. Each row has a delete button which deletes the item. I then fire the following function to remove the item from the grid.
function onItemDeleted(name) {
$("#" + name).remove();
}
It works fine when I use a submit button but when I use action link the JavaScript from the controller action is returned as string and not executed.
public JavaScriptResult DeleteItem(string name)
{
var isAjaxRequest = Request.IsAjaxRequest();
_stockService.Delete(name);
var script = String.Format("onItemDeleted('{0}')", name);
return JavaScript(script);
}
And here is the HTML code:
<td>
<% using (Ajax.BeginForm("DeleteItem",null, new AjaxOptions() { LoadingElementId = "divLoading", UpdateTargetId = "divDisplay" },new { id="form_"+stock.Name }))
{ %>
<%=Html.Hidden("name", stock.Name)%>
<a id="link_delete" href="#" onclick="deleteItem('form_ABC')">Delete</a>
<% } %>
</td>
My theory is that submit button does alter the response while the action link simply returns whatever is returned from the controller's action. This means when using submit the JavaScript is added to the response and then executed while in case of action link it is simply returned as string.
If that is the case how can someone use action links instead of submit buttons.
UPDATE:
Seems like I need to perform something extra to make the action link to work since it does not fire the onsubmit event.
http://www.devproconnections.com/article/aspnet22/posting-forms-the-ajax-way-in-asp-net-mvc.aspx
My guess is the MS Ajax form knows how to handle a JavaScriptResponse and execute the code whereas your plain old Action link, with no relationship to the AjaxForm, does not. I'm pretty sure the MS ajax library essentially eval()s the response when it sees the content type of javascript being sent back.
Since you have no callback in your deleteItem() method there is no place for the script to go. To fix you'll have to manually eval() the string sent back which is considered a bad practice.
Now I'm not familiar with the MS Ajax library to be certain of any of this but what your doing is possible. I'd do things differently but don't want to answer with a "my way is better" approach ( especially because your blog has helped me before ) but I'd like to show this can be easier.
I'd ditch the form entirely and use unobtrusive javascript to get the behavior you want. IN psuedo jqueryish ( don't know ms ajax ) code:
function bindMyGrid() {
$('.myDeleteLink').onclicksyntax( function() {
//find the td in the row which contains the name and get the text
var nameTdCell = this.findThisCellSibling();
//do an ajax request to your controller
ajax('myUrl/' + nameTdCell.text(), function onSuccessCallback() {
//get the tr row of the name cell and remove it
nameTdCell.parent().remove();
});
});
}
This also gains the benefit of not returning javascript from your controller which some consider breaking the MVC pattern and seperation of concerns. Hope my psuedo code helps.
Try without the UpdateTargetId property in the AjaxOptions (don't specify it)
new AjaxOptions() { LoadingElementId = "divLoading" }
What about just change look of a standard or using some css class? It'll look like a link and you'll avoid some problems you get with anchors - user will be able to click on it by a mouse wheel and open that link in a new tab/window.

Passing arguments from one asp.net page to another using jQuery

I need to pass 4 arguments (3 strings and one comma separated list) from an ASP.NET page to another ASP.NET page using jQuery. The destination page ought to be launched as a separate window, which works fine with the following jQuery snippet:
$('#sourcePageBtn').click(function(){
window.open("destinationPage.aspx");
return false;
});
How can I pass the arguments to the destination page? I am trying to avoid the query string to pass the arguments because:
I don't want to show the url arguments (which can also be very long) in the destination window.
There are some special characters like ',/,\, & etc. in the string arguments.
Please suggest.
Edit:
I'm trying to access the arguments in the script section of the aspx file i.e.
<script language="C#" runat="server">
protected void Page_Load ( object src, EventArgs e)
{
//Creating dynamic asp controls here
}
</script>
My specific need for the arguments in the Page_Load of the script section stems from the fact that I am creating a few dynamic Chart controls in the Page_Load which depend on these arguments.
cheers
Initial Thoughts (before solution created)
Use POST for large data instead of GET. With POST no querystring will be used for data and therefore URL length restriction isn't a concern. (The max URL length differs between browsers so you're right to stay away from it when large data is moving).
Special URL characters can be encoded to be passed in the query string so that shouldn't be an issue.
Alternatively you might store the data on the server side from the first page, and have the second page pick it up from the server side. But this is overkill. And it makes you do unneeded server programming.
Passing state via HTTP calls is standard practice. You shouldn't try to circumvent it. Work with it. All the facilities are built in for you. Now it's just up to jQuery to provide us some help...
Note: Be careful using jQuery for main app features in case JavaScript is disabled in the browser. In most cases your web application should be usable at a basic level even when JavaScript is disabled. After that's working, layer on JavaScript/jQuery to make the experience even better, even awesome.
Edit: Solution (with ASP.NET processing)
Key resources for solution implementation are:
How use POST from jQuery - initiates the request, passes arguments, gets response
jQuery context argument - this is how the popup window DOM is accessed/affected from the main window
How it works: From a main page, a POST occurs and results are displayed in a popup window. It happens in this order:
The main script opens a popup window (if it doesn't already exist)
main script waits for popup window to fully initialize
main script POSTs (using AJAX) arguments to another page (sends a request)
main script receives response and displays it in the popup window.
Effectively we have posted data to a popup window and passed arguments to the processing.
Three pages follow and they constitute the complete solution. I had all 3 sitting on my desktop and it works in Google Chrome stable version 3.0.195.38. Other browsers untested. You'll also need jquery-1.3.2.js sitting in the same folder.
main_page.html
This is the expansion of the logic you provided. Sample uses a link instead of a form button, but it has the same id=sourcePageBtn.
This sample passes two key/value pairs when the POST occurs (just for example). You will pass key/value pairs of your choice in this place.
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
</head>
<body>
<a id="sourcePageBtn" href="javascript:void(0);">click to launch popup window</a>
<script>
$(function() {
$('#sourcePageBtn').click( function() {
// Open popup window if not already open, and store its handle into jQuery data.
($(window).data('popup') && !$(window).data('popup').closed)
|| $(window).data('popup', window.open('popup.html','MyPopupWin'));
// Reference the popup window handle.
var wndPop = $(window).data('popup');
// Waits until popup is loaded and ready, then starts using it
(waitAndPost = function() {
// If popup not loaded, Wait for 200 more milliseconds before retrying
if (!wndPop || !wndPop['ready'])
setTimeout(waitAndPost, 200);
else {
// Logic to post (pass args) and display result in popup window...
// POST args name=John, time=2pm to the process.aspx page...
$.post('process.aspx', { name: "John", time: "2pm" }, function(data) {
// and display the response in the popup window <P> element.
$('p',wndPop.document).html(data);
});
}
})(); //First call to the waitAndPost() function.
});
});
</script>
</body>
</html>
popup.html
This is the popup window that is targeted from the main page. You'll see a reference to popup.html in the jQuery script back in the main page.
There's a "trick" here to set window['ready'] = true when the popup window DOM is finally loaded. The main script keeps checking and waiting until this popup is ready.
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
</head>
<body>
<!-- The example P element to display HTTP response inside -->
<p>page is loaded</p>
</body>
<script>
$(function() {
window['ready'] = true;
});
</script>
</html>
process.aspx.cs (C# - ASP.NET process.aspx page)
The dynamic server page the arguments are POSTed to by the main page script.
The AJAX arguments arrive in the Page.Request collection.
The output is delivered back as plain text for this example, but you can customize the response for your apps requirements.
public partial class process : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
// Access "name" argument.
string strName = Request["name"] ?? "(no name)";
// Access "time" argument.
string strTime = Request["time"] ?? "(no time)";
Response.ContentType = "text/plain";
Response.Write(string.Format("{0} arrives at {1}", strName, strTime));
}
protected override void Render(System.Web.UI.HtmlTextWriter writer) {
// Just to suppress Page from outputting extraneous HTML tags.
//base.Render(writer); //don't run.
}
}
Results of this are displayed into the popup window by the original/main page.
So the contents of the popup window are overwritten with "[name] arrives at [time]"
Main References: HTTP Made Really Easy, jQuery Ajax members and examples.
If you keep a reference to the new window when you open it, ie var destWin = window.open(...) then you can access the variables and methods on the destWin window object. Alternatively you can "reach back" from the destination window with window.opener.

How can I tell whether a JavaScript file has already been included in an ASP.NET page?

I have an ASP.NET user control (.ascx file). In this user control I want to use a .js file.
So I include <script src="file.js" type"text/javascript"></script> on the page.
However, sometimes I use this user control in a webpage where this same script has already been loaded.
How can I add a <script /> declaration that will render on the page only if the page doesn't already contain another <script /> tag for the same script?
As you are using asp.net, it makes sense to do the check server-side as that will be where you choose to add the reference to the javascript file. From your .ascx file you could register with the following:
this.Page.ClientScript.RegisterClientScriptInclude("GlobalUnqiueKey", UrlOfJavascriptFile);
... from your page you just call the ClientScript object directly:
ClientScript.RegisterClientScriptInclude("GlobalUnqiueKey", UrlOfJavascriptFile);
The 'GlobalUniqueKey' can be any string (I use the url of the javascript file for this too)
If you try to register a script with the same key string, it doesn't do anything. So if you call this in your page, your control or anywhere else, you'll end up with only one reference in your page. The benefit of this is that you can have multiple instances of a control on a page and even though they all try to register the same script, it is only ever done a maximum of one time. And none of them need to worry about the script being already registered.
There is a 'IsClientScriptIncludeRegistered(stringkey)' method which you can use to see if a script has already been included under that key but it seems pretty redundant to do that check before registering as multiple attempts to register do not throw exceptions or cause any other errors.
Doing the check client-side means that, assuming the multiple javascript references are cached by the browser (they may not be), you still have multiple tags and the over head of each one causing some javascript to run. If you had 20 instances of your control on a page, you could get serious issues.
You can do one of two things client side...
Check the dom for the script tag corresponding to the javascript file your want to check
Test a variable or function you know has/will be defined within the javascript file for undefined.
Here's a quick JS function which uses JQuery to do what you need:
function requireOnce(url) {
if (!$("script[src='" + url + "']").length) {
$('head').append("<script type='text/javascript' src='" + url + "'></script>");
}
}
use something like the following:
if(typeof myObjectOrFunctionDecalredInThisScript != 'undefined') {
// code goes here
var myObjectOrFunctionDecalredInThisScript = { };
}
This tests if your object or function already exists and thus prevents redeclaration.
var storePath = [];
function include(path){
if(!storePath[path]){
storePath[path]= true;
var e = document.createElement("script");
e.src = path;
e.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
return false;
} }
use this in your main page and call function include with argument javascript file name
As you want to include javascript on one page of your site only.
If you use Asp.net(4.0) then it is very easy.
Just include following code
<script type="text/javascript" scr="filepath and name here"></script>
in ContentPlaceHolder of content page.
#Shimmy I am coming across this after a long while but maybe it might still be useful or at least help other coming from behind. To check if jquery is already loaded do this
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.x.x.min.js"><\/script>')</script>
Don't miss the escape character in the closing script tag in document.write statement.
#mysomic, why did I not think of that. thumbs up
PS: I would have loved to write this right under the line where #Shimmy wrote that jQuery itself is the script he wants to load. But dont know how to write it there. cant see a reply or anything similar link. This may sound dumb but maybe I'm missing something. Pls point it out, anybody?
If you need it from server side, and Page.ClientScript.RegisterClientScriptInclude won't work for you in case script was included by some other way other then Page.ClientScript.RegisterClientScript (for example page.Header.Controls.Add(..), you can use something like this:
static public void AddJsFileToHeadIfNotExists(Page page, string jsRelativePath)
{
foreach (Control ctrl in page.Header.Controls)
{
if (ctrl is HtmlLink htmlLink)
{
if (htmlLink.Href.ToLower().Contains(jsRelativePath.ToLower())) return;
}
if (ctrl is ITextControl textControl
&& (textControl is LiteralControl || textControl is Literal))
{
if (textControl.Text.ToLower().Contains(jsRelativePath.ToLower())) return;
}
if (ctrl is HtmlControl htmlControl)
{
if (htmlControl.Attributes["src"]?.ToUpper()
.Contains(jsRelativePath.ToUpper())) return;
}
}
HtmlGenericControl Include = new HtmlGenericControl("script");
Include.Attributes.Add("type", "text/javascript");
Include.Attributes.Add("src", page.ResolveUrl(jsRelativePath));
page.Header.Controls.Add(Include);
}

Resources