ajaxed - your classic asp libraryhttp://www.asp-ajaxed.org/asp-ajaxed.orgenasp ajaxed RSS Component v1.0Tue, 18 Jun 2013 22:55:09 UTCVersion 2.1.1 Releasedhttp://www.asp-ajaxed.org/article.asp?id=33asp-ajaxed.orghttp://www.asp-ajaxed.org/article.asp?id=33Tue, 18 Jun 2013 22:55:09 UTCVersion 2.1 releasedhttp://www.asp-ajaxed.org/article.asp?id=32asp-ajaxed.org
Today, we are announcing the minor release of ajaxed - version 2.1. It does not contain too many fancy features. I would say the most additions are syntactic sugar ;)

Note: Upgrading from 2.0.1 to 2.1 should not cause any problems and does not require any changes of your existing files. Just download the new version and override your existing ajaxed/ folder.

Let's quickly walk through the new additions:

PostgreSQL support added

Thanks to iurisilvio ajaxed supports now PostgreSQL as well. He contributed the necessary changes and found some issues within the database tests. These bugs were fixed and his changes were merged into ajaxed.

TextTemplate placeholders default values

Now it's possible to specify default values for placeholders within your templates. Let's have a look at a template which uses default values:


Dear <<< name | unknown >>>,
We are happy you joined our website.

Greetings,
<<< sender | Your website team >>>


The template contains two placeholders name and sender. Both have a default value which will be used if no value is available. Great! Thanks to iurisilvio as well.

Add recordset to a TextTemplateBlock

Assume we have a a template like that:


Dear <<< customerName >>>,

Your ordered articles:
<<< block articles >>>
   Name: <<< name >>>
   Number :<<< nr | unknown >>>
<<< endblock articles >>>

Thank you for purchasing with us.


So far you had to add each block item manually to the template. In most cases we would have the items in a recordset though. Now we are able to add a whole recordset to the template (recordset fieldnames are mapped to the placeholder names within the block):


<%
set t = new TextTemplate
t.filename = "template.txt"

'create a block and add the data from a recordset
set b = new TextTemplateBlock
b.addRS(db.getRS("SELECT * FROM ...", empty))

'add the block to the template
t.add "articles", b
%>


Cool, isn't it? Idea from iurisilvio too ;) Thumbs up! I personally think we could improve that even more in the future. I am thinking of e.g. simple conditions within templates, default values which are evaluated as script e.g. <<< date | now() >>>. Default values if there is no code block, etc... okay that's gonna come in 2.2.

Creating disconnected recordset

We all know that it's sometimes a pain when working with arrays and dictionaries. It can get very confusing cause they are very limited data structures. I personally prefer recordsets whereever possible. Mainly because I can use column names which makes my code more readable. For that reason I have added a shortcut function (lib.newRecordset()) which will create a new disconnected recordset. The alias is ["R"](). Let's see some examples:


<%
'creating new recordset
set rs = ["R"](array( _
  array("firstname", "lastname", "age"), _
  array("Lebron", "James", 20), _
  array("Britney", "Spears") _
))

'ready for looping...
while not rs.eof
  str.write(rs("firstname"))
  rs.movenext
wend

'we can even skip columns
'age is skipped for the first record .. cause we do not know the age ;)
set rs = ["R"](array( _
  array("firstname", "lastname", "age"), _
  array("Barack", "Obama"), _
  array("David", "Copperfield", 50) _
))
%>


That's really cool because it allows you to create powerful data structures easily without any confusing code. I recommend correct indenting though, otherwise it can get confusing with the brackets ;)

Tip: Not sure if you know the other useful aliased functions: [], ["D"]() and ["O"](). If not then check out the API ... They are useful!

DataContainer adding new items

The last addition is the adding facitlity for DataContainer. It allows us to add new items to a container instance easily. Here is a quick example:


<%
'dynamically expanding an array
set dc = (new DataContainer)(array())
'chained add
dc.add("one").add("two").add("three")

'dictionary data container
with (new DataContainer)(["D"](empty))
  .add(array(1, "BWM"))
  .add(array(2, "Ford"))
  'updating the item with key 1
  .add(array(1, "Toyota"))
end with

'recordsets
set dc = (new DataContainer)(["R"](array( _
  array("brand", "ps"), _
  array("BWM", 200)
)))
dc.add(array("Toyota", 100)).add(array("Mazda", 7))
%>


That was it! It is not that much but it's something useful and should help us with new ideas on how to improve ajaxed more and more. Hope you enjoy and I am looking forward to your feedback.

Michal.]]>
http://www.asp-ajaxed.org/article.asp?id=32Sun, 24 May 2009 22:21:00 UTC
Creating unit tests for your asp applicationhttp://www.asp-ajaxed.org/article.asp?id=31asp-ajaxed.org
  • Offers unit testing like you might know it from junit, nunit, etc. Including all the known assertions.
  • Test runner within the ajaxed console or directly executable
  • Additionally it offers functionality tests to check and ensure that all your pages are responding correctly.

Writing your first test

<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()

sub test_1()
    tf.assert 1 = 1, "1 is not equal 1"
end sub
%>
Run this code — test_first.asp


You should see a test report when running that file. It shows if anything is working smoothly or if there are any errors. The code above results in no failures. Check the following - which contains an obvious failure (1 is not equal to 2):

<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()

sub test_1()
    tf.assert 1 = 2, "1 is not equal 1"
end sub
%>
Run this code — test_fail.asp


Ok, now lets explain the code:) Normally every page in your app would contain an instance of AjaxedPage. The only exception is a test file because it contains an instance of TestFixture. For this reason the first line loads (includes) the TestFixture class and an instance named tf is created on the following line. Last but not least it is required to run the test with the run() method.

Note: By default all tests are only runnable on the DEV environment (security). To enable a test on all environments it's required to set the allEnvs to true. All examples here will contain that setting.

How to structure a test fixture?
The TestFixture class comes with a lot of assertion methods which are only executeable within a test method. So we need to create a test method first. A test method has the following signature:

Test methods: a sub prefixed with test_ and followed by a continous number (starting with 1)


<%
sub test_1()
  'your asserts
end sub

sub test_2()
  'other asserts
end sub

'IMPORTANT: This test won't be called cause
'the test_3 is missing. Tests are not in
'continous order
sub test_4()
end sub
%>


Setup method
Sometimes you want to perform certain tasks before running each of your test methods (e.g. opening a database connection, etc.). In that case we just add a setup() procedure into our test and it will be executed automatically before each test method.

Naming the test file
The naming of the test file is up to you. However, it is recommended to prefix it with test_. Prefixing allows you to identify your tests much quicker and additionally they will be available within your ajaxed console (under the "Test" tab).

Assertion examples


The following example outlines only the most important assertions you should know. In order to get an overview of all please refer to the latest API.

Most important to remember when working with assertions is that the last parameter always contains a message which will be displayed if the assertion fails.

<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()

sub test_1()
  with tf
    'simple
    .assert true, "Asserts truth"
    .assertEqual true, true, "Asserts equality"
    .assertNotEqual 1, 2, "Asserts not equality" 
    .assertInDelta 10, 15, 5, "Number with tolerance (delta)" 
    .assertInstanceOf "TestFixture", tf, "Asserts a type"
    .assertNothing nothing, "Asserts that an object is nothing"
    
    'advanced
    .assertMatch "\d+", "12312", "Asserts to a given regex pattern"
    .assertInFile "/ajaxed/ajaxed.asp", "include", "Asserts a string in a file"
    .assertEqual array("x", "y"), array("x", "y"), "Asserts equality also on arrays" 

    'contains assertions
    .assertHas array(1, 2, 3), 2, "Asserts containment"
    .assertHasNot array(1, 2, 3), 5, "Asserts non existence"
  end with
end sub
%>
Run this code — test_assertions.asp


Force failure


Sometimes you need to perform a customized test which requires more than a simple assertion. Still you want to place that test into your test fixture because this is where it should be. To perform such tests you will require to fail a test manually. Thats possible with the fail() method.

<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()

sub test_1()
    if lib.env = "LIVE" then
      tf.fail "i want the test to fail"
    end if
end sub
%>
Run this code — test_manually_fail.asp


Real world example


There are many different opinions out there on how many tests you should write. I am sure you have your own as well and that's why we kill that discussion here.
Nevertheless there is one thing which I strongly recommend when working with classic ASP: Testing the availability of each of your asp files. The test is setup quickly (does not hurt) and gives you an overview if all your pages are working properly (do not throw an unexpected error).

Our friend is the assertion assertResponse() which lets us check if a page contains a given response text (please refer to the API for a more detailed description). The following script tests some pages of the ajaxed.org page.

<!--#include virtual="/ajaxed/class_TestFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.allEnvs = true
tf.run()

'testing our most important pages
sub test_1()
  with tf
    .assertResponse "/api/", empty, "API", "API has a problem"
    .assertResponse "/", empty, "ajaxed", "Main page has a problem"
    .assertResponse "/about/", empty, "michal", "About page has a problem"
    .assertResponse "/download/", empty, "2.0", "Download page is not workin"
  end with
end sub
%>
Run this code — test_response.asp


Tip: We strongly recommend creating a test for your asp files. That saves you the time to browse each page manually.
]]>
http://www.asp-ajaxed.org/article.asp?id=31Mon, 23 Feb 2009 17:03:00 UTC
Browse API docs of previous versionshttp://www.asp-ajaxed.org/article.asp?id=30asp-ajaxed.orgcurrent API and you will notice those new links. Stay tuned!]]>http://www.asp-ajaxed.org/article.asp?id=30Fri, 13 Feb 2009 20:42:00 UTCNumbering Rows and making them clickablehttp://www.asp-ajaxed.org/article.asp?id=29asp-ajaxed.orga href tag. But what if you need something quick?

Here it comes. Check the recordLink property. It allows us to set a link which will be applied to each cell of each record. The best thing is that you can specify placeholders which will be replaced with a data value on each record iteration. In other words, you can create a record link with one line of code:

table.recordLink = "modfify.asp?id={id}"

Did you recognize the placeholder? {id} will be replaced with the value of the column named id on each record iteration. Lets have a look at a working example:

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  table.sql = "SELECT id, title, created_on FROM active_articles"
  table.recordlink = "javascript:alert('The id is: {id} and ive been created on {created_on}')"
end sub

sub callback(a)
  table.draw()
end sub

sub main()
  table.draw()
end sub
%>
Run this code — recordlink.asp


Ok nice. But what if we want to disable the link some specified columns? Sometimes you change the columns output on runtime and it would break your design if each column would be surrounded with an ahref tag. For that we can use the link property of DatatableColumn and set it to false:

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  table.sql = "SELECT * FROM active_articles"
  table.recordlink = "javascript:alert('The id is: {id} and ive been created on {created_on}')"
  'for that we need to add the columns manually
  table.newColumn("id", "ID").link = false
  table.newColumn "title", "Title"
  table.newColumn "created_on", "Created"
end sub

sub callback(a)
  table.draw()
end sub

sub main()
  table.draw()
end sub
%>
Run this code — recordlinkDisable.asp


If you feel funny (or maybe you really need it) you could create a record link like in the following example as well:

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  table.sql = "SELECT id, title, created_on FROM active_articles"
  table.onRowCreated = "onRow"
end sub

sub onRow(dt)
  'setting the link on the whole row.
  'it will be placed into the TR tag directly
  dt.row.attributes = "onclick=""alert('The id is: " & dt.data("id") & " and ive been created on " & dt.data("created_on") & "')"""
end sub

sub callback(a)
  table.draw()
end sub

sub main()
  table.draw()
end sub
%>
Run this code — recordLinkOnRow.asp


Thats just an example of what would be possible but its not recommended unless you really need it. We manipulated the attributes property of each row for that.

Numbering records

A last quick example demonstrates how to number your records. So the first gets always a 1, the second a 2, etc. Here we go:

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  with table
    .sql = "SELECT * FROM active_articles"
    'we use the ID column to place the number into it
    .newColumn("id", "").onCellCreated = "onID"
    .newColumn "title", "Title"
    .newColumn "created_on", "Created"
    'turn on paging so you see how it affects numbering
    .recsPerPage = 10
  end with
end sub

function onID(dt)
  'pass the row number and ignore the actual value (ID)
  onID = dt.row.number
end function

sub callback(a)
  table.draw()
end sub

sub main()
  table.draw()
end sub
%>
Run this code — numberedRecords.asp
]]>
http://www.asp-ajaxed.org/article.asp?id=29Tue, 30 Dec 2008 15:42:00 UTC
version 2.0.1 releasedhttp://www.asp-ajaxed.org/article.asp?id=28asp-ajaxed.orgDatatable class thanks to Danilo Peruš. He pointed out some bugs (which been fixed) and we implemented some of his great suggestions. See the changelog below.

Upgrading from v2.0 is no problem at all. Just overwrite the ajaxed core and you"ll be happy with its new improvements.

Please be so good and report any issues & feature requests at our
Google Project Issue list.


Changelog v 2.0.1

Datatable

BUGFIX: when paging was on, then one record to less has been displayed.

BUGFIX: DatatableRow.number was not set correctly on paging. It was rested back to 1 on each page. Instead should be numbered continously over all pages

BUGFIX: Fulltextsearch used to highlight Markup (if any) within cells if the cells value matched the query and the query was found within the markup as well
- this required a new property "valueF" of DatatableColumn which contains the formatted value.

- backwards compatibility: if a cell has a onCreated Event then no fullsearchtext highlighting is done as the original value is passed through. clients need to change it to use valueF


<%
'before:
function onCell(dt)
    onCell = dt.col.value
end function

'now (so fullsearch highlighting will work fine):
function onCell(dt)
    onCell = dt.col.valueF
end function
%>


- added css classes to the paging bar. Next page link, currentpage, etc. all have classes now. makes them available for skinning as well

- added an "all" option in the paging bar which allows to show all data.

- added "attributes" to the DatatableRow class. E.g. you can hook up an onclick attribute to make whole rows clickable

- added a "recordLink" property which allows a quick creation of link per record.
- Placeholds for data values can be used. That allows linking to specific records
- DatatableColumn.enableLink property can be used to disable the link on specific columns

Other

- added ajaxedPage.QSHas() and ajaxedPage.QST() as opposed to RFHas() and RFT(). They were missing so far.

Happy 2009!
Michal]]>
http://www.asp-ajaxed.org/article.asp?id=28Tue, 30 Dec 2008 14:57:00 UTC
Customizing and using your own loading indicatorhttp://www.asp-ajaxed.org/article.asp?id=27asp-ajaxed.orgdiv tag. Ajaxed automatically displays and hides it for you. That means no work for you ;)

Customizing default loading indicator

To change the loading text all you have to do is assign a new value to the AJAXED_LOADINGTEXT initialization variable. Normally you set this within your config.asp file:


<%
AJAXED_LOADINGTEXT = "Please be patient..."
%>


In order to change the appearance of the indicator you just need to provide some css styles for the css class called ajaxLoadingIndicator. Have a look at this example which overrides the indicator message and applies a fancy style.

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
AJAXED_LOADINGTEXT = "You have to wait. You know that ;)"
set page = new AjaxedPage
page.draw()

sub pagePart_do()
  lib.sleep(1)
  str.write("Done! Do it again?")
end sub

sub main() %>

  <style>
    .ajaxLoadingIndicator {
      font:16pt tahoma;
      color:#fff;
      background:#f00;
      padding:1em 2em;
      top:auto !important;
      bottom:0px;
    }
  </style>

  <button id="btn" type="button" onclick="ajaxed.callback('do', 'btn')">
    Do somehthing
  </button>

<% end sub %>
Run this code — loading.asp


The example did override the css class and the message directly within the page. Normally you would configure that globally but its good to know you can still override it.

Important: The default indicator is ALWAYS positioned at the right top corner of your page with a fixed position. That means it will always stay at the top even if the user will scroll on your page. ajaxed sets the following css attributes automatically:

top: 0px;
right: 0px;
position: fixed;

In case you want to position your default indicator somewhere else (means you want to change either the top, right or position attribute) you need to mark those attirbutes with !important so ajaxed cannot overwrite them:

right: 20px !important;

Creating your own loading indicator

The default indicator is nice but sometimes you would like to show a custom one somewhere else on the page. Lets think about a widget which gets reloaded. It would be nice to place an indicator directly inside it. Thats possible and not even really difficult.

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub callback(a)
  lib.sleep(2)
end sub

sub main() %>

  <script>
    function doSomething() {
      $('loading').show();
      ajaxed.callback('do', function(r) {
        alert('done!');
      }, null, function(t) {
        $('loading').hide();
      })
    }
  </script>

  <button type="button" onclick="doSomething()">
    Do somehthing
  </button>
  <div id="loading" style="display:none">
    <img src="http://www.tourmandu.com/img/uploading.gif" style="vertical-align:middle"/>
    Please be patient...
  </div>

<% end sub %>
Run this code — customLoading.asp


The only magic here is to create your own function which will call the ajaxed.callback. Before the actual call we show our custom indicator and hide it within the onComplete callback. Thats all. Styling is up to you.

In case you only want to use your own custom indicators you can disable the default indicator by setting its message to an empty string and/or setting its style to display:none
]]>
http://www.asp-ajaxed.org/article.asp?id=27Mon, 29 Dec 2008 13:50:00 UTC
Creating dependent dropdowns with Ajax flavourhttp://www.asp-ajaxed.org/article.asp?id=20asp-ajaxed.org
This article will explain you the best practice to achieve just that. As a goodie the solution will be fully ajaxed.

Problem summary

Lets assume we have a huge list of articles in our database and want to offer a convenient selection in one of our applications. Providing a simple dropdown which lists all articles would be too common as it would load all the thousands of articles in one long list. What we're going to do is create a second dropdown which allows us to select the articles category first and then load the articles according to its selection.

Solution

We will use the articles of ajaxed.org for this example ;) Here we have a view called active_articles which holds all the current articles and a category table which contains all our categories. Connecting them together is as easy as this..

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub drawCategoryDD()
  with new Dropdown
    .datasource = "SELECT * FROM category ORDER BY catname"
    'we use the category name as a value and as a caption
    .dataTextField = "catname"
    .dataValueField = "catname"
    .name = "cat"
    .commonFieldText = "-- category --"
    'the "no category" item should have a blank value
    'as by default its 0
    .commonFieldValue = ""
    .attributes = "onchange=""ajaxed.callback('articlesDD', 'articles')"""
    .draw()
  end with
end sub

sub pagePart_articlesDD()
  with new Dropdown
    set .datasource = db.getUnlockedRS( _
      "SELECT * FROM active_articles WHERE category = '{0}' ORDER BY title", _
      page.RF("cat") _
    )
    .dataTextField = "title"
    .dataValueField = "id"
    'we disable the articles if we have no selected category
    .disabled = page.RFT("cat") = ""
    .commonFieldText = "-- article --"
    .name = "article"
    .draw()
  end with
end sub

sub main() %>

  <form id="frm">
    <div><% drawCategoryDD() %></div>
    <div id="articles"><% pagePart_articlesDD() %></div>
  </form>

<% end sub %>
Run this code — dropdowns.asp


That was easy. Our code looks nice and everything is done on the server side. What we just did is we created two dropdowns using the Dropdown class and rendered them onto our page. The second dropdown (the one with the articles) has been put into a pagepart (pagePart_articlesDD) so it can easily be called using our ajaxed pagepart technique.

What happens within pagePart_articlesDD is the selection of all articles according to the selection of the category dropdown (see drawCategoryDD procedure). We grab the selected category from the post variables using page.RF("cat") and use it within our where clause. Last but not least we disable the articles dropdown if no category has been selected.

Note: Both dropdowns are using different datasources. The category uses a string and the artciles dropdown uses a recordset. Why? The reason for that is that we want to use a parametrized SQL-query for the articles as we want to prevent SQL-Injection. You could solve that using string concatenation but that would be vulnerable.

In a perfectly normalized database you would rather use an ID for the category and parse the form variable using page.RFP() into a number.

Oh, almost forget the change event. Have a look at the attributes property of our category dropdown. There you find a javascript call to ajaxed.callback() which reload our pagepart (Reminder: as we have a form with the id frm all form fields are passed as post variables to the callback automatically).

Pimp it! Lets give the dropdowns a bit of style

The problem has been solved but I would like to outline some features of the Dropdown class at this point. With only some modifications we are able to do some cool stuff. Lets do the following:
  • Allow a user friendly multiple selection of articles
  • Highlight the "News" category
  • Style it nicely so it looks like an article selection control

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub drawCategoryDD()
  with new Dropdown
    .datasource = "SELECT * FROM category ORDER BY catname"
    'we use the category name as a value and as a caption
    .dataTextField = "catname"
    .dataValueField = "catname"
    .name = "cat"
    .commonFieldText = "-- category --"
    'the "no category" item should have a blank value
    'as by default its 0
    .commonFieldValue = ""
    .attributes = "onchange=""ajaxed.callback('articlesDD', 'articles')"""
    'create an event handler to highlight the news item
    .onItemCreated = "onCategory"
    .draw()
  end with
end sub

sub onCategory(it)
  'if the items value is news we make it bold and red
  if it.value = "News" then it.style = "font-weight:bold;color:#f00"
end sub

sub pagePart_articlesDD()
  with new Dropdown
    set .datasource = db.getUnlockedRS( _
      "SELECT * FROM active_articles WHERE category = '{0}' ORDER BY title", _
      page.RF("cat") _
    )
    .dataTextField = "title"
    .dataValueField = "id"
    'we disable the articles if we have no selected category
    .disabled = page.RFT("cat") = ""
    .commonFieldText = "-- article --"
    .name = "article"
    .cssClass = "articles"
    'enable convenient multiple selection
    .multiple = true
    .multipleSelectionType = DD_SELECTIONTYPE_MULTIPLE
    'we need the height if multiple is turned on
    .size = 80
    .draw()
  end with
end sub

sub main() %>

  <style>
   #articleSelection {
     border:2px inset #ccc;
     background:#eee;
     padding:0.5em 1em;
     width:300px;
   }
   .articles {
     overflow:auto;
     white-space:nowrap;
     margin-top:1em;
   }
  </style>

  <form id="frm">
    <div id="articleSelection">
      <div><% drawCategoryDD() %></div>
      <div id="articles"><% pagePart_articlesDD() %></div>
    <div>
  </form>

<% end sub %>
Run this code — dropdown_pimped.asp


Looks not so bad but I am sure some designers could make it better.

Anyway, what we just did is hooked up an event handler for the categories dropdown. That was necessary to style a specific item (in our example the news item). Furthermore we made the articles dropdown appear as a multiple dropdown by turning on its multiple property. Just this change makes the dropdown render as a conventional multiple select tag, but we control the multiple selection with the multipleSelectionType property. This provides the ability to select multiple items using a checkbox and a single item using a radio button. Thats much more user friendly I guess. Play around with it - you will like it.

Last but not least we added some style section to get a appearance. Just have a look at the produced XHTML if you want to style your dropdown. You will get an idea of how to style it quickly.

Tip: You can even create dropdowns with a one-liner. Check the getNew() method for that. You could use it as follows:

Renders a month dropdown with one line:
(new Dropdown)(lib.range(1, 12, 1), "month", month(date())).toString()
]]>
http://www.asp-ajaxed.org/article.asp?id=20Mon, 29 Dec 2008 10:13:00 UTC
Requesting an URL via XMLHTTP made easyhttp://www.asp-ajaxed.org/article.asp?id=26asp-ajaxed.orglib.requestURL().

Requesting an URL is as easy as the following snippet:


<%
sub main()
  'request an URL and store its response
  r = lib.requestURL("GET", "http://www.ajaxed.org", empty, empty).responseText
end sub
%>


The first parameter defines the request method (post, get, put, ...), the second is the URL (which also can be a local URL when it starts with a slash) and the thirst parameter takes request parameters which are being sent with the request (e.g. using the query string if its a GET request). Last but not least the last parameter allows us to provide some options for the request (timeout, special request headers, etc.).

The method returns an object of type IServerXMLHTTPRequest. In case of any network or connection problems the return will be nothing. So a suggested real world implementation would look like the following...



<%
sub main()
  'request as a post with a parameter "date" set to current date
  'set the timeout to 5 seconds.
  set r = lib.requestURL( _
    "POST", "http://www.ajaxed.org", array("date", date()), _
    array("timeout", 5)
  )
  if r is nothing then
    lib.error("Could not request URL. It did not respond.")
  elseif r.status <> 200 then
    lib.error("Request successful but response not a success.")
  else
    'everthing fine .. do something with it
    str.write(r.responseText)
  end if 
end sub
%>


The preceeding example did check all the problems which can occur when requesting an URL. If you implement all those failure checks then you will be on the safe side.

Because the request is processed synchronously on server side its recommended to set a timeout and implement caching if possible. Page will load as long as the request lasts.

Ok, as the last example lets write a small page which allows us to enter an URL which is then requested parsed for all email addresses found within the page.

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()

'we are using ajaxed PageParts for that
sub pagePart_parse()
  url = page.RFT("url")
  if url = "" then lib.error("Please enter a valid URL.")
  set r = lib.requestURL("GET", url, empty, array("timeout", 5))
  if r is nothing then
    lib.error("Could not request URL (timeout, ..). Are you sure it exists?")
  elseif r.status <> 200 then
    lib.error("URL responded with an unsuccessful status: " & r.status)
  else
    with new Regexp
      'simple email pattern
      .pattern = "[a-z0-9_.]+@[a-z0-9_.]+"
      .global = true
      .ignoreCase = true
      set mails = .execute(r.responseText)
    end with
    if mails.count = 0 then str.write("No emails found at " & url)
    for each mail in mails
      str.write(mail & "<br/>")
    next
  end if
end sub

sub main() %>
  
  <form id="frm" onsubmit="ajaxed.callback('parse', 'result');return false;">
    <input type="text" name="url" value="http://www.">
    <input type="submit" value="get all emails">
    <div id="result"></div>
  </form>

<% end sub %>
Run this code — requestUrl.asp


Although its a simple example it covers everthing which you need when requesting URLs. It handles all the different failures that might occur and is still more readable than the conventional way when using XMLHTTP objects directly.

Debugging: If you have logging turned on within your setup then you will see a bunch of useful information which helps you debugging your requests. Just have a look at your log ;)

ajaxed makes heavy use of requestURL() internally as well. e.g. When requesting pages on unit tests, when localizing the users country, ...
]]>
http://www.asp-ajaxed.org/article.asp?id=26Thu, 25 Dec 2008 11:13:00 UTC
Using & Understanding Datatableshttp://www.asp-ajaxed.org/article.asp?id=22asp-ajaxed.orgDatatable class is the right one. It allows us to render data from the database in tabular manner and offers functions like sorting, paging, fulltext search, etc. All that with a simple SQL query. Thats the first and most important fact here to remember: The Datatable can only display data from a SQL query (SELECT * FROM ...).

Okay, this article will explain the most important things you should know when working with the Datatable. All the examples here will connect to a sample database whose connectionstring is stored in a constant called DATA_CONNSTRING. So you will see a connection call to a different database than the default on. It should not confuse you because in your environment you can use your default configured connection. Furthermore we will with only one table called person which contains the columns id, firstname and lastname.

First Simple Datatable


<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <% table.draw() %> 

<% end sub %>
Run this code — datatable.asp


Voila, we've got our first naked Datatable!

You can see that we've included our Datatable class and created an instance of it called table. The instance has been defined globally because its necessary to access it during different steps of our page lifecycle. All your datatable settings must be done within the init() and your table must be drawn within the main() (to actually render the HTML at a given position) and within the callback() procedure (to process all requests its callback requests).
Last but not least you see that we have set the sql property which is being used to populate the Datatable. Thats all the stuff you require to hook up your first one. They are a must!

Defining columns

You may have recognized that the columns in our first example have been detected automatically and rendered within your browser (column names are made human readable as well check str.humanize()). Thats cool, but not always what we want. We might want to change order, hide some columns, etc. For that reason its possible to add the columns manually and as long as there is at least one column added manually the automatic detection is deactivated. newColumn() method is our friend in that case. Check that:

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  with table
    .sql = "SELECT id, firstname, lastname, lastname + ', ' + firstname AS name FROM person"
    .sort = "lastname, firstname"
    .newColumn "name", "Name"
    .newColumn "lastname", "Last"
    .newColumn "firstname", "First"
  end with
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <% table.draw() %> 

<% end sub %>
Run this code — columns.asp


We've added 3 columns manually using newColumn() within our init() procedure. The first parameter is the name of the column as it appears in the SQL Query and the second parameter is caption which should be displayed as the column headline.

Pro TIP: The second parameter can also be an array which would result that the first field is the caption and the second a tooltip. E.g.
.newColumn "firstname", array("First", "Users Firstname")

Yeah, we also selected a column named name (which concatenates firstname and lastname) within the SQL query. Thus we were also able to add it as a column to the Datatable.

Primary key columns: The first column is always being considered as the primary key column (which uniquely identifies each record). If you are missing that then you get an exception which tells you to set pkColumn property manually to the column which contains the primary key.

By the way you can see that the sort property has been used to set the initial sorting of the data. You cannot use an ORDER BY clause within your SQL Query. Thats being added by the component itself. The sort property can contain any valid ORDER BY expressions though. Examples:


'valid sorting expressions
table.sort = "firstname DESC, lastname"
table.sort = "id, name, lastname"


Setting column properties:
If you check the API docs you will see that the newColumn methods returns an instance of a DatatableColumn. Thats great, because we can grab the column and customize it even more. Check the following example where we assign a css class to the id column to make it look gray in order to not attract too much attention. In addition we also styled the lastname column.

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  
  'add id column and set CSS class
  'quickes way (but only one property)
  table.newColumn("id", "ID").cssClass = "light"
  
  'add lastname column and set CSS class
  'another way of assigning properties
  with table.newColumn("lastname", "Name")
    .cssClass = "name"
    .help = "Some tooltip for the header"
  end with
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>
  
  <style>
    .light {
      color:#bbb;
    }
    .name {
      color:#f00;
      text-align:center;
      font-weight:bold;
    }
  </style>
  <% table.draw() %> 

<% end sub %>
Run this code — columnProperty.asp


The power is within the SQL


The power of our Datatable is "hidden" within the SQL query. As you may have already noted its only possible to use columns which are defined within the SQL query. That shouldn't be seen as a negative constraint at all. Its good news for us instead. SQL is really powerful and thats why you can solve most of the problems directly within the SQL query already. Remember the custom column creation? It could be solved without adding columns manully as well. Just use an SQL query like this to achieve a renaming of columns (this should be familiar to most):

 
table.sql = "SELECT id, firstname AS first, lastname AS last FROM person"

' would result in a datable with the following columns:
' id | first | last


You can even use SQL dialect for simple if conditions (CASE). Use your imagination and you will be able to solve every problem. E.g. you could even chuck HTML code into your SQL like (though I would recommend to avoid it as much as possible - just because it lacks readability):

 
table.sql = "SELECT id, '<strong>' + firstname + '</strong>' AS first FROM person"

When using HTML within your data be sure to set encodeHTML to false on the respective column. Otherwise all HTML will be safely encoded by default.

Runtime modifications


What if you need to change values during runtime according to its values or according to some business logic you have written? Thats not a big deal and can be solved using events. There are 2 main events. You can hook up either a whole row and modify the properties of a row (DatatableRow) during runtime or you can do the same for a column. First should be used when you want to apply customizations on a per record basis and the latter is perfect when you want to deal with a column across all records. Lets take a deeper look at both of them.

onRowCreated Event
The onRowCreated event is fired when the current row instance has been created but just before its been rendered. It has to be set on the datatable instance. At this stage you can modify any properties of the current row instance which is being held in the row property of your Datatable instance (check the API docs of DatatableRow for all its members). The following example will assign a css class called inactive to all persons whose ID is less than 10. Makes not much "real world" sense but should do it :)

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  table.onRowCreated = "onRowHandler"
end sub

sub onRowHandler(dt)
  if clng(dt.data("id")) < 10 then
    dt.row.cssClass = "inactive"
  end if 
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <style>
    .inactive td {
      color:#ccc;
    }
  </style>

  <% table.draw() %> 

<% end sub %>
Run this code — onRow.asp


Right, onRowCreated is nothing more as a normal property. Just that the string it holds is the name of the procedure which will be called everytime a row has been created. The procedure has to accept one argument which is the Datatable instance itself. That makes it convenient to access all datatable properties during runtime and makes it even possible to reuse event handling procedures across more datatables.

You see we use that argument (dt in that case) and its row property to get access to our current row instance. Also very interesting is the usage of the data property which always gives us access to underlying recordset. That allows us to read all the records data during runtime. Powerful!

onCellCreated Event
The other way to control execution is by using the onCellCreated event of a Datatable column. Its fired when a given cell of that column has been created but not rendered yet. Thus you can change its properties according to any conditions you like. Lets check the following example which writes "less 10" if the ID is less than 10 and "greater 10" if the ID is greater instead of the ID itself. Additionally it should highlight all persons whose lastname starts with the letter "A".

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  
  'quick way of attaching an event handling procedure
  table.newColumn("id", "ID").onCellCreated = "onID"
  
  'other way...
  set c = table.newColumn("lastname", "Last")
  c.onCellCreated = "onLastname"
end sub

'Important: its a function! onRowCreated was a sub!
function onID(dt)
  onID = "less 10"
  dt.col.cssClass = "inactive"
  if dt.col > 10 then
    onID = "greater 10"
    dt.col.cssClass = ""
  end if
end function

function onLastname(dt)
  'important to pass through the original value
  'as we dont change it
  onLastname = dt.col.value
  'note; we can use dt.col instead of dt.col.value
  'as its the default property
  if str.startsWith(dt.col, "A") then
    dt.col.cssClass = "ePerson"
  else
    'important to clear it as it would remember
    'it for the whole column
    dt.col.cssClass = ""
  end if
end function

sub callback(action)
  table.draw()
end sub

sub main() %>

  <style>
    .inactive {
      color:#ccc;
    }
    .ePerson {
      color:#00f;
    }
  </style>

  <% table.draw() %> 

<% end sub %>
Run this code — onCell.asp


The process is the same as with onRowCreated apart from the fact that this time we need to define a function instead of a sub. Its because our cell event handlers need to return the actual value which will be rendered. The simplest handler would be just passing through the value:


<%
'same as if you would not use it. just pass through
function onFirstname(dt)
  onFirstname = dt.col.value
end function
%>


Action columns
For sure you want to add a column one day which allows you to perform some action on a record or you want to open a form for the modification of a record. There are many ways to solve that but most of them require the onCellCreated event.

You could e.g create link for a column:


<%
function onLastname(dt)
  onLastname = "<a href=""person.asp?id=" & dt.data("id")  & """>" & dt.col & "</a>"
end function
%>


or you could also create an empty column, hook up a cell created event and add action links to it. This example shows one which redirects to a page and another one which would call a javascript function (you can even use ajaxed.callback) to perform an action:


<%
function onAction(dt)
  onAction = "<a href=""person.asp?id=" & dt.data("id")  & """>edit</a>" & _
    "<a href="javascript:void(0)" onclick=""doDelete(" & dt.data("id") & ")"">delete</a>"
end function
%>


Styling your Datatable. Lets Pimp!


The whole Datatable control can be styled using pure CSS. So that everthing you need to know ;) By default a standard ajaxed theme is used if you do not specify a stylesheet explicitly using the css property (which takes a virtual path to a stylehseet file). If you specify it then yours is loaded instead of the standard one. Another option would be to just add your own stylesheet to the page which will override the standard settings. Thats not recommended though, as the standard appearance might change over time.

So what you have to figure out then is what css class names are being used and define them within you stylesheet. To figure out that just create a new Datatable and have a look at the source code (firebug recommended!). The class names won't change as they are part of the style contract. So lets create our first theme and apply it a Datatable.

td, th {
  padding: 0.2em 1em;
  font: 10pt arial;
  width: 200px;
}

.axdDTControlsRow {
  background:#bbb;
}

th, th * {
  background:#000;
  color:#fff;
}

.axdDTRowOdd td {
  background:#eee;
}

.axdDTHighlight {
  font-weight:bold;
  color:#f00;
}

.sortedASC, .sortedDESC {
  font-weight:bold;
}

tfoot tr {
  background:#bbb;
  color:#fff;
}

tbody tr:hover td {
  background:#B6E2F3;
}

.pagingBar span {
  padding:0.1em 0.3em;
}

.pagingBar .pCurrent {
  font-weight:bold;
  background:#000;
  color:#fff;
}
Run this code — style.css


I am not a Designer so its just for demonstration purpose ;) Last but not least we need the Datatable which uses our just created theme. The new part here is the reference to the theme using the css property.

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  table.css = "style.css"
  table.recsPerPage = 10
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <% table.draw() %> 

<% end sub %>
Run this code — pimped.asp


Share your Theme: Please share your Datatable themes with others by posting to our Discussion Group. Its great to have some nice and good looking datatables around.

Now you should be able to create your data tables from the scratch and style them. You will see that after some days you get used to it and its great fun. Its a powerful component with great flexibility. More functions are coming...

Debugging: Check your log files if you want to debug the Datatable. You will find the whole generated SQL which is being sent to the database when populating the table.
]]>
http://www.asp-ajaxed.org/article.asp?id=22Wed, 24 Dec 2008 19:00:00 UTC
Version 2.0 downloaded more than 15 times / day!http://www.asp-ajaxed.org/article.asp?id=25asp-ajaxed.org
Thanks everybody for support and stay tuned.
Michal]]>
http://www.asp-ajaxed.org/article.asp?id=25Tue, 23 Dec 2008 19:40:00 UTC
Debugging ajaxhttp://www.asp-ajaxed.org/article.asp?id=24asp-ajaxed.org

JavaScript debugging

In most cases you are dealing with an AjaxedPage instance when working with AJAX. In that case you can set its debug property to true to get more information about the ajax life cycle. Thats especially useful when you experience problems during the ajaxed.callback call.

It will give you information about which action is being called (being sent to the server side callback()), what parameters are being sent to it, which URL is being requested and what the response looks like. All debug information will pop up in JavaScript alert boxes.

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
with
  .debug = true
  .draw()
end with

sub callback(a)
  if a = "do" then page.return "done"
end sub

sub main() %>
  
  <button type="button" onclick="ajaxed.callback('do', function(r) { alert(r)})">
    clik me for debug info
  </button>

<% end sub %>
Run this code — debug.asp


Debugging with Logger

Using our built in Logger helps you to debug every piece of your code. Hook up the Logger as describe in Debugging & Logging and log messages within you callback() routine.


<%
sub callback(a)
  lib.logger.debug "Action: " & a
  if a = "some" then
    lib.logger.debug "Went here"
  else
    lib.logger.error "Should not be here"
  end if
end sub
%>


TIP: If you take a look at your logfiles you will recognize detailed tracing information about all the ajax calls. We highly recommended to set up logging.

Basic Pop Up messages

Sometimes you just want to do a simple str.write() within the callback() but its not possible as its never written back directly into the browser.

In that case you can use the lib.error() or lib.throwError() method to alert some debug details. The first one stops the response, flushes it and write out the error message (useful for debugging or user friendly errors). The latter throws a real runtime error which can also be caught by error handlers.


<%
sub callback(a)
  if a = "some" then
    a = 1 + 2
    'can be useful for debugging
    lib.error("a = " & a)
  else
    lib.throwError("Invalid call.")
  end if
end sub
%>

The good thing about both methods is that they cause our ajaxed.callback to fail and thus the JavaScript calback function is not called. Thats good cause it might produce errors as the callback failed already.]]>
http://www.asp-ajaxed.org/article.asp?id=24Wed, 3 Dec 2008 14:20:00 UTC
Using request.form, request.querystring and response.writehttp://www.asp-ajaxed.org/article.asp?id=23asp-ajaxed.orgrequest.querystring (for GET parameters) and the request.form (for POST parameters) collection. Thats great but ajaxed takes it a step further.

As those parameter are so commonly used we thought of creating wrapper methods for them. Those methods should fulfill the following requirements:

  • Improve readability by shorter names (e.g. request.querystring is really really long and results in unreadable code)
  • Provide the ability to perform common tasks on a parameter in one go. E.g. Parse the parameter into a number and if parsing fails use a fallback (default) value.

The methods are already here :) You can find them in the AjaxedPage class. All methods starting with RF are referring to POST parameters (RF stands for request.form). On the other hand all methods starting with QS are referring to GET parameters (QS stands for querystring). From today on you should use them instead of the native ones ;) Just have a look at the length of the methodname. Its much shorter.

So whenever you create an instance of AjaxedPage you will have access to methods for POST and GET parameters.

Important: The same applies when working with ajax. You can access POST and GET parameters within the callback() using the same methods.

POST parameters

All methods of AjaxedPage starting with RF. They always contain at least one parameter which holds the name of the field (parameter) your are requesting. Examples:


<%
'we need a page instance in order to get
'access to the post parameters
set p = new AjaxedPage
p.draw()

sub main()
  '-- 1. common reading
  'old style
  someVal = request.form("paramName")
  'new style
  someVal = p.RF("paramName")
  
  '-- 2. parsing into a given datatype in one go
  'old style (fails if a parameter is not an int)
  someVal = cint(request.form("paramName")) 
  'new style
  someVal = p.RFP("paramName", 0)
  someBool = p.RFP("paramName", true)
  
  '-- 3. trimming
  'old style
  someVal = trim(request.form("paramName"))
  'new style
  someVal = p.RFT("paramName")
  
  '-- 4. getting an array (if more value have the same name)
  'old style 
  set val = request.form(name)
  someVal = array()
  redim preserve someVal(val.count - 1)
  for i = 0 to uBound(someVal)
    someVal(i) = val(i + 1)
  next
  RFA = arr
  
  'new style
  someVal = p.RFA("paramName")
  
  '-- 5. Checking existence
  'old style
  someVal = request.form("paramName") <> ""
  'new style
  someVal = p.RFHas("paramName")
  
end sub
%>


The above code sample should be mostly self explaining. Nevertheless I would like to stress to of the methods.

The first one is RFP which parses a posted value into a desired datatype. What does that actually mean to us? That means that it tries to parse the parameter into the datatype of your fallback variable. If it fails then the fallback variable is passed through. In that way you can prevent malicous input. You could e.g. use that value directly within a SQL query:


<%
'this is safe because the ID parameter will be
'parsed into an integer for sure
set RS = db.getRS("SELECT * FROM users WHERE id = " p.RFP("id", 0), empty)
%>


Note: When you reqiure a floating point number then be sure to pass 0.0 as the fallback value.

As RFP() uses str.parse internally, you should refer to its documentation if you require more details about the parsing behavoir.

The second "cool" method is RFHas(). It checks if a given parameter contains a non empty string. This is very useful when dealing with e.g. checkboxes. The following example ticks a checkbox when the page contains a posted value named foo:


<input type="checkbox" checked="<%= lib.iif(p.RFHas("foo"), "checked", "") %>"/>


TIP: Use the isPostback() method if you want to check if the page is a postback (has been requested using POST).

GET parameters

The same which applies to POST applies to GET parameters as well. Apart from the fact that all the methods are starting with QS.


<%
'we need a page instance in order to get
'access to the post parameters
set p = new AjaxedPage
p.draw()

sub main()
  '-- 1. common reading
  'old style
  someVal = request.querystring("paramName")
  'new style
  someVal = p.QS("paramName")
  
  '-- 2. parsing into a given datatype in one go
  'old style (fails if a parameter is not an int)
  someVal = cint(request.querystring("paramName")) 
  'new style
  someVal = p.QSP("paramName", 0)
  someBool = p.QSP("paramName", true)
  
  '-- 3. trimming
  'old style
  someVal = trim(request.querystring("paramName"))
  'new style
  someVal = p.QST("paramName")
    
end sub
%>


You may have recognized that some of the methods are missing although they exist for POST parameters. They will be implemented soon :) sorry, been forgotten.

However, you can easily parse querystring values into a desired datatype now and the "old fashioned" SQL injections (seen in many legacy ASP apps) should not be possible anymore. Lets assume we have a page called page.asp which can take a parameter speed:


<%
set p = new AjaxedPage
p.draw()

sub main()
  'tries to parse it into a floating point number
  'page can be called with ...
  ' page.asp?speed=2.2 => would pass 2.2
  ' page.asp?speed=xy => would pass 0.0
  ' page.asp?speed=1 => would pass 1.0
  ' page.asp => would pass 0.0
  speed = page.RFP("speed", 0.0)
end sub
%>


Writing to the response

In classic ASP applications we used to make heavy usage of response.write. When working with ajaxed we try to avoid its usage as we have several helper methods which makes life easier and are shorter to write:


<%
set p = new AjaxedPage
p.draw

sub main()

  'old style
  response.write("hello world")
  'new style
  str.write("hello world")
  'or
  page.write("hello world")  

  'old
  response.write("hello world" & vbnewline)
  'new
  str.writeln("hello world")
  'or
  page.writeln("hello world")

  'old
  response.end()
  'new
  str.end()

  'old
  response.write("some debug")
  response.end()
  'new
  str.writeend("some debug")

  'old
  name = "Jack"
  response.write("My name is " & name & ".")
  'new
  str.writef("My name is {0}.", name)
  'more parameters
  str.writef("My name is {0} and i am {1}.", array(name, 18))

  'old
  response.write(server.HTMLEncode("some <html>"))
  'new
  str.HTMLEncode("some <html>")
  'or quicker by using the default method
  str("some <html>")

end sub
%>


Do you see the difference? Its not huge but it makes sense as you write less and your code gets cleaner which makes it more readable. Just check the writef() method which allows you to format a string and write it our directly to the response. Thats a time saver :)

Please refer to the str.format() method if you need more details about the formatting of str.writef().

Important (preventing XSS attacks): Be sure to use str.HTMLEncode() whenever you display user input within your page. You can use the short version str() as well
]]>
http://www.asp-ajaxed.org/article.asp?id=23Wed, 3 Dec 2008 11:35:00 UTC
Finally! Version 2.0 is here. Overview about the Highlightshttp://www.asp-ajaxed.org/article.asp?id=19asp-ajaxed.org
So whats new? One of the most popular updates is the addition of so called page parts. They allow us to load XHTML fragments directly into our page using the existing and famous ajaxed.callback function.

The other important addition is a powerful debugging tool: A Logger! Version 2.0 contains a ready-to-use logger with ASCI colorization support. It allows you to debug your own code and provides you insight information about what the library does. Perfect for debugging our AJAX callbacks as well.

I am not sure if that is important to mention as some might say Email is too common. But! ajaxed provides you now an email wrapper. You don't care about email components anymore.

Beside all that additions there are new classes like: a Localization class for gathering information about the client, DataContainer which acts as an abstract container for all kinds of data structures, a Validator which helps you validating data, a StringBuilder which lets us work with huge amount of strings.

Last but not least there are two new amazing controls. One is a Dropdown - it allows you generating dropdowns within seconds. The other one is the giant within ajaxed:
  • A fully AJAX driven Datatable (also know as Datagrid).

And many, many more small updates & bug fixes. Please refer to the changes.txt for the details. Read some details about the highlights below.

Real world samples

First of all you should know that this website is totally developed with ajaxed. Why is that so important? Because the entire source code has been released for the public.
Just go, browse our code and see how we did it.

Okay, so here is the overview of the highlights...

Database

ajaxed has been fully tested with MySQL, MS Access, MS SQLServer and sqlite and Oracle. Take it easy if you want to use one of them.

Probably the biggest change to the database has been the addition of getRS() and getUnlockedRS() methods. Those replace the prior methods getRecordset() and getUnlockedRecordset(). Although all are still supported its recommended to use the new ones. They give you the ability of parametrized SQL queries. In other words - everthing is done to prevent SQL injection. The names are also shorter :) check the changes:


<%
'before
set RS = db.getRecordset("SELECT * FROM user WHERE id = " & str.sqlSafe(id))
'now
set RS = db.getRS("SELECT * FROM user WHERE id = {0}", id)
%>


When using update() or insert() all the text fields are trimmed to the maximum allowed length. This mean no errors anymore if the input is too long as allowed by the database field.

Added a insertOrUpdate() method which allows you to insert a record if it does not exist yet and update it if its already there. With update() you can also perform batch updates now.

Page parts

So far ajaxed.callback could only return different data types but it could not return whole page fragments. Lets say you wanted to implement a tab control where each tab loads different XHTML from your server. This is now possible with page parts. Just prefix a server side procedure with pagePart_ and use the name as the action parameter within ajaxed.callback:

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub main() %>

  <button type="button" onclick="ajaxed.callback('one', 'content')">content 1</button>
  <button type="button" onclick="ajaxed.callback('two', 'content')">content 2</button>

  <div id="content"></div>

<% end sub %>

<% sub pagepart_one() %>

  the first content

<% end sub %>

<% sub pagepart_two() %>

  some other content from server side

<% end sub %>
Run this code — pageparts.asp


When the second parameter is a string (like in the example) then its assumed to be an element which will be updated with the page parts content. You can still provide a JavaScript function. In case of a page part it will receive the HTML returned by the page part.

Logger

I have seen that debugging AJAX calls can be a bit hard. Thats why here comes the Logger into the game. You can use the logger to trace page requests, database access, etc. Last but not least you are able to add your own log messages.
Read this article to get more details about the logger.

Email

Do you know the pain when working with emails? All the different components and what happens if you switch the hosting provider? Then you might probably need to rewrite your email sending code. Not anymore! ajaxed offers you an powerful email wrapper class. It even comes with powerful features as: Piping all emails to one email, debugging emails, etc.
Read the detailed article about Emails within ajaxed.

Datatable

Our new giant within ajaxed is called Datatable. It lets you grab data from the database and display it in a tabular form. Including sorting, paging, searching, etc. All fully ajaxed! Perfect for backends. We love SQL and we use it. That's why the Datatable is focused on SQL queries as datasource.

There will be an own tutorials section on how to use the Datatable, as its a control with a lot of functionality. You can do incredible stuff with it. For the beginning I want to show you the first example using the popular Northwind database :)

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_datatable/datatable.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set table = new Datatable
set page = new AjaxedPage
page.draw()

sub init()
  db.open(DATA_CONNSTRING)
  table.sql = "SELECT * FROM person"
  table.recsPerPage = 10
end sub

sub callback(action)
  table.draw()
end sub

sub main() %>

  <% table.draw() %> 

<% end sub %>
Run this code — datatable.asp

Please refer to the API for more details and usage examples of the Datatable. This is the first version and it has some small bugs but the main features are working perfectly. E.g. Language support will be added soon. Also styles have to be provided by yourself yet as it still looks a bit naked. Go on, the CSS classes are already within the markup - just styling is missing.

DataContainer

Did you ever needed to page a recordset in your application? I bet you had :) Pagination, this is just one cool functiona of our new DataContainer. This class is loaded by default. Here is a short demonstration:

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub main()
  'create a new datacontainer for an array
  set data = (new DataContainer)(array("first", "second", "Second", "third"))

  'write some details about it
  str.writef "First Element: {0} <br/>", data.first
  str.writef "Last Element: {0} <br/>", data.last
  str.writef "Count: {0} items <br/>", data.count

  'get only unique elements
  for each el in data.unique(false)
    str.write(el & "<br/>")
  next
end sub
%>
Run this code — datacontainer.asp


Thats cool. An example of pagination will follow soon. Check the documentation if you need it now. DataContainer can be used with a Dictionary or Recordset as well. Even simple arrays are supported. You just don't care anymore whats the underlying data strucuture.

Validator

Do you use Models within your application? Probably not as it's not so common for classic ASP - but it does not matter. The Validator provides you the base for any kind of validation. Use it to validate your models or use it directly in your views.

Basically the Validator acts as a container for error messages (which are result of invalidity). You can ask it if its valid, add new messages to it, iterate through the errors, get an error summary or even return them directly within an AJAX call. Here is a quick example using AJAX:

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<!--#include virtual="/ajaxed/class_validator/validator.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub callback(a)
  if a = "save" then
    'create new validator
    set v = new Validator
    'if the name is empty add an error
    if page.RFT("name") = "" then v.add "name", "Name required"
    'if age is not a number or less than 1 then error
    if page.RFP("age", 0) < 1 then v.add "age", "Age invalid"
    'thanks to ajaxed we can return the whole
    'object. it implements the reflect() method
    page.return v
  end if
end sub

sub main() %>

  <script>
    function save() {
      ajaxed.callback('save', function(r) {
        //easy access to the validator properties.
        if (!r.valid) return $('errors').update(r.summary);
        alert('Successully saved. No errors :)');
      })
    }
  </script>

  <form id="frm" onsubmit="save();return false;">
    <ul id="errors"></ul>
    Your Name:
    <input type="text" name="name"/>
    <br/>
    Your Age:
    <input type="text" name="age"/>
    <br/>
    <input type="submit" value="save"/>
  </form>

<% end sub %>
Run this code — validator.asp


Localization

Modern web applications need to know everything about the client and localize it according to its information. We cannot know everything but we try hard :) The new Localization class offers information about the client and its settings. An instance is available by default through the local variable. Here is one example which locates you (it should show the country of your ISP):

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub main()
  'get the countrycode with timeout of 5 seconds
  countryCode = local.locateClient(5, empty)
  if isEmpty(countryCode) then str.writeEnd("Service unavailable")
  if countryCode = "XX" then str.writeEnd("Location unknown")
  str.writef "You are located in '{0}', arent you?", countryCode
end sub
%>
Run this code — local.asp

Really useful stuff if you are working with goecoding services such as google maps API. You can provide content localized to the visitor.

Dropdown

We believe that a dropdown is really important in web applications and that the implementation is usually are real mission. Thats why ajaxed introduces a fully flexible Dropdown class. It's optimized for performance and allows you to get rid of the spaghetti code involved with creating dropdowns. Check a simple example:

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub drawDD()
  with new Dropdown
    'you can also use SQL, Dictionary, ...
    .datasource = array("Jack Johnson", "John Meyer")
    .name = "myDD"
    .multiple = page.QS("t") <> ""
    if .multiple then .size = 2
    if page.QS("t") = "multipleS" then
      .multipleSelectionType = DD_SELECTIONTYPE_SINGLE
      .size = 50
    elseif page.QS("t") = "multipleM" then
      .multipleSelectionType = DD_SELECTIONTYPE_MULTIPLE
      .size = 50
    end if
   .draw()
  end with
end sub

sub main() %>

  <a href="?t=">classic</a>
  <br/>
  <a href="?t=multiple">multiple classic</a>
  <br/>
  <a href="?t=multipleS">multiple single selection</a>
  <br/>
  <a href="?t=multipleM">multiple multi selection</a>
  <br/>

  <% drawDD() %>

<% end sub %>
Run this code — dropdown.asp


StringBuilder

Ever needed to concatenate a large amount of strings? This can get slow really quickly. This is a common problem not only in classic ASP. For this reason I have implemented a StringBuilder class which takes advantage of the available .net String Builder (System.IO.StringWriter). Yes, we are gettnig help from .net. Thats great! So whenever you are working with a lot of data consider using the StringBuilder (e.g. Datatable makes heavy use of it already as it usually deals with large sets of data):

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub main()
  'create new instance
  set o = new StringBuilder
  for i = 0 to 10000
    'append some value
    o("Hello world")
  next
  'output the concatenated string
  str.write(o.toString())
end sub
%>
Run this code — stringbuilder.asp


Header &: Footer

The AjaxedPage provides you a property called headerFooter. This makes it possible to define a page header and footer explictly. Programatically. This could be a "master page" for all your page now:


<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
with new AjaxedPage
  .headerFooter = array("pageHeader", "pageFooter")
  .draw()
end with
%>

<% sub pageHeader() %>
  <html><body>
<% end sub %>

<% sub pageFooter() %>
  </html></body>
<% end sub %>


Test Fixtures

The coolest addition within our TestFixture is the ability to check the response of any page of your application. You can quickly write tests to check if all pages are working correctly (at least are accessible and do not throw an error). Write a test like this:


<!--#include virtual="/ajaxed/class_testFixture/testFixture.asp"-->
<%
set tf = new TestFixture
tf.run

sub test_1
  tf.assertResponse "/somePage.asp", empty, "<h3>.*?</h3>", "Page seem not to work"
end sub
%>


Write tests for every of your pages and find useful assertions for the response (using regular expression pattern). This helps you find problems faster in the future.

Regular Expressions

Small functions but massive power. Two new string functions join our compilation. Both deal with Regular expressions and make code so much more readable. rReplace() replaces all occurrences according to a regex pattern wheras matching() only checks if a string matches it. The idea for the latter comes from Ruby on Rails =~ operator ;)


<%
'checks if numbers exist in a given string
isNumber = str.matching("2", "^\d$", true)
'removes all numbers from a string
noNumbers = str.rReplace("abcd2efg4", "\d", "", true)

'Note: the last parameter has to do with
'case sensivity. check the API for it.
%>


New String & Request Utilities

There is not much to say here as code can say more than thousand word.
"Code is poetry" - wordpress


<%
'get values from forms auto-trimmed
name = page.RFT("name")

'get from querystring, form and parse immediately
'before
id = str.parse(page.RF("id"), 0)
id = str.parse(page.QS("id"), 0)
'now
id = page.RFP("id", 0)
id = page.QSP("id", 0)

'combine str.write and str.format
'before
str.write str.format("say {0}", "hello")
'now
str.writef "say {0}", "hello"

'str.format can be used without array too (if only one arg)
'before
str.format "say {0}", array(10)
'now
str.format "say {0}", 10

'StringOperations default function HTML encodes string
'dont forget to encode all your user input
'to prevent attacks such as XSS
str.write(str("<please encode me>"))
%>


Documentor

Lots of updates made to the documentor. To see its capabilities just check the recent API Docs. Remember: you can use the documentor also for your own classic ASP source code.

Last note

As you can see this update is really huge. All those functions are working in production systems already so you can easily upgrade to the new version. It will help you to be more productive in the future.

Upgrading from 1.0

Upgrading from version 1.0 should be no problem at all if you did not change any source files. Please read the changelog carefully if you have huge applications running with ajaxed and be sure to backup your running system.

Thanks to everyone ajaxed is getting nicer and getting more attention. Our goal is to make the tool for all the legacy ASP applications out there. Help us, provide your feedback.

Michal
]]>
http://www.asp-ajaxed.org/article.asp?id=19Tue, 25 Nov 2008 17:31:44 UTC
The power of ajaxed.callback. Get the insights!http://www.asp-ajaxed.org/article.asp?id=16asp-ajaxed.orgajaxed.callback, this JavaScript function is our god when it comes to AJAX usage. It's usage can be simple but also very complex. A detailed explanation of its parameters and the usage follows below.

If you are starting to use ajaxed, please refer to the beginners tutorials first. This article requires advanced ajaxed and JavaScript knowledge.

First of all you should know that ajaxed.callback has to be used if you want to use ajaxed Page Parts or want to call server side functions. It's the communication node between client side and server side.

Now let's take a look at the method signature:

ajaxed.callback(action, onReturn, [params, onCompleted, url])

  • action: The action which is called on server side. If there is a server side procedure named after the action e.g. pagepart_yourActionName then the procedure is being called directly. Otherwise callback is executed and the action is passed as the argument.
  • onReturn: Reference to a JavaScript function which will handle the returned values from server side. In case of a page part you can specify the ID of the target element.
  • params (optional): Parameters for the action in JSON notation. Those parameters are sent as POST parameters to the server. Thus they are accessible through the request.form collection on server side.
  • onCompleted (optional): Reference to a JavaScript function which will be executed when the action has completed (even if it failed).
  • url (optional): The url where the action is located. By default its the URL of the current page. This allows you to call actions which are located within other pages.

Okay, thats the theoretical part. Lets get some examples done. I want to demonstrate all the different usage scenarios. Read carefully as it can get a bit tricky sometimes.

Using the action

The action is being used to identify the callback on the server side. This allows you to add as many callback as you want. Basically every action is piped into our callback procedure unless it finds a procedure with the same name and prefixed with pagepart_. In this case it calls the procedure directly and returns its response back to the client


<%
sub callback(a)
  if a = "one" then
    'NEVER REACHED! because there is a
    'sub called pagePart_one
    page.return "foo"
  elseif a = "two" then
    'will be reached
    page.return "foo"
  end if
end sub

sub pagePart_one
  str.write("<strong>some html response</strong>")
end sub
%>
<script>
  ajaxed.callback("one", function(r) {
    //do something
  });
  ajaxed.callback("two", function(r) {
    //do something
  });
</script>


Using the onReturn

The function passed to the onReturn parameter will be invoked once the callback has been finished successfully. It's NEVER called if something fails as opposed to the onComplete function.

Be sure to provide one parameter for it. This parameter will hold the response back from the server. In case of a callback it contains all the variables sent with AjaxedPage.return() or AjaxedPage.returnValue(). In case of a page part call it contains a string which holds the XHTML produced by the page part.


<%
sub callback(a)
  if a = "actionOne" then
    page.return "wow!"
  end if
end sub

sub pagePart_partOne %>
  <strong>hi</strong>
<% end sub %>
<script>
  //you can create functions and reuse them
  function myHandler(r) {
    alert(r);
  }

  //will alert "<strong>hi</strong>"
  ajaxed.callback('partOne', myHandler);
  
  //will alert "wow!"
  ajaxed.callback('actionOne', myHandler);

  //you can write the function inline as well
  //will alert "wow!"
  ajaxed.callback('actionOne', function(r) {
    alert(r)
  });
</script>


And because we want to make life easier for developers you can also pass a string in case of a page part. Then the string is treated as the ID of the target element. This element will be updated with the page part response then.


<% sub pagePart_partOne %>
  <strong>hi</strong>
<% end sub %>

<script>
  ajaxed.callback('partOne', 'destination');

  //same, but not rapid :)
  ajaxed.callback('partOne', function(r) {
    $('destination').update(r)
  });
</script>

<div id="destination"></div>


Using parameters

The third parameter of ajaxed.callback can be used to pass parameters to our server side action. By default it checks if there is a form with an ID frm. If there is one it grabs all its form fields and passes those values as parameters to your action. If ther is no form called frm then no parameters are being sent unless you specify them manually. You can always provide the parameters by yourself regardless of an existing form or not. Those parameters must be provided as a Hash then (name value pairs in JSON notation). All parameters are available via the request.form collection on the server side afterwards.


<script>
  //#1
  //will automatically pass the param1 value
  //to the server side (because the page contains
  //a form with the ID 'frm'
  ajaxed.callback('check', function(r) {
    //do something
  });

  //#2
  //provide parameters manually
  ajaxed.callback('check', function(r) {
    //do something
  }, {someParam1: "foo", anotherParam: "foo"});

  //#3
  //take parameter from form and add an own (1st)
  ajaxed.callback('check', function(r) {
    //do something
  }, {pName: $F("pID"), anotherParam: "foo"});

  //#4
  //take parameter from form and add an own
  //automatically grab all fields from the form
  ajaxed.callback('check', function(r) {
    //do something
  }, $("frm").serialize(true).set(anotherParam, "foo"));
</script>

<form id="frm">
  <input type="text" id="pID" name="pName" value="foo"/>
</form>

<%
'our server side handler
sub callback(a)
  if a <> "check" then exit sub
  
  'will contain "foo" when calling with #1, #2, #4
  'will contain no value when caling with #3
  pName = page.RF("pname")

  'will contain value only when calling with #3
  someParam1 = page.RF("someParam1")

  'will always contain "foo" unless on #1 call
  anotherParam = page.RF("anotherParam")
end sub
%>


All this applies to page parts and callbacks.
Remember: The fields NAME is being used as the parameter name.
Not the fields ID (see in example #3)!

You can pass parameters the same way to a page part. You can access the parameter afterwards within the page part. Check the following example:

<!--#include virtual="/ajaxed/ajaxed.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub main() %>
  <div id="output"><% pagePart_one %></div>
  
  <form id="frm" onsubmit="ajaxed.callback('one', 'output'); return false">
    <input type="text" name="value">
    <input type="submit" value="send">
  </form>
<% end sub %>

<% sub pagePart_one %>

  You typed: <%= page.RF("value") %>

<% end sub %>
Run this code — pagepart_param.asp


Using onCompleted

The onCompleted parameter can hold a reference to a JavaScript function which is invoked after the callback has completed (its being passed the XMLHttpRequest object). Very useful to do clean up work. Check the following example. It demonstrated how to disable a button on callback and enable it back again when it finished.


<%
sub callback(a)
  if a = "myAction" then page.return "something"
end sub
%>

<script>
  function doFoo(sender) {
    //first we disable our element which invokes
    //the function. So he cannot call it again as
    //as its already running
    Field.disable(sender);
    ajaxed.callback('myAction', function(r) {
      //do something
    }, null, function(t) {
      //when the callback completes we enable it back
      Field.enable(sender)
    })
  }
</script>

<button type="button" onclick="doFoo()">Do</button>


You have to place the enable code into the onComplete as it would never enable the button when the action failed.

Using the url parameter

This parameter tells us where the server side code is located. By default its the current executing page and this is the most needed case. But sometimes (when it gets tough) you might want to call code within another page (try to avoid it as it reduces readability and maintainability!) or you want to expose a public interface for your application. Read on :)

Important: You need to specify the URL parameter when using ajaxed.callback on a default page of a folder. This is due to a bug within IIS. It does not return the default page when being called (with POST) without the filename.

E.g. Lets say /pages/default.asp is the default page of the /pages/ folder. In that case the ajaxed.callback wont work with only on /pages/
You need to specify default.asp as the url here. Don't forget the querystring if required.

Being able to specify the URL for the callback you could realize something like services for your application. Every application could expose a file with possible actions and other applications (clients) could call those function and get structured data back (even from outside through the internet). Check this example.

App 1 - file: service.asp

<%
sub callback(a)
  'gets the total sales for a given year
  if a = "getTotalSales" then
    'calculate the total sales somehow
    page.return getTotals(page.RFP("yr", 0))
  end if
end sub
%>


App 2 - can request the sales from App 1

<script>
  ajaxed.callback('getTotalSales', function(r) {
    $('sales').update(r);
  }, {yr: <%= year(now) %>>);
</script>

Sales this year:
<div id="sales"></div>


Do you see the benefit? You could write complete APIs for your applications. As these calls return JSON it would be possible for other systems (PHP, Java, Ruby, etc.) to consume those APIs. Use your imagination :)

Closing words

As you can see ajaxed.callback is really powerful and allows you to do all kind of tricky calls. To use all its power be sure to understand the basics first. After that you will see how easy it is to hook up AJAX functionality into your apps.

Have fun
Michal]]>
http://www.asp-ajaxed.org/article.asp?id=16Mon, 24 Nov 2008 13:55:00 UTC
Version 2.0 releasedhttp://www.asp-ajaxed.org/article.asp?id=17asp-ajaxed.orgversion 2.0. Usually some days after the release there will be a news which explains all the new features in more detail. Thanks everyone for participation.

Detailed article about version 2.0

Michal

Changelog ajaxed version 2.0

  • BUGFIX: ajaxed loading indicator stays fixed now even if there are scrollbars and you scroll.
  • BUGFIX: lib.range previously failed float tests are now running
  • BUGFIX: database tests used to run with the default DB if available.
  • BUGIFX: lib.throwError had a bug calling just QS but should call page.QS
  • BUGFIX: lib.detectComponent didnt work due to a wrong variable name
  • BUGFIX: str.format used to have a bug when arguments contained a placeholder string
  • added error handling in db.insert and db.update to get more information about an error if one happens
  • updated documentor:
    • support for lists added and code blocks () added, because docs looked a little messed up sometimes.
      elements supporing lists and code blocks include:
      @CDESCRIPTION, @SDESCRIPTION, @DESCRIPTION, @PARAM, @RETURN and public properties.
      A list item must be on one line, and start with "- "
    • Code examples are now better to read within the documentation
    • due to this change some docs have been updated for better readability
    • removed the class path in the documentation. it made no real sense to display this.
    • default methods has not been supported yet. are marked in "italic" now
    • BUGFIX: methods, properties, etc where case sensitive and it didnt recognize e.g. "Public Function" or "pUblic Property"
    • BUGFIX: byVal parameters have been not correctly detected.
    • removes square brackets on a methodname (reserved words)
    • parameter lists have a "," now between every parameter
    • use to emphasize code words (e.g. methodnames, etc.). EMPTY is emphasized automatically
    • @ALIAS keyword added for methods.
    • options are detected in the documentation
  • added db.getRS() and db.getUnlockedRS(). those should be used instead of db.getRecordset and db.getUnlockedRecordset
    because they can be used with parameters which will be sql-safe. e.g. db.getRS("SELECT * FROM user WHERE id = {0}", 1)
  • added some more error checks to the db class
  • added a Logger class which allows you to log all kind of things on 4 levels (debug, info, warn and error)
    • by default dev env logs till level debug (as a result all levels) and live env only till error level
    • logging comes with ASCII coloring which makes reading the logs easier
    • lib.logger holds a ready-to-use instance .. debug with lib.logger.debug("some message")
    • console has logging management
    • ajaxed logs some useful information by default as debug. e.g. all page requests and database accesses are logged. Nice thing: every class logs in its own color - to outline the different components
    • also warnings about e.g. obsolete methods are performed
    • supports also non ascii chars.
  • code-consoles in the console scroll from top to down now. this is more logic ;)
    • console has a favicon
  • updated str.isValidEmail() pattern to match all emails
  • TestFixture
    • assertInFile() and assertNotInFile added
    • assertResponse() added. with this its possible now to write tests which check if pages actually respond and work
    • asserts can be failed manually now by using fail() method
    • info() method added.
  • str.rReplace added => performs a quick regex replace on a given string
  • str.format is simpler to use when having one placeholder
  • instead of str.format("one {0}", array(1)) we can use str.format("one {0}", 1)
  • added a StringBuilder component which speeds up the common string concatenation.
    • uses either a .net stringbuilder or a custom VB StringBuilder (if installed)
    • is loaded by default and can be used e.g. ...
      set output = new StringBuilder
      output("test")
  • its possible to override config vars for each environment.
    • place a evnDEV() and/or envLIVE() sub into config.asp and override the wanted config vars
    • each var which should be overriden must be configured globally first otherwise it wont be recognized
  • database class
    • tested databases microsoft access, sqlite and ms sql server
    • dbType added which tells us which database we are using
    • trims all text field columns on insert() and update() to the length which is maximum allowed for the column => no error happens anymore because a field is too long
    • added insertOrUpdate()
    • update() is also doing batch updating now and return the number of updated records
  • added a lib.require method which allows us to do a check if a given class has been included. if not a friendly error is shown
  • friendly error raised when ajaxed.callback is used but no callback() is defined on server side
  • ajaxed.callback supports page-parts now:
    it possible get page content back on callback and update a container with it.
    example: lets say we have a DIV container with ID 'c' and want to load some html from the server into it
    this is also possible now with ajaxed.callback:
    e.g. ajaxed.callback('partname', 'c') will load a sub called "pagePart_partname()" and put its content
    into the element with the ID 'c'. if no sub found with that name then it acts like a normal callback.
  • added a Dropdown control.
    • uses stringbuilder. can output directly or to a string
    • different multiple types (checkboxes, radiobuttons)
    • different datasources
  • str.HTMLEncode() is now a default function so its quicker to use in your views. use it to prevent XSS attacks!
  • str.humanize() added. it makes "computer" strings more "human" readable
  • Validator class added. It helps validating business objects!
  • AjaxedPage.RFA() method added. It gets arrays from the request.form collection
  • logger deactivated by default
  • JSON:
    • BUGFIX: if a dictionary contains no items then "null" is returned. used to return nothing (this is wrong according to JSONs RFC).
    • BUGFIX: supports all unicode chars now (using ascw() function instead of asc()). Chinese, etc. works fine now!!
    • recordsetPaging property added
    • updated documentation
    • toJSON() has been defined as default method. thus generation is possible with (new JSON)("some value")
    • standalone test(s) added
    • supports request object now (includes IStringList and IRequestDictionary support). exposes session, form, querystring, etc.
  • DataContainer class added
    • provides an abstract utility to manipulate data. supports recordset, array and dictionary
    • lib.contains() is obsolete as it has been moved to DataContainer
    • paginate() can be used to paginate data
  • Localization class introduced
    • available with 'local' instance
    • str.parse() parses float number with the comma according to the local settings
  • added str.writef() function which is a combination of write() and format()
  • AjaxedPage.headerFooter property introduced to allow adding custom headers.
  • added lib.requestURL() to easily request urls with IServerXMLHTTPRequest
  • added AjaxedPage.QFP() and AjaxedPage.RFP() methods to get values and parse them in on go.
  • added lib.arrayize() which ensures a value to be an array.
    • alias: []
  • introduced optionsHash which can be seen as an own ajaxed datatype.
    • can be created using lib.options or ["O"] function
  • lib.newDict() got an alias ["D"] => create dictionaries only with: set d = ["D"](empty)
  • ]]>
    http://www.asp-ajaxed.org/article.asp?id=17Sun, 23 Nov 2008 18:51:00 UTC
    Sending Emailshttp://www.asp-ajaxed.org/article.asp?id=14asp-ajaxed.org
    Forget the problem of choosing the right email component (installing & configuring it). ajaxed does that for you. It checks which components are installed on the server and uses the "best" one. Please refer to the API to see which components are currently supported. As this page been written Jmail, ASPEmail and CDOSYS was supported. Great, so lets send out our first email:

    
    <!--#include virtual="/ajaxed/ajaxed.asp"-->
    <!--#include virtual="/ajaxed/class_email/email.asp"-->
    <%
    set page = new AjaxedPage
    page.draw()
    
    sub main()
      with new Email
        .addRecipient "to", "some@email.com", "Some recipient"
        .sendersName = "ajaxed.org"
        .sendersEmail = "ajaxed@yourserver.com"
        .mailserver = "smtp.yourserver.com"
        .subject = "Hello world!"
        .body = "ajaxed says hello"
        .send()
      end with
    end sub
    %>
    


    This snippet quite explains itself. We load the Email class with the include statement first. After that we are able to use the class. The property names and methods say the rest. But thats a bit lot of code if we want to send emails rapidly within our app. For that reason we can override all the common settings of Email in our config.asp (afterwards they are used for every new email instance):

    
    <%
    AJAXED_EMAIL_SENDER = "ajaxed@yourserver.com"
    AJAXED_EMAIL_SENDER_NAME = "ajaxed.org"
    AJAXED_MAILSERVER = "smtp.yourserver.com"
    AJAXED_EMAIL_DISPATCH = false
    %>
    

    Clear? The only one which might be unclear is AJAXED_EMAIL_DISPATCH. This setting prevents the email from actually being dispatched. The whole process of email sending is simulated but in the end the email is not sent. Thats useful during the development process. The emails seem to be sent, but acutally they are not :) You can see them in your log though. Anyway, now we can send an email with only that code:

    
    <!--#include virtual="/ajaxed/ajaxed.asp"-->
    <!--#include virtual="/ajaxed/class_email/email.asp"-->
    <%
    set page = new AjaxedPage
    page.draw()
    
    sub main()
      with new Email
        .addRecipient "to", "some@email.com", "Some recipient"
        .subject = "Hello world!"
        .body = "ajaxed says hello"
        if not .send() then str.write("Could not send: " & .errorMsg)
      end with
    end sub
    %>
    

    The send() method returns true if mailing was successful. Thus in this case we demonstrated even some error handling ;)

    Recommended Settings during development

    When developing a web application we recommend you to make use of one of our email DEV features (don't forget to turn them off when going live):
    • Disabling actual email dispatching by setting AJAXED_EMAIL_DISPATCH to false
    • Pipe all emails to one email address by setting the AJAXED_EMAIL_ALLTO config variable. This allows you to receive all emails sent within your system.
    • Turn on the logger and follow your log file (check /ajaxedLogs/ folder) to see details about the sending process.
    ]]>
    http://www.asp-ajaxed.org/article.asp?id=14Sat, 22 Nov 2008 22:24:00 UTC
    Debugging and Logginghttp://www.asp-ajaxed.org/article.asp?id=13asp-ajaxed.org
    Do you remember the good old times? Where we used to do the following...

    
    <%
    response.write("something")
    response.end
    %>
    

    :) Those times are over thanks to ajaxed.

    Logger

    ajaxed ships with a powerful logger which supports ASCI colorization and logging on different levels (like e.g. only errors, only debugging messages, etc.). The logger is disabled by default. So first you need to enable it within your config.asp. Set the AJAXED_LOGLEVEL to 1 to log all kinds of messages.
    Important: The logger logs by default into the /ajaxedLogs/ directory. This folder requires write permission for the IUSR.
    Now browse any of your application pages. After that check the /ajaxedLogs/dev.log file (in case of live environment the file is named live.log). It should contain log messages about your previous page requests. It logs details about page requests, sent form parameters, querystring details, ajax callback, used SQL queries, user information, etc. This information is brought by the library itself, but how to log messages by yourself?

    
    <!--#include virtual="/ajaxed/ajaxed.asp"-->
    <%
    set page = new AjaxedPage
    page.draw()
    
    sub main()
      lib.logger.info "page called"
      'we write the querystring into the logger as a debug message
      lib.logger.debug page.QS(empty)
    end sub
    %>
    

    As you can see the library offers you a ready-to-use Logger instance lib.logger. It can be used everywhere within your code. If the logger is disabled you don't need to remove those calls. The logger just does not log the messages ;) If you call this page you should see those messages in your logfile now.

    Ok, you don't like the appereance of the logger file? We feel with you. It contains some special character and is not really readable. Thats because it support ASCI colorization. Now you have two choices: The first is to disable colorization by setting AJAXED_LOG_COLORIZE to false or (the recommended choice) you download (win users) a shell which supports colorization. We recommend Cygwin.

    After you have downloaded cygwin you can hook up the tail command (with follow option) on your log file:

    tail -f dev.log

    If you've come so far you should see a log which looks similar to the following:

    Here you see nicely whats going on, don't you? We strongly recommend you to setup colorized logging and following it using the tail -f command. This allows you to see changes of the log immeditely. You productivity will rise dramatically!

    Console

    For sure you already played a bit with your console. The console is also a good source for debugging information. You get some general information about your app (e.g. DB connection, installed components, etc.) and - which is the most important - it comes with a test runner for your unit tests. Writing unit tests helps you maintaining your code later.

    str.writeend()

    If you feel that you still need the classic way then you can use str.writeend to write out something to the response. The response stops afterwards. However, we recommend using lib.logger.debug instead. Especially if you work with AJAX callback the writeend approach won't work.]]>
    http://www.asp-ajaxed.org/article.asp?id=13Sat, 22 Nov 2008 22:22:00 UTC
    Understanding environmentshttp://www.asp-ajaxed.org/article.asp?id=12asp-ajaxed.org
    ajaxed currently supports two environments: DEV (default) and LIVE. You can set the environment in your config.asp using the AJAXED_ENVIRONMENT variable:

    
    'live environment
    AJAXED_ENVIRONMENT = "LIVE"
    

    The Library class provides some properties for the environment setting:

    
    <%
    'gets the current environment
    currentEnvironment = lib.env
    'checks if it is development
    isDevelopment = lib.dev
    'checks if it is live (production)
    isLive = lib.live
    %>
    

    With those properties you can easily control your code dependent to the configured environment. E.g. You could disable google analytics during the development process. Just chuck this snuippet into your footer.asp.

    
    <% if lib.live then %>
      <script>
        //google analytics javascript
      </script>
    <% end if %>
    


    Some interesting dependencies

    • By defautl Emails are not dispatched on DEV
    • The ajaxed console is blocked on LIVE
    • Tests only run on DEV by default
    • and more
    ]]>
    http://www.asp-ajaxed.org/article.asp?id=12Sat, 22 Nov 2008 22:21:00 UTC
    Bringing AJAX into the gamehttp://www.asp-ajaxed.org/article.asp?id=11asp-ajaxed.org
    First of all you should know that ajaxed is using prototypejs as its internal JavaScript library. It performs all the XHR calls and offers a lot of convenient functions for our JavaScript work. It is strongly recommended to know its basics when working with ajaxed. As prototype is already very popular we guess you know the basics already ;) Then kick it off.

    If you haven't seen a demonstration of ajaxed capabilities yet then click here.

    ajaxed allows you to call server side code directly from client side. There a two ways:
    • Page parts: calling a server side procedure and updating an element with its content. Scenario: You have several tabs on a page and want to load different contents into your page when the user clicks each of the tab
    • Callback: calling a server side function and returning its values directly to the client side. Scenario: You want to call your business logic to calculate some values and display its result on the page.

    Page parts

    Lets assume we have two buttons and every button should show different content when clicking it. The content should be loaded from server side and displayed without refreshing the whole page (thats AJAX!):

    <!--#include virtual="/ajaxed/ajaxed.asp"-->
    <%
    set page = new AjaxedPage
    page.draw()
    
    sub main() %>
    
      <button type="button" onclick="ajaxed.callback('one', 'content')">content 1</button>
      <button type="button" onclick="ajaxed.callback('two', 'content')">content 2</button>
    
      <div id="content"></div>
    
    <% end sub %>
    
    <% sub pagepart_one() %>
    
      the first content
    
    <% end sub %>
    
    <% sub pagepart_two() %>
    
      some other content from server side
    
    <% end sub %>
    Run this code — pageparts.asp

    Works like a charm. That was easy, wasn't it? Do you see the loading indicator in the top right of the screen? ajaxed takes care of everything.

    The thing you need to learn here is the ajaxed.callback() JavaScript function. This is the main (and currently only one) AJAX function within ajaxed. Please remember it, its the most important function for the use of AJAX.
    What we did? We created a simple page and hooked up the buttons click event with the ajaxed.callback() function. The first argument holds the name of the page part (one and two) and the second refers to the target element (which will receive the content). On the server side we created two procedures. One for each page part: pagepart_one and pagepart_two. Thats all! As you see the only thing you need to do on server side is to prefix your page part procedure with pagepart_.

    Callback

    The other way is to call server side functions directly and return its values to the client side. Just think of the following: Grabbing whole recordsets from the database and using them within our JavaScript. That would be great! No Problem at all. But for the beginning we have to start with something simple. Lets return some primitive stuff from server side and display it to the user:

    <!--#include virtual="/ajaxed/ajaxed.asp"-->
    <%
    set page = new AjaxedPage
    page.draw()
    
    sub callback(action)
      if action = "returnChar" then
        page.return "A"
      elseif action = "returnFloat" then
        page.return 2.5
      end if
    end sub
    
    sub main() %>
    
      <script>
        function returned(result) {
          $('result').update(result);
        }
      </script>
    
      <button type="button"
        onclick="ajaxed.callback('returnChar', returned)">
        return A
      </button>
      <button type="button"
        onclick="ajaxed.callback('returnFloat', returned)">
        return 2.5
      </button>
    
      <div id="result"></div>
    
    <% end sub %>
    Run this code — callback.asp

    Do you like it, do you see the benefit already? We click a button and the server side responds to the click without a refreshing the whole page.

    How does it work? As already mentioned it is important to know that the ajaxed.callback() function is our go-to-guy. We used it again :) This time the first parameter tells the server which function (also called action) needs to be executed. The second parameter holds a JavaScript callback function which receives the return values from the server side (in this case we named it returned). Whatever you return on server side this function will get it.

    The last important fact here is that we need a callback() procedure on server side to handle our client requests. This procedure requires one parameter which holds the actual action. You can see it in the example: sub callback(action). Within the callback procedure we use page.return() to return the value to the client side. This method can return (almost) everything which is available within VBScript. E.g. You can even return an array: page.return array(1, 2, 3) and it can be used as an array within your JavaScript. For more details about the different datatypes please read the documentation of AjaxedPage.return() method or a more technically detailed version at Generate JSON from asp datatypes.

    As this page is just for "getting started" you need to browse our page for other articles to get more examples of AJAX usage. Check the AJAX section.

    When to use what?

    Simple rule: Whenever you need to return whole XHTML fragments use page parts. In all other cases use callbacks.

    Read this article if you need another beginners tutorial]]>
    http://www.asp-ajaxed.org/article.asp?id=11Sat, 22 Nov 2008 20:48:00 UTC
    Creating your first page "Hello World!"http://www.asp-ajaxed.org/article.asp?id=10asp-ajaxed.orgAjaxedPage class. Others would say it's our page controller. Call it whatever you want :) The thing to remember here is that each of your pages will contain an instance of AjaxedPage. With that knowledge we can code our first page:

    <!--#include virtual="/ajaxed/ajaxed.asp"-->
    <%
    set page = new AjaxedPage
    page.draw()
    
    sub main()
      str.write("Hello world!")
    end sub
    %>
    Run this code — hello-world.asp

    Ok, lets have a look what happend here. First we included the ajaxed library (include statement) and created an instance of AjaxedPage which is drawn afterwards - draw() method.

    In order to make the draw work we need an entry point for our page. This entry point is a procedure called main. Our entry point contains only one line which prints out Hello world!. str.write() is the ajaxed version of response.write. Get used to it :) The thing to remember here is that the output of your main procedure will be surrounded with the page header and page footer.

    Oh, you wanna see more. Here comes a more sophisticated example for everyone who wants to see more at this point:

    <!--#include virtual="/ajaxed/ajaxed.asp"-->
    <%
    set page = new AjaxedPage
    page.title = "A page title"
    page.draw()
    
    sub main()
      content()
    end sub
    
    sub content %>
    
      The page is located here:<br/>
      <%= page.getLocation("virtual", true) %>
      <br/><br/>
    
      The environment is: <%= lib.env %>
      <br/><br/>
    
      ajaxed version: <%= lib.version %>
    
    <% end sub %>
    Run this code — page.asp

    Hmm, thats not really sophisticated I guess. However, I think you can figure it out by yourself what that code does. One thing to note here is the seperation of code and content. As you can see the example calls a content procedure within the main. The content is our view and the rest (main and the other procedures) is the page controller. The view consists mostly of markup thats why it is not surrounded with propellers whereas the rest of our code has propellers. Brings a bit of MVC feeling :)]]>
    http://www.asp-ajaxed.org/article.asp?id=10Sat, 22 Nov 2008 20:48:00 UTC
    Database setuphttp://www.asp-ajaxed.org/article.asp?id=9asp-ajaxed.org
    • Microsoft Access
    • Microsoft SQL Server
    • MySQL
    • Oracle
    • sqlite

    First of all open the config.asp and configure your connection string. The constant called AJAXED_CONNSTRING must be set to a valid connection string. Some are already suggested and can be used directly - just uncomment.

    Don't forget to change the password, user and database within the predefined connection strings (for more connection strings check http://www.connectionstrings.com/). If your database is running on a different machine than the webserver then you need to change the server adress as well.

    Accessing the database

    Now you are able to access the database. Access is always performed through the Database class. ajaxed offers you an ready-to-use instance of that class called db. Before using it you need to either open the connection manually using the db.openDefault() method or let it do automatically by your pages. You can set the DBConnection property of your page to true. This opens and closes the database connection automatically.

    
    <!--#include virtual="/ajaxed/ajaxed.asp"-->
    <%
    set page = new AjaxedPage
    page.DBConnection = true
    page.draw()
    
    sub main()
      'getting a read-only recordset
      set RS = db.getRS("SELECT * FROM table", empty)
      'getting an updateable recordset
      set RS = db.getUnlockedRS("SELECT * FROM table", empty)
      'using parameters
      set RS = db.getRS("SELECT * FROM table WHERE id = {0}", 10)
      'getting a single value
      age = db.getScalar("SELECT MAX(age) FROM users", 0)
    end sub
    %>
    


    Connecting to more databases

    In order to switch to another database you need just to call the db.open() method with a connection string of your desired database. After that, all methods of the Database class are executed against the "new" connected database. The database connection is closed automatically at the end of page processing.

    Alternatively you could also have created a new instance of Database and have both Databases opened in seperate variables.

    More information

    For more details about the different access methods refer to the API.
    Another tutorial is available at here.]]>
    http://www.asp-ajaxed.org/article.asp?id=9Sat, 22 Nov 2008 20:47:00 UTC
    Configuration & Customizationhttp://www.asp-ajaxed.org/article.asp?id=8asp-ajaxed.org/ajaxedConfig/. After the installation you will find 3 files there:
    • config.asp:
      contains all settings for the ajaxed library and can be used for your own settings as well.
    • header.asp:
      contains the XHTML fragment which will be used for every page as the header.
    • footer.asp:
      contains the XHTML fragment which is used as the footer section of your pages.

    Config.asp

    Open that file and you will recognize some commented out configuration variables. These are the most common configurations. Uncomment those you want to use. This line changes the loading message of all ajax requests to "Hold on. I am working..."

    
    AJAXED_LOADINGTEXT = "Hold on. I am working..."
    

    List of configurations: Have a look into the ajaxed API to see what values can be configured. Those values can be found in every class definition in the "Initializations" section.

    Header.asp

    This file contains the XHTML which will be placed first in every page you create. It should contain all the general markup like e.g. html, body and it may contain custom markup which is needed by your application.

    Lets have a look at the default header file:

    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        <head>
            <meta http-equiv="content-type" content="text/html; charset=utf-8" />
            <title><%= title %></title>
            <% ajaxedHeader(array()) %>
        </head>
        <body>
    


    You see there is just the basic stuff. A doctype-, html- and body-tag. Chuck everything inside which you require for your app. One point to note is that the file is already prepared for UTF-8 ;)

    Two things might be new for you. The first is the title variable and the second is the ajaxedHeader(array()) call. The first one is easy :) It holds the page title. The second is a request to load the necessary libraries for ajaxed to work (javascript libraries). If you need to know the details please refer to the API. For now remember: This call is a MUST!

    Footer.asp

    Not much to say to the footer file. It contains all the markup which is placed last on the page. Just a quick hint: You could show the environment setting in the footer of your pages:

    
      <div><%= lib.env %></div>
    </body>
    </html>
    

    ajaxed is fully flexible on structuring your pages. You can create different headers for different pages. You can even move the location of those files. Refer to the API for more details.
    ]]>
    http://www.asp-ajaxed.org/article.asp?id=8Sat, 22 Nov 2008 20:46:00 UTC
    Installation and Requirements evaluationhttp://www.asp-ajaxed.org/article.asp?id=7asp-ajaxed.org
    • IIS 6 or IIS 7
    First you need to download the latest ajaxed release. Done? Unzip the archive directly into your webroot. It now should contain the following folders:

    /ajaxed/
    /ajaxedConfig/

    Finished! To check if everything works fine browse to http://yourhost.com/ajaxed/demo/ and try the demo. If it works you are ready for development.

    Oh, and you can also browse to http://yourhost.com/ajaxed/console/ which will show you an own ajaxed console. Just in case you need something to play with :)

    In case of upgrading to a newer version you should only override the ajaxed folder. Otherwise your config gets lost.
    ]]>
    http://www.asp-ajaxed.org/article.asp?id=7Sat, 22 Nov 2008 20:39:00 UTC
    ajaxed 1.0 releasedhttp://www.asp-ajaxed.org/article.asp?id=2asp-ajaxed.org
    ajaxed1.gifajaxed2.gif
    For all who cannot wait go directly to the download and update to the new version.

    Ok, so whats new in 1.0? I have aggregated all the comments and tried to implement all your requests. As I am developing with Ruby on Rails, I have added some rubyism to the whole library as well. Do I have to change my running system completely now? No! no need to panick. There are only two small things you need to prepare before upgrading to 1.0 (mentioned at the end of the article). Ok, let's move to the updates...

    ajaxed console

    Probably the most fancy update is the new ajaxed console which supports you while you develop your applications. its immediately available through /ajaxed/console/ on your server. You'll find there a lot of useful stuff like a test runner, current configuration, documentation creator, regular expression evaluator, etc. Check a demo of the console here.

    New ajaxed config

    The ajaxed config (config.asp) has been moved to an own folder which is located in the webroot /ajaxedConfig/config.asp. Seperating the configuration from the library makes the update process easier now. You can overwrite the ajaxed library without any thoughts...

    html, head and body tags integration (header & footer)

    People used to have problems of integrating the html, head and body tags into their pages. This has been solved now and you can find a header.asp and footer.asp in your ajaxedConfig folder. Those files are automatically loaded within your pages. This change brought some additional features as well
    • ajaxedPage.defaultStructure property lets you load your page with a default header and footer (provided by the library itself). This is useful if you quickly need a standard structure and don't really care about the details
    • ajaxedPage.title gives you the possibility to assign a title to your page which may be used in the header
    • ajaxedPage.plain property allows to render a page completely plain which means it renders only those things which are defined in the page itself. (no header and footer). You will need this for your XHR
    • lib.exec lets you call any function only if it exists. This is used within the header to allow page specific head elements. Whenever you want anything to appear in your header just add a header() sub to your page and place the stuff there. e.g. especially javascripts, styles

    
    <% sub header() %>
        <style>
            /* some styles */
        </style>
        <% page.loadJSFile "someJSFile.js" %>
    <% end sub %>
    


    Best thing is you take a look into the predefined header.asp in your config to see whats going on there. And don't forget it's up to you what you bung in there. You can add as many lib.exec calls as you want.

    Environment support

    ajaxed supports two environments now: LIVE (production) and DEV (development). The config variable AJAXED_ENVIRONMENT sets the current environment. This enhancement allows us ...
    • creating conditions dependent on the environment. lib.env, lib.DEV and lib.LIVE are helpers to check which environment is running.
    • setting ajaxedPage.onlyDev to true in order to prevent calling it on the LIVE environment. E.g. whole ajaxed console and tests are only available on DEV env yet.
    (The default environment is always development)

    Unit tests

    TestFixture class lets you create unit tests for your own applications. It comes with a lot of different asserts (as commonly known in other unit testing frameworks) and is very quickly to set up. After you have written your tests you can run them directly from the ajaxed console. Tests for the library itself are located there as well. Here is an example of how you write a simple test (test file must start with test_):

    
    <!--#include virtual="/ajaxed/class_testFixture/testFixture.asp"-->
    <%
    set tf = new TestFixture
    tf.run()
    
    'create test methods starting with test_ 
    'followed by an increasing number
    sub test_1()
        tf.assertEqual "x", "x", "x should be x"
        tf.assert 1 = 1, "one should be definitely one"
        tf.assertInstanceOf "testFixture", tf, "this test should be a TestFixture"
        tf.assertInDelta 1, 1.1, 0.1, "equality with a delta"
        tf.assertHas array(1, 2, 3), 2, "the array should contain 2"
    end sub
    %>
    


    Documentation generator (Documentor)

    Some of you have long waited for the day to come :) Finally its here! Automatically create a documentation of your ASP code. The tool is called Documentor and can be found within your ajaxed console. It is also used for the generation of the actual ajaxed documentation which got a small face lift as well. Read How to document to know more about documenting your code (This manual can be found in your ajaxed console). Example of a method documentation:

    
    <%
    '' @DESCRIPTION: gets all users for a given country
    '' @PARAM: country [string]: specified country. e.g. AT
    '' @RETURN: [recordset]
    function getUsersBy(country)
      
    end function
    %>
    


    Automatic version check

    The ajaxed console checks automatically for a new version of ajaxed and reminds you if there is a new version available. Never miss an update.

    MD5 hash class

    We have a hash on board of the library now. Hash your passwords, etc. easily with:

    
    <% hash = (new MD5).hash("mypassword") %>
    


    Database methods

    There are some new useful database methods which makes your code more readable and easier to debug.

    
    <%
    'get the number of records of a table
    numberOfRecords = db.count("tablename", empty)
    
    'get number of records with condition
    numberOfRecords = db.count("tablename", "active = 1")
    
    'insert a record into a table (returns the ID)
    ID = db.insert("tablename", array("firstname", "jack", "lastname", "johnson"))
    
    'updating a record
    db.update "tablename", array("firstname", "Johnny"), ID
    
    'works as well with condition
    db.update "tablename", array("firstname", "Johnny"), "firstname = 'Jack'"
    
    'delete a record with a condition
    db.delete "tablename", "active = 0"
    
    'delete with an ID
    db.delete "tablename", 10
    
    'toggle the value of bit fields.
    db.toggle "tablename", "active", "country = 'AT'"
    
    'or with ID
    db.toggle "tablename", "active", 10
    %>
    


    Most of them support a condition parameter which can be either a number (its used automatically as ID) or a string (which is a condition for the WHERE clause).
    Those are handy for common CRUD operations. If you need more sophisticated stuff you should still use db.getRecordset().

    RSS reader and writer

    If you ever want to generate an RSS feed in your application or read another one - it's no problem anymore. ajaxed has the right component inside. There is a full detailed article about this component here.

    
    <%
    'example of reading some feed
    set r = new RSS
    r.url = "http://domain.com/feed"
    r.load()
    %>
    


    Template class

    ajaxed 1.0 includes a template class called TextTemplate. Very useful if you send emails within your applications. Create an template for the content of your email and parse it using TextTemplate. The ajaxed console provides a simple management of templates. For more details about TextTemplate read the article Template component for classic ASP or check the ajaxed documentation. Simple example

    
    <%
    set t = new TextTemplate
    t.filename = "mytemplate.template"
    t.add "name", "jack johnson"
    
    'your email object
    'i use the first line of the template as the subject of my emails
    mail.subject = t.getFirstLine()
    mail.body = t.getAllButFirstLine()
    %>
    


    Cache component

    Caching data, rss feeds, etc. globally for all visitors can be achieved now with the Cache class. This class has been around for while as well and a details article is available here.

    Discussion group launched

    Google GroupsI have created an own google asp ajaxed discussion group for better support and communication with all ajaxed users.

    Minor changes/improvements

    • init() must not exist necessarily anymore. If it exists it will be called. if not then not ;) Thanks to the new lib.exec() and lib.getFunction(). Check the reference for more details.
    • str.matching checks a string against a given regular expression pattern. Is much more readable then creating the regex object over and over again.
    • An error will be raised if there is no main() in your page. Makes it easier to know whats going on...
    • lib.contains is a useful function which lets us check if a given value exists in a given data structure. Currently supported data structures are array and dictionary. Usage is easy: lib.contains(array("yes", "no"), "maybe")
    • script.aculo.us is now part of the library as well. I decided to do this as it plays very nicely with prototype and both are being used in ruby on rails successfully for a long time.
    • some more minor improvements and bug fixed which you bette look up in the changes.txt or directly in the SVN

    That was it. Hope you enjoy working with the new version and I am looking forward for your feedback .. which already made the library what it is! Here are some things i am working on already...
    • generic email class
    • automatically notify admin about ANY errors
    • plugin infrastructure
    • asserts for request and response
    • logging possibilities
    • validator for business objects
    • dropdown control and calendar control

    If you UPGRADE from 0.3 then the only thing you need to do is to move your config.asp into the new /ajaxedConfig/ folder. Last but not least create a header.asp and footer.asp in that folder. Put all your html, head and body tags there. You can use the existing ones and adopt them to your needs. Last but not least change AJAXED_ENVIRONMENT to your needed environment.

    Download ajaxed 1.0
    If you want to live on the edge then grab the latest version from SVN.
    ]]>
    http://www.asp-ajaxed.org/article.asp?id=2Sat, 15 Nov 2008 14:30:00 UTC