Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Wednesday, April 18, 2018

Angular - Page Layout syntax

Built-in Structural Directives

*ngIf

This adds control flow to the page for hiding and showing content based on conditional logic.

<div *ngIf='someLogic()'>
      Some content to show
</div>

*ngFor

This adds control flow to the page for looping over content like a for loop.

<div *ngFor='let obj of objects'>
      <div>{{obj.PropertyHere}}</div>
</div>

Tuesday, April 17, 2018

Angular - Creating a Component in Angular

Component

A Component has 3 parts

  • Template
    • View layout
    • Created with HTML
    • Includes binding and directives
  • Class
    • Code supporting the view
    • Created with TypeScript (.ts file)
    • Properties for data
    • Methods for logic
  • CSS
    • A CSS file for the styling needed for the component
  • Metadata
    • Extra data for Angular
    • Defined with a decorator

Example

app.component.ts file


import { Component } from '@angular/core';

@Component({
  selector: 'myApp-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Hello World';
}

  • The import pulls in dependency references
  • @Component is the metadata / decorator that says the class is a component. This is similar to an [Attribute] in C#
  • export makes the class definition available for user elsewhere.
  • The selector is a unique value for this component in the application. It is suggested you prefix selectors with something that identifies it as part of your app. This is also what is used as a tag to use this component in another component. In this case it is <myApp-root><myApp-root>
  • It is common to append "Component" to the name name of the class so that it is clear that it is a component.

app.component.html

<div>
  <h1>
    Hello{{title}}!!
  </h1>
</div>

This is the HTML that defines the layout. Anything in {{...}} tags are binds to properties in the class associated with this component.

app.component.css

h1 { text-align:center }

This is a standard CSS file, except these styles will be unique to this component automatically. They will not affect other styles in the project.

Imports

Some common Angular Modules you may need to import are:
  • @angular/core (as we have done above)
  • @angular/animate
  • @angular/http
  • @angular/router

Using the Component

This is how you would use it in index.html for example:

<body>
<myApp-root></myApp-root>
</body>

Telling index.html about our Component

In app.module.ts you list what angular modules should be included in our application#

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [ AppComponent ],
  imports: [ BrowserModule ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Angular - Running Angular application using npm / Visual Studio Code

Assumptions

Using Visual Studio Code (on Windows)

Opening Integrated Terminal

View menu -> Integrated Terminal

Install packages

Open Integrated Terminal and type:
npm install

Launching Angular Application

Open Integrated Terminal and type:
npm start
This will build app and launch in the browser.

This also launches the web server. To stop the web server type control-c at the command prompt.

Tuesday, February 14, 2017

Knockout Resources

KnockoutJS Home - Live example, extensive documentation, interactive tutorials, download, etc.
Steven Sanderson's blog on KnockoutJS - the author of Knockout
knockmeout.net - for the latest in information on knockout follow this blog
Stackoverflow forum - very active forum to ask questions on; typically get response in minutes or hours.
Google Group - quite active forum to ask questions on; typically get response in hours.

Monday, April 29, 2013

Querying QuickBase

QuickBase is a nice tool for quickly creating data-centric web applications without having to write any code. It basically allows you to define table similar to the way you would a database, but also give you roles, permissions, reports, forms, emails, etc all in one place. If you are familiar with Custom Lists in SharePoint you will see how this is very similar.

Once you have a table created in your application you may want to show that data in another web application. You can do that use a POST or GET request. For our purposes we will be doing a GET request since it doesn't require any coding and is easiest to play with.

Here are the API docs. Of particular interest are the following:
  • api_authenticate-- you will need this to get the authToken if you are not logged in
  • gen_results_table -- does all the heavy lifting
  • do_query -- use this to create your own custom query instead of a existing view
  The example below shows how to return all rows in the table that match the criteria I pass it using a GET request. You can also customize the query.

According to the sample here, you can embed the QuickBase on your page by doing something like the following:

<html><head> <script lang="javascript" src=https://yourcompanyhere.quickbase.com/db/yourdbidhere?a=API_GenResultsTable&qid=1&jht=1></script> <style> td.m { font-family:verdana; font-size:70%; } td.hd { font-family:verdana; font-size:70%; font-weight:bold; color:white;} </style> </head> <body> <h3>QuickBase.com content below:</h3> <script lang="javascript">qdbWrite();</script> </body></html>

Finding the parameters we need using the UI

While this is pretty easy to do the stuff above, you need to know what to put for the placeholders in red. First thing I recommend is log into www.quickbase.com using your favorite browser. Click on the tab for the application you want to access. Next click on one of the reports. Now take note of the url. It should map pretty closely to the following:

https://yourcompanynamehere.quickbase.com/db/yourdbidhere?a=q&qid=1

The host will be your host. The stuff after db/ and before ?a= will be your dbid. The qid variable is the id (integer) of the report you want to use. In this example, the report id is 1. Now, just use those same values in the url for the javascript src attribute (replacing the items in red with the values you see in the url to the report).

POTENTIAL MAJOR SECURITY ISSUE:
In the above example, we will not be getting the authToken (QuickBase calls it a ticket) and instead assume that you are already logged into QuickBase.com. However, if you are trying to display the QuickBase.com on your own web page and you will be using a functional account for QuickBase.com instead of each user that comes to your website having a QuickBase.com login also you will need to get the authToken programmatically.  Read this discussion on how you might do this. The short answer is you COULD (but SHOULD NOT) pass the username and password via the url in the browser's address bar, because this is dangerous because even HTTPS does not hide urls stored in browser history. Thankfully, the url is enrypted from everyone except the browser and server computers. The url will be on the QuickBase.com log files, but they already have access to your data so it should not be an issue.

So, I suggest making a HTTP POST request ON THE SERVER-SIDE (not client-side such as JavaScript) and using SSL to protect the functional username and password of the account that will be accessing QuickBase.com. Please note, JavaScript is accessible to anyone that cares to read it, so it is not a good way to do the HTTP POST. I recommend doing this on the serverside and passing it to your page. Keep in mind the authToken (ticket) in in the url for the JavaScript so, end users could get the content just by going to going to the url and doing exactly what we are doing here. This not a huge issue since they already have access to the page you are displaying the data. To minimize how long someone can use the url, you may want to make the ticket expire after 1 hour. 

Monday, June 27, 2011

Show JavaScript alert() or execute other code from Code-Behind

I find that sometimes I need to show the user a JavaScript alert for something I don’t want them to be able to miss and I only want to show it based on some server-side (Code-Behind) code. This will work for most any chunk of JavaScript code as well.

Below is the code that you can put in your Page_Load()

if (!Page.ClientScript.IsClientScriptBlockRegistered("MyMadeUpNameHere"))
{
    string jscript = "alert('test here');";
    Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "MyMadeUpNameHere", jscript, true);
}

Notice that MyMadeUpNameHere is some unique string that allows you to not have multiple copies of your code on the page. This ensures that you will only have it execute once. If you don’t want that functionality then don’t do the check.

In this example, the user will see the ‘test here’ in a JavaScript alert (Message Box).

Note, that the RegisterClientScriptBlock puts the JavaScript immediately below the opening form tag. If your code references any form elements or any other elements on the page this is most likely going to be too early since the page has not finished loading. This makes this method good for simple things like I did here that doesn’t really care about the page, or you can use it to register JavaScript functions that is executed later in the page or in an event handler.

If you want your code to execute LATER then I recommend using the RegisterStartupScript and IsStartUpScriptRegistered methods instead. This code executes at the end of the page after all the items on the page have finished loading. Here is the same code as above, but with the StartUp versions.

if (!Page.ClientScript.IsStartupScriptRegistered("MyMadeUpNameHere"))
{
    string jscript = "alert('test here');";
    Page.ClientScript.RegisterStartupScript(this.GetType(), "MyMadeUpNameHere", jscript, true);
}

Like I said before, if you had more complex logic you could put it earlier in the page and just execute the function using the RegisterStartupScript. You can also put your complex logic in functions in JScript include and use the RegisterClientScriptInclude. See here for more details on that.

I don’t know why I can never remember which methods to use for showing a JavaScript alert or executing other JavaScript code when the page loads, so I am writing in down for me and everyone else to have.

Tuesday, October 6, 2009

Add JavaScript file to Master Page in ASP.NET using code-behind

Sometimes it is necessary to include a JavaScript file in your Master Page or any ASP.NET page. There is no problem if all your pages are in the same directory or not using dynamic data and routing. When you start messing with the pattern of the url, hard coded references to a JavaScript file quickly get broken.

Thankfully, there is a relatively easy way to add the JavaScript file using code-behind.

Page.ClientScript.RegisterClientScriptInclude("Validation.js", ResolveClientUrl("~/jscripts/Validation.js"));

In the above example, I have set the key to “Valiadtion.js”, but this could be anything unique. The second parameter uses the ~ so that the proper path is used when developing in a virtual directory and deploying to a separate web site the url works in both cases.

When the html page is generated and sent to the browser, you will see that path changes depending on what directory your page is in. Which is exactly what we needed; a smart path to the JavaScript file.

Friday, March 6, 2009

ASP.NET GridView Scrollable area and Fixed Header Solution

I have search long and hard and spent much of my own time trying to find a good Fixed Header and Scrollable rows for the ASP.NET GridView. I think I have finally come up with a combination of my own efforts and the best of borrowed from other solutions. Unfortunately, I didn't keep track of where I got some of the code that I borrowed. My apologies to the original authors.

This solution attempts to remedy all the buggy solutions I found on the internet. Some of the common problems were

  • The solution did work for the GridView
  • The solution assumed we had more control of the generated html from the GridView
  • The solution required modifying the html generated by the GridView
  • The solution stopped working when windows is resized
  • The solution assumed that there was only one scrollable area on the page. (To be fair, my solution doesn't assume this, but it would require some duplication of style sheets, jscript code, etc.)

Here is a sample implementation of my solution. It works quite well with only one scrollable area that has a fixed header. You will need to change duplicate the JavaScript file and CSS and all references to "container" if want more than one scrollable area that has a fixed header. Another alternative is to generate a customized .css .js file based on a special url. That is beyond the scope of this though.

There are several key things I would like to point about the .aspx page. The .js and .css are just really items you need to reference and don't really require any changes (except as noted above). So, I really just want to highlight what you would need to add to your page (that has a working GridView that does not have any special Fixed column header) to make this solution work.

  1. The DOCTYPE line is VERY important. This is NOT the default that Visual Studio adds to your page. Replace the line that Visual Studio puts in your .aspx page with the one shown below.
  2. Copy and Paste the GridView1_PreRender event handler to your .cs file. If you have a different name for your GridView you will need to change references to it to make the name you gave it. You will also want to set the height and width that you want. A word of warning, I did not implement the width yet, so actually that doesn't do anything. Currently the width of the GridView is not set here. Don't forget to change the references to GridView1 in the literals to match the name you use. Also, un/comment the appropriate example. If you want the GridView to be the Maximum height available and expand as the window resizes use the SetFixedHeaderWithMaxHeight call. Otherwise, if you want a fixed height, use the SetFixedHeader example.
  3. Register the GridView1_PreRender event handler with the GridView1. One easy way to do this is to add the following line to the GridView1 tag.
    OnPreRender="GridView1_PreRender"
    NOTE: You will of course need to make this match what you specified in step 2.
  4. Create a .css file by copying the CSS lines below into its own file. Alternatively, you could just include it in a style tag in the head of the page, though I don't recommend this approach.
  5. Create a .js file by copying the JavaScript lines below into its own file. Alternatively, you could just include it in a script tag in the head of the page, though I don't recommend this approach.
  6. Reference the .js and .css file in the head of the page.
  7. Copy and Paste the HeaderStyle tag in the GridView.
  8. Copy and Paste the DIV tag that has the id="container" and is directly around the GridView. It is important that there is no other DIV tags or other tags in between the DIV and the GridView. If you change this, you will need to make changes to the CSS and JavaScript.

<%@ Page Language="C#" %>

<!-- This comment keeps IE6/7 in the reliable quirks mode -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "
http://www.w3.org/TR/html4/loose.dtd">


<script runat="server">
    protected void GridView1_PreRender(object sender, EventArgs e)
    {
        if (GridView1.Rows.Count > 0)
        {
            //This replaces <td> with <th> and adds the scope attribute
            GridView1.UseAccessibleHeader = true;

            //This will add the <thead> and <tbody> elements
            GridView1.HeaderRow.TableSection = TableRowSection.TableHeader;
        }

        GridView1.Style["border-collapse"] = "separate";

        string height = "450px";
        string width = "200px";
        string gv1FixedHeaderJScript = string.Format("SetFixedHeader('{0}', '{1}', '{2}');", GridView1.ClientID, height, width);

        // MAX Height Example
        string gv1FixedHeaderJScript = string.Format("SetFixedHeaderWithMaxHeight('{0}', '{1}', '{2}');", GridView1.ClientID, width, "20px");

        // Fixed Height Example
        //ScriptManager.RegisterStartupScript(this, this.GetType(), "gvGridView1FixedHeaderKey", gv1FixedHeaderJScript, true);


    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>GridView Test</title>
    <link href="FixedHeader.css" rel="stylesheet" type="text/css" />
    <script language="JavaScript" src="FixedHeader.js"></script>
</head>
<body>
    <form id="form1" runat="server">
   
     <div id="container" style="border-style:none;">
        <asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1"
            OnPreRender="GridView1_PreRender">
         <HeaderStyle CssClass="DataGridFixedHeader" />
        </asp:GridView>
       
    </div>
        <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetDelinquentActions"
            TypeName="DAL"></asp:ObjectDataSource>
    </form>
</body>
</html>


// JScript File

// This is the function that gets called to resize the scrollable area to the size we want.
// We have to use the setTimeout() with a 1ms delay IF we call this function
// from within the form tag instead of just before the bottom of the body end tag.
// The reason is that in IE any of the height and width (scrollHeight, offsetHeight, style.height)
// are not set until after the form has finished rendering. This means, that the call to the
// SetFixedHeader2 method must occur after the form has been rendered. When using master pages
// as we are doing the location we need can only be specified in the .aspx page of the master page.
// Since we use this setting for different pages we can't hard code it there.
// Also, we can't use the ScriptManager.RegisterStartupScript unless we use the
// setTimeout either. This is because using this method puts the code just before the form end tag,
// which is again not where we need it to be. However, using setTimeout allows us to use this method.
function SetFixedHeader (gvClientID, height, width)
{
    var expr = "SetFixedHeader2('" + gvClientID + "', '" + height + "', '" + width + "')"
    setTimeout(expr, 1);
}

// This is essentially the same as SetFixedHeader function except the height is
// always going to be the height on the body - bottomMargin - top of GridView.
// bottomMargin is the number of pixels that create the gap between the bottom of
// the window and the bottom of the GridView
function SetFixedHeaderWithMaxHeight (gvClientID, width, bottomMargin)
{
    // get the max height that the GridView can be
    var maxHeight = getMaxHeight() - parseInt(bottomMargin);
   
    // We need to resize the GridView when the Window is resized
    window.onresize = MaximizeGridViewScrollableArea;
       
    var expr = "SetFixedHeader2('" + gvClientID + "', '" + maxHeight + "', '" + width + "')"
    setTimeout(expr, 1);
   
    gridViewClientID = gvClientID;
    desiredHeight = maxHeight;
    desiredWidth = width;
    desiredBottomMargin = bottomMargin;
}

function MaximizeGridViewScrollableArea()
{
    SetFixedHeaderWithMaxHeight(gridViewClientID, desiredWidth, desiredBottomMargin);
}

var gridViewClientID = null;
var desiredHeight = null;
var desiredWidth = null;
var desiredBottomMargin = null;

// height - string - the height of the scrollable area in pixels (not percent) i.e. 330px
// width - string - the width of the scrollable area in pixels or percent i.e. 600px or 50%
function SetFixedHeader2 (gvClientID, height, width)
{   
    // get numeric values for height and width
    var heightNum = parseInt(height);
   
    // adjust the size since the grid view needs to be slightly smaller
    // than the container div
    var heightAdjustment = 40;
    heightNum = heightNum - heightAdjustment;
   
    // do we need scrolling or not?
   
    var gv = document.getElementById(gvClientID);
    var tbody = null;
   
    // loop through the four (or fewer) child nodes of the table
    // and find the tbody node
    for (var i=0; i<gv.childNodes.length; i++)
    {
        var child = gv.childNodes[i];
       
        if (child.tagName)
        {
            if (child.tagName.toUpperCase() == "TBODY")
            {
                tbody = child;
                // we found what we needed, exit the loop
                i = gv.childNodes.length;
            }
        }
       
    }
   
    if (tbody != null)
    {
   
        var gvDiv = GetDivGeneratedByGridView();
       
        // scrolling is needed
        if (parseInt(tbody.scrollHeight) > parseInt(heightNum))
        {
        //alert('needs scrolling');
            //tbody.style.height = height;
            tbody.style.height = (heightNum) + "px"
                     
            if (gvDiv != null)
            {
                // add the height adjustment back in for the container, so it is bigger
                gvDiv.style.height = (heightNum + heightAdjustment) + "px";
            }
        }
        // scrolling is NOT needed
        else
        {
        //alert('NO scrolling');
            tbody.style.height = '100%'
           
            if (gvDiv != null)
            {
               
                gvDiv.style.height = "100%";
            } 
        }        
    }
}

// returns the DIV surrounding the GridView.
// NOTE: This is NOTE the DIV with id="container" that we added.
// This is the DIV that is generated by the GridView when it is rendered.
// This the DIV between teh DIV with id="container" and the table that is
// generated by the GridView.
function GetDivGeneratedByGridView()
{
    // set the size of the container div to be just a little bigger
    // than the grid view
    var container = document.getElementById("container");
   
    var isIE = typeof container.children == 'object';
    var gvDiv = null;
   
    if (isIE)
    {
        gvDiv = container.children[0];   
    }
    else // Firefox
    {
        // NOTE: First childNode is a textnode that is a new line
        gvDiv = container.childNodes[1];   
    }
   
    return gvDiv;
}


// get the max height the GridView can have
// NOTE: This is based on where the GridView is vertically on the page
//       For example, if the GridView is 100 pixels from the top of the
//       top of the body, then this will return the height of the body - 100.
function getMaxHeight() {
  var div = GetDivGeneratedByGridView();
 
  myHeight = 0;
//  alert(document.body.topMargin);
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myHeight = window.innerHeight;
  } else if( document.documentElement && document.documentElement.clientHeight) {
    //IE 6+ in 'standards compliant mode'
    myHeight = document.documentElement.clientHeight;
  } else if( document.body && document.body.clientHeight) {
    //IE 4 compatible
    myHeight = document.body.clientHeight;
  }

  var maxGridViewHeight = myHeight - div.offsetTop;

  return maxGridViewHeight;
 
 
}



/*** The Fixed Header Stylesheet ***/

.DataGridFixedHeader { POSITION: relative; TOP: expression(this.parentNode.parentNode.parentNode.scrollTop-1);}

#container div {
 overflow: auto; /* so the extra columns and rows flow as needed */
 margin: 0 auto;
 }

#container table {
 width: 99%;  /*100% of container produces horiz. scroll in Mozilla*/
 
 /* Gets rid of the 1 pixel space on the top of the header that shows through when scrolling */
 border: none ! important;
 }
 
#container table>tbody {  /* child selector syntax which IE6 and older do not support*/
 overflow: auto;
 overflow-x: hidden;
 }
 
#container thead tr {
 position:relative;
 top: expression(offsetParent.scrollTop); /*IE5+ only*/
 }
 
#container table tfoot tr { /*idea of Renato Cherullo to help IE*/
      position: relative;
      overflow-x: hidden;
      top: expression(parentNode.parentNode.offsetHeight >=
   offsetParent.offsetHeight ? 0 - parentNode.parentNode.offsetHeight + offsetParent.offsetHeight + offsetParent.scrollTop : 0);
      }

#container td:last-child {padding-right: 20px;} /*prevent Mozilla scrollbar from hiding cell content*/

#container thead td, thead th {
 
 /* the background color for the header to something other than transparent
  so that the rows don't show behind while scrolling */
 background-color:white;
 
 }
 
 
/*** Purely Cosmetics ***/

#container div {
 width: 99%;  /* table width will be 99% of this*/
 height: 50px;  /* a small efault value so user won't really see resize if delay rendering 1ms. it is changed by the SetFixedHeader() javascript function. Must be greater than tbody*/
 } 

/*** print style sheet ***/

@media print {

#container div {overflow: visible; }
#container table>tbody {overflow: visible; }
#container td {height: 14pt;} /*adds control for test purposes*/
#container thead td {font-size: 11pt; }
#container tfoot td {
 text-align: center;
 font-size: 9pt;
 border-bottom: solid 1px slategray;
 }
 
#container thead {display: table-header-group; }
#container tfoot {display: table-footer-group; }
#container thead th, thead td {position: static; }

#container thead tr {position: static; } /*prevent problem if print after scrolling table*/
#container table tfoot tr {     position: static;    }

}


/*** Global Print Styles ***/
@media print {

.noprint {display: none;}

body {
 font-family:"Palatino Linotype", Georgia, Garamond, serif;
 background-image: none;
 }
 
#container {
 border: none;
 padding: 0;
 }

}

Wednesday, March 4, 2009

Making a call to the server from JavaScript using ASP.NET Client Callback (Low Level AJAX)

The concept behind AJAX is really nothing new. The basic idea is that from JavaScript we want to call a server function and have the result returned to us. The result is returned by calling a JavaScript callback function such that the result from the server is sent via a parameter to this JavaScript callback function. Typically we want to display the result dynamically by updating the HTML page we are on without a postback. To do this, the JavaScript callback function typically will change the page using JavaScript.

In ASP.NET, you can use update panels, and other high level methods of doing this, but this is essentially what is going on behind the scene. Sometimes, you may want or need to do the same thing but at a very low level.  

To add this type of functionality to your ASP.NET page, here is a list of simple ways (not the only ways) you can add this low level AJAX to your page.

Make the class for your page implement System.Web.UI.ICallbackEventHandler interface.

This interface requires that you define the following two methods.

   
string result;
    // Define method that processes the callbacks on server.
    // NOTE: This is required by the ICallbackEventHandler interface
    public void RaiseCallbackEvent(String eventArgument)
    {
        result = eventArgument + "!!!!";
    }


    // Define method that returns callback result.
    // NOTE: This is required by the ICallbackEventHandler interface
    public string GetCallbackResult()
    {
        return result;
    }

Since AJAX is asynchronous that means that after the RaiseCallbackEvent method is called that processing can take a long time, and will not block further execution. This means that when the processing is done we need to have some way of knowing that, and then doing something with that result. In our example, I am simply adding "!!!!" to the value, but this could very complex long running code. That is what the GetCallbackResult method is for. It gets called only when the RaiseCallbackEvent method has completed. You don't call it, the framework does this for you.

You may notice that we use a result variable to communicate between the two methods. I think this is a bit strange, and would have expected the result to be passed as an parameter to GetCallbackResult, but it wasn't. :) I expect this approach is nearly as simple, and arguably more flexible.

Now that we have defined what will be happen when the AJAX request is received by the server, we need to define what will happen when data is passed back to the JavaScript page.

For this, let's assume we want to display the results in a span tag on our page. For this let's assume we have something like the following defined somewhere on our page.

<span id="MyMessage">Original Content Here</span>

We need to define one methods to do something with the result once it is sent back to the browser. The result shows itself as a parameter to the JavaScript function. It would be best to define one that will get called if there is an error with the AJAX call.

The JavaScript Function would be something like this.

function DoSomethingCallback(text)
{
  var myMessage = document.getElementById("MyMessage");
  MyMessage.innerHTML = text;
}

function DoSomethingErrorHandler(text)
{
  var myMessage = document.getElementById("MyMessage");
  MyMessage.innerHTML = 'An error has occurred.';
}

 
Now all we need to do is wire up the callback. This is done by asking ASP.NET to create the callback for us. Here is the C# code that must fire on EVERY page load. So, put it in the page load event or some other event that occurs EVERY time the page loads. If you don't do this on every page load, you will get postbacks instead of AJAX calls.

In the method below the first parameter is the page that implements the ICallbackEventHandler. The second parameter is in this case the name of parameter, but it could also be a global JavaScript variable, or a constant. If it is a string constant, you will need to use the embedded single quotes. The third parameter is the name of the JavaScript function we defined above that handles the returned data. The fourth parameter is the name of the JavaScript function we defined above that handles the error. The last parameter is to designate we are doing this asynchronously.

// Get the JavaScript Callback
String jscriptCallback = Page.ClientScript.GetCallbackEventReference(this, "val", "DoSomethingCallback", "",
"DoSomethingCallbackErrorHandler", false);


In theory we are done. You can now use the value of the jscriptCallback C# variable however you want. Now, however you want to get that JavaScript code to the browser is up to you. In this example, I am taking some shortcuts to make understanding the example a bit easier. However, in real life just like other JavaScript stuff in ASP.NET, you would want to use RegisterClientScriptBlock to define the JavaScript function, instead of using a property as I have done below.

For our example, I am adding a property like the following.

public string AjaxJavaScript
{
   get
   {
      // Get the JavaScript Callback
      String jscriptCallback = 
         Page.ClientScript.GetCallbackEventReference(this, "val", 
      "DoSomethingCallback", "",
      "DoSomethingCallbackErrorHandler", false);
      return jscriptCallback;
   }
}

Now I can use this anywhere in my .aspx page to get access to the code. In most cases you will need to wrap this code in a JavaScript function since the code generated by ASP.NET has both single and double quotes. This makes it very difficult to use directly in an event handler that is defined in quotes or double-quotes. Here is the example JavaScript function.

function GetMessageFromServer(val)
{
  <%= AjaxScript %>
}

If you view the source on the page, you would see that the code looks something like the following.

function GetMessageFromServer(val)
{
WebForm_DoCallback('__Page',val,DoSomethingCallback,"",DoSomethingCallbackErrorHandler,false);
}


Notice that the second parameter here is val which must match the parameter of the surrounding function.

Now for the exciting part. Now we can actually call the function from anywhere we can use JavaScript. In the below example, I am calling the JavaScript from two HTML buttons.

<input type="button" value="12345" onclick="GetMessageFromServer(12345)"/>
<input type="button" value="111" onclick="GetMessageFromServer(111)"/>



Here is the complete source for easy copy and paste:


<%@ Page Language="C#" %>
<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<script runat="server">
   
    string result;
    // Define method that processes the callbacks on server.
    // NOTE: This is required by the ICallbackEventHandler interface
    public void RaiseCallbackEvent(String eventArgument)
    {
        result = eventArgument + "!!!!";
    }


    // Define method that returns callback result.
    // NOTE: This is required by the ICallbackEventHandler interface
    public string GetCallbackResult()
    {
        return result;
    }



public string AjaxJavaScript
{
    get
    {
       // Get the JavaScript Callback
       String jscriptCallback = Page.ClientScript.GetCallbackEventReference(this, "val",
       "DoSomethingCallback", "",
       "DoSomethingCallbackErrorHandler", false);
    return jscriptCallback;
    }
}
   
</script>


 


<html>
<head>
    <title>Low Level AJAX Example</title>
   
    <script type="text/javascript">
function DoSomethingCallback(text)
{
  var myMessage = document.getElementById("MyMessage");
  myMessage.innerHTML = text;
}


function DoSomethingCallbackErrorHandler(text)
{
     var myMessage = document.getElementById("MyMessage");
  myMessage.innerHTML = 'An error has occurred.';
}


function GetMessageFromServer(val)
{
     //Example: WebForm_DoCallback('__Page',val,DoSomethingCallback,"",DoSomethingCallbackErrorHandler,false);
     <%= AjaxJavaScript %>
}


</script>
</head>
<body>
    <form id="Form1" runat="server">
  <span id="MyMessage">Original Content Here</span>
  <input type="button" value="12345" onclick="GetMessageFromServer(12345)"/>
  <input type="button" value="111" onclick="GetMessageFromServer(111)"/>
    </form>
</body>
</html>


Thursday, August 21, 2008

Looping through an array of regular expressions in JavaScript

Regular Expressions in JavaScript are easy (assuming you know regular expressions ;). This is NOT a tutorial on regular expressions. I recommend the this site as an intro to Regular Expressions using JavaScript. What this entry does cover is how to create a regular expression in JavaScript. The recommended way is var regex = /int/ This will create a RegExp object that matches on the pattern int. This is not very interesting but it does illustrate that lack of double-quotes. This a very nice syntax that does NOT require the regular expression to be a literal string which in turn require the escaping of backslashes. Here is an example of a match on a digit var regex = /\d/ There are times like when getting input from the user or a database or something that you need to use the string syntax. In this case you do have to escape backslashes. This site explains this concept in more depth than I do here. var regex = new RegExp("\\d"); By default Regular Expressions in JavaScript match based on case and only match on the first occurrence. You can change that by using one of the following options. var regex = /int/gi or var regex = new RegExp("int", "gi"); Now that we know how to create a regex expression you may be starting to see how we can use them in arrays. Arrays in JavaScript can be done a few ways. var myArray = new Array(/int/gi, /\w/gi); or var myArray = new Array( new RegExp("int", "gi"), new RegExp("\\w", "gi" ); or var myArray = new Array(5); myArray[0] = new RegExp("int", "gi"); myArray[1] = /int/gi

Thursday, July 24, 2008

Adding Loading animation to all AJAX calls

So, you like AJAX, and your users like it except they can't tell when something is being done in AJAX. They complain that there is nothing that indicates to them that something is happening such as a query for a list, save, etc. One solution to this is to show an animated gif next to the element that caused the AJAX call in the first place. While the first line below and the BeginRequest() method are ASP.NET AJAX specific, the addLoadingAnimation() method is just JavaScript and can be used with Java, PHP, etc. The addLoadingAnimation() is IE and FireFox compatible on Windows, and has not been tested in any other browsers or platforms. The animation is removed in the endRequest event in ASP.NET when using an Update Panel. There is nothing needed because of how the Update Panel works. In other situations, the animation may need to be removed manually. Additional code would be needed, but should be able to be done in a similar way. The code assumes that the image for the text input will fit nicely inside the text input. A 16 x 16 image will probably work for text inputs of default height. The loading image for all other elements can be whatever size you like.
// Register our event listener that is called when an AJAX request is made.
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequest);

function BeginRequest(sender, args) {
var srcElement = args.get_postBackElement();
addLoadingAnimation(srcElement);
}

// Adds a loading animated gif to the right of
// the element that started an element
// (Except if the element is a text input,
//  then the image is inside the text input
//  as a background image)
function addLoadingAnimation(srcElement)
{

// if element is a textfield, then show the loading image in the textfield itself.
if (srcElement.tagName == "INPUT" && srcElement.type == "text")
{      
   srcElement.style.backgroundImage = 'url(images/loading.gif)';
   srcElement.style.backgroundRepeat = "no-repeat";
   srcElement.style.backgroundPosition = "right";
}

// else the element looks better with the loading image to the right of the element
else
{
   // only add the animation if it isn't there already
   // i.e. user click link twice in a row
   if (srcElement.nextSibling == null
       ||
       (
           !srcElement.nextSibling.innerHTML
          || (
             srcElement.nextSibling.innerHTML
             && srcElement.nextSibling.innerHTML.indexOf("otherLoadingImage") == -1)
             )
      )
   {
      var anim = document.createElement("span");
      anim.innerHTML = '<IMG ID="otherLoadingImage" BORDER="0" SRC="images/loading.gif">';
      srcElement.parentNode.insertBefore(anim, srcElement.nextSibling);
   }
}
}

Wednesday, July 23, 2008

insertAdjacentElement for FireFox

There is a JavaScript method called insertAdjacentElement() that only works in Internet Explorer (IE) and it is NOT supported in FireFox. There is an alternative however. Here is how you could use insertAdjacentElement() to insert a newly created element to the right of a specified element that already exists on the page: var newElem = document.createElement("span"); newElem.innerHTML = 'this is a new element here'; existingElement.insertAdjacentElement("AfterEnd", newElem); If you are want your code to work in IE and FireFox, try replacing the last line with: srcElement.parentNode.insertBefore(newElem, existingElement.nextSibling); If the browser you are using doesn't support that, you can try replacing the last line with the following (it is the same sometimes, but sometimes not) existingElement.parentNode.appendChild(newElem);