Archive

Archive for the ‘Ajax’ Category

Insert multiple oData records with a single post

6 February 2011 Leave a comment

A few months ago I was playing around with some user interface ideas. I used the jQuery sortable which will save to my oData service after the user had made a change. This caused multiple posts to the database, depending on the amount of items added, removed or reordered should the user navigate away before it was all completed.
After some searching I found a way around this which I decided to play around with again today for a project I will be doing soon. The idea is to combine all the updates/inserts into a sequence and then submit it in a single post. This will make it a lot quicker and also have a lot less code to display a single message when all the updates are complete.

This post will not go into much detail about getting the whole lot set up, so here is what you need already set up:

  • ASP.NET website building and running
  • WCF Data Service set up to return values when queried through the browser
  • MS AJAX Toolkit (Had to download the source to get all the .js libraries I needed)

For the sake of not struggling I copied all the files from the .\MS Ajax\SampleWebSites\AjaxClientWebSite\Scripts\MicrosoftAjax folder into my website’s Scripts folder and referenced only Start.debug.js (or Start.js for release). This takes care of loading all the rest of the required libraries.

My data service was called TestDataService.svc with a single table containing two columns since this was just the bare minimum to get my head around it again.

In my html page I have the following code to load, display and insert data.

    
    
        // We require this library
        Sys.require([Sys.components.dataView, Sys.components.openDataContext, Sys.components.openDataServiceProxy]);
        
        // A reference to our service
        var exampleService;

        // Page loaded and DOM ready
        Sys.onReady(function () {
            // Create the proxy to our data service
            exampleService = new Sys.Data.OpenDataServiceProxy("/TestDataService.svc");
            // Load the data from the service
            loadData();
        });

        function loadData() {
            // Query the service
            exampleService.query("/testTables", cbSuccess, cbFailure);
        }

        // Success callback for the loadData query
        function cbSuccess(result, context, operation, userContext) {
            // Clear the list
            $("#dbData").children().remove();
            // Add all the items from the database/service to the list
            $.each(result, function (index, row) {
                $("#dbData").append("<li>" + row.name + ": " + row.value + "</li>");
            });
        }

        // Failure callback for the loadData query
        function cbFailure() {
            alert("Error contacting service");
        }

        // Insert multiple recors into the database using a single post
        function insertMultiple() {
            // Sequence action for inserting data
            var actionSequence = exampleService.createActionSequence();
            // Creating 3 records
            for (var i = 0; i &lt; 3; i++) {
                var tmpData = {
                    name: "Sequence" + i, // Column name for the table
                    value: "" + i // Column name for the table
                };
                // Add the freshly create item to the sequence for testTable
                actionSequence.addInsertAction(tmpData, "/testTables");
            }
            // Execute the sequence
            actionSequence.execute(cbInsertComplete, "Inserted all three records");
        }

        // Success callback for the bulk insert
        function cbInsertComplete(results) {
            // Results returned after successful insert as _result. 
            $.each(results, function (index, row) {
                $("#dbData").append("<li>" + row._result.name + ": " + row._result.value + "</li>");
            });
        }
    

There is not much to the HTML

        <p>
            <ul id="dbData">
                        <!-- Data gets loaded here -->
            </ul>
        </p>
        <p>            
            <input id="Button2" type="button" value="Reload Data" onclick="loadData();" />
            <input id="Button1" type="button" value="Insert Data" onclick="insertMultiple();" />
        </p>

As you can see in the image below, when running this through FireBug you can see the first request to load the data which is currently empty, then there is a single post which posts the data and get the results back. We use the results to add the new data to the list.

This code is a very rough guide to executing a sequence of actions using oData services and should not be used as is in a live environment. No error checking is done and no best practices are followed in this post, it is purely the very basics to getting started.

If you would like more detailed information then please do contact me, I will be more than happy to help where I can.

Advertisements

jEditable Dropdown Text

9 June 2010 3 comments

I was stuck for a few minutes trying to figure out how to display the text of the selected item in the dropdown when using jEditable.

My script was querying an odata service (WCF Data Service) which returned a list of values with their ids.  I wanted this to be represented in a dropdown with the text displayed instead of the default “selected value”.  After struggling for a bit I came up with an idea, since someone else was struggling with the same problem I decided to create a post about it.

Here is my JavaScript code:

var settings = new Array(); // holds settings
function loadSettings(selected) {
    // loads the settings from the database via oData service
    $.ajax({
        type: "GET",
        url: "dataService.svc/Settings?$orderby=Code%20asc",
        data: {}, // required for Chrome
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        async: false, // wait until it's done before setting up jEditable
        success: function (data) {
            /* Settings
             *   int SettingId (PK)
             *   string Title
             */
            // step through all the results
            $.each(data.d, function () {
                // add them to a hashtable
                settings[this.SettingId] = this.Title;
            });
            // check if a selected one was passed in
            if (selected != undefined)
                settings["selected"] = selected; // set the selected one
        },
        error: function (msg, a) {
            // call error method
            // TODO: Display correct error here by checking resulting code
            showError("ERROR: Could not load the settings from the database");
        }
    });

    // get the selected one's TEXT from the hashtable
    var set = settings[val];
    // TODO: Make this code better, use .append("<span />");
    $('#settings').html('<span id="ddSettings">' + set + '</span>');

    // setup field for inline editing
    $('#ddSettings').editable(function (value, settings) {
        saveSettings(value, docId, settings); // function to save settings
        return suffixes[value]; // return the text from the hastable
    }, 
    {
        data: settings, // my hashtable
        cancel: 'Cancel',
        placeholder: '---',
        submit: 'Save',
        tooltip: 'Click to edit',
        type: 'select',
        style: 'display: inline;'
    });
}

And my html:

<html>
<head>
    <script src="/Scripts/jquery-1.4.2.min.js" type="text/javascript"></script>
    <script src="../Scripts/jquery.jeditable.mini.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function() {
            loadSettings();  // load the settings dropdown
        });

        // put the JavaScript source from above in here
    </script>
</head>
    <body>
        Settings:&nbsp;<div id="settings"></div>
    </body>
</html>

I hope this will help someone get around this problem.

ETag value in header different from object ETag

1 June 2010 4 comments

This is more a note to self which will hopefully help someone.

The last couple of weeks I have been working on a web application that uses WCF Data Services (oData) and jQuery.  I started receiving the following error: "The etag value in the request header does not match with the current etag value of the object.".  After hours of struggling I found out that it was the oninsert trigger in my database causing the problem.  It seems that when you insert the data the trigger changes that and then it is different from what you inserted and it then throws an error instead of returning the record you have just inserted.

Invalid JSON primitive in Chrome

24 April 2010 2 comments

I ran into the strangest problem today.  My Ajax call to my web service failed with the error “Invalid JSON primitive”, but only in Chrome…it worked in all my other browsers.

After reading up a bit it seemed to be a problem with the data being sent to the service.  Strange enough I was not sending any…and that turned out to be the problem. 

So for whoever runs into this problem, try setting the data parameter in jQuery to nothing, like in the code below.

$.ajax({
    type: "POST",
    contentType: "application/json; charset=utf-8",
    url: "DataService.asmx/ProjectList",
    dataType: "json", //response type
    data: "{}", // IMPORTANT
    success: function(msg, success) {
        if (success == 'success') {
            //good response
            data = eval(msg.d);
            // attach the template
            $("#slideshow").setTemplateElement('projectFaderTemplate', null, { runnable_functions: true });
            // process the template
            $("#slideshow").processTemplate(msg);
        } else {
            $('#msg').html('Error: ' + msg.d); //service returned an error
        }
    },
    error: function(msg, e) {
        $('#msg').html('Error.  Please contact Technical.'); //error with ajax call
    }
});

Hope this helps someone.

Linq to XML

23 April 2010 Leave a comment

Here is a very short post giving an example on how to use Linq to XML. 

Let’s say we have the following XML

<?xml version="1.0" encoding="utf-8" ?>
<projects>
    <project title="Kraankuil">
        <screenshot>kraankuil.jpg</screenshot>
        <uri>http://www.kraankuil.co.za</uri>
        <technologies>Gimp, ASP.NET, CMS</technologies>
        <launched>12 March 2010</launched>
        <description>Website for a small family owned B&amp;B in South Africa.</description>
    </project>
</projects>

We will do a class to easily handle the data like this

public class Project
{
    public string Title { get; set; }
    public string Screenshot { get; set; }
    public string Uri { get; set; }
    public string Technologies { get; set; }
    public string Launched { get; set; }
    public string Description { get; set; }
}

I will be using this is a web service where I use this code

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public List<Project> ProjectList()
{ 
    XDocument dataDoc;

    // load the file - from cache if possible
    if (HttpContext.Current.Cache["dataDoc"] == null)
    {
        dataDoc = XDocument.Load(Server.MapPath("~/App_Data/Data.xml"));
        HttpContext.Current.Cache["dataDoc"] = dataDoc; // store it in the cache
    } else {
        dataDoc = (XDocument)HttpContext.Current.Cache["dataDoc"];
    }

    //get the projects from the file
    var projectList = from project in dataDoc.Descendants("project")
                      select new Project
                      {
                          Title = project.Attribute("title").Value,
                          Screenshot = project.Element("screenshot").Value,
                          Technologies = project.Element("technologies").Value,
                          Launched = project.Element("launched").Value,
                          Description = project.Element("description").Value,
                          Uri = project.Element("uri").Value
                      };

    return projectList.ToList();
}

Note: This is NOT a best practices guide so do use common sense, it is only meant to get you started

Onkeyup delay in JavaScript

18 September 2009 3 comments

In a recent post I used a little script to delay the onkeyup event for a set amount of milliseconds after the key was released.  That way a callback will not be made for every key that gets released, but only once the user stopped typing.  That post was brought up many times by a search for this specific function, so I decided to extract it into its own post.

For this post I will only show an alert message to keep it to the bare minimum.

First we need to add this to our page, preferably in the <head> section.  Have a look here if you are not familiar with XHTML compliant JavaScript, it’s a short and straight to the point post.

<script language="javascript" type="text/javascript">
    //<![CDATA[
    function delayTimer() {
        var timer;
        return function(fun, time) {
            clearTimeout(timer);
            timer = setTimeout(fun, time);
        };
    }
    var delayFunction = delayTimer();

    function showMessage() {
        alert('Delay expired!');
    }
    //]]>
</script>

This is just a timer which restarts itself every time the onkeyup event triggers.  Once it runs out it will execute the function that you specify later.

<input id="txtDelay" type="text" onkeyup="delayFunction(showMessage, 500);" />

This is our input box which uses the onkeyup delay.  We use our delegate that we created and pass in two parameters, the first is the function to call and the second is the delay before doing so.  In this case it will wait 500ms before calling the showMessage function.

That is all you need to get a delay for an event in JavaScript.

NOTE: This does NOT work when pasting text in using the mouse.  Disable right-click on the input box if you rely on this function to execute.

Hope this is clear.  Comment if there is anything unclear or if you have a better way for me to do things.

Update – Passing in a parameter to the function as requested by a comment

<script language="javascript" type="text/javascript">
    //<![CDATA[
    function delayTimer() {
        var timer;
        return function (fun, time) {
            clearTimeout(timer);
            timer = setTimeout(fun, time);
        };
    }

    var delayFunction = delayTimer();

    // Takes parameters to display
    function showMessage(message, sender) {
        alert(message + " - " + $(sender).val());
    }
    //]]>
</script>
<!-- Now passing in a function with parameters -->
<input id="txtDelay" type="text" onkeyup="delayFunction(function() { showMessage('Display this string!', $('#txtDelay')); }, 500);" />

Note that passing in ‘this’ to the function in the onclick event does not send the input through.

jQuery and ajax calling webservice

30 August 2009 Leave a comment

In my previous post I wrote a domain name availability checker.  In this post I will create the front-end for it to display the availability using jQuery and ajax.  Note that I changed the web service slightly for this post.  The service will now return ‘available’ or ‘not available’.

I will not be doing much in the sense of validation and will leave that up to you when implementing code.

First off I will add a new HTML (can do webform) file to my solution.  If you do not have jQuery, get it here and rename the downloaded file from .download to .js, then add them to your solution (right-click on the project and click ‘add existing file’, then browse to your jQuery download and add the file.  See image below).

addFile

You can drag the script files from the solution explorer into the header section of your page or add them yourself.  You should then have something along this line:

<head>
    <title>My domain checker</title>
    <script src="jquery-1.3.2.min.js" type="text/javascript"></script>
</head>

We need a text field where the domain will be entered.  If you are not using a plain HTML input, then you will have to do a clever thing later to get the .NET generated name using <%= TextBox1.ClientId %>, so I recommend using a HTML text input if possible.  Add an onkeyup event, I am using onkeyup="getAvailability(displayAvailability, 500);".  The 500 is the delay in ms after the user stopped typing, but more about this later.

Add the following javascript to the page:

    function getAvailabilityTimer() {
        var timer;
        return function(fun, time) {
            clearTimeout(timer);
            timer = setTimeout(fun, time);
        };
    }
    var getAvailability = getAvailabilityTimer();

This will act as the timer since the user stop typing until the ajax callback is made.  You  can change this time by altering the number (500) in the onclick event for the textbox we created above.

The final function is the one that will be doing all the work.  Here is what mine looks like:

function displayAvailability() {
    var domain = $('#domainName').val();
    var selector = "result";//easy to change later
    $.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        url: "DomainService.asmx/CheckDomain", //our service name with the method
        data: "{'domain' : '" + domain + ".com'}", //parameter for which to pass data
        dataType: "json", //responde type
        success: function(msg, success) {
            if (success == 'success') {
                $('#' + selector).html('Domain is ' + msg.d); //good response
            } else {
                $('#' + selector).html('Not able to check at the moment.  Please try again later.'); //service returned an error
            }
        },
        error: function(msg, success) {
            $('#' + selector).html('Not able to check at the moment.  Please try again later.'); //error with ajax call
        }
    });
}

One last thing before testing, in the web service, make sure to uncomment the [System.Web.Script.Services.ScriptService] attribute.  Then let’s start it up and try it out.

checker

And there you have it, working like a charm…I will leave the nice graphicy stuff to you!

When I was testing it the first time, I ran into some problems.  I used nikhilk’s web developer helper.  It is a MUST have when working with ajax.  The problem was that I forgot to set the ScriptService attribute *blush*.

Here is my whole pages’ source: (is the ‘ used right? hope so :))

<head>
    <title>My domain checker</title>
    <script src="jquery-1.3.2.min.js" type="text/javascript"></script>
    <style type="text/css">
        body 
        {
            line-height: 25px;
            vertical-align: middle;
        }
        label
        {
            padding-right: 5px;
        }
    </style>
    <script type="text/javascript" language="javascript">
        function getAvailabilityTimer() {
            var timer;
            return function(fun, time) {
                clearTimeout(timer);
                timer = setTimeout(fun, time);
            };
        }
        var getAvailability = getAvailabilityTimer();

        function displayAvailability() {
            var domain = $('#domainName').val();
            var selector = "result";//easy to change later
            $.ajax({
                type: "POST",
                contentType: "application/json; charset=utf-8",
                url: "DomainService.asmx/CheckDomain", //our service name with the method
                data: "{'domain' : '" + domain + ".com'}", //parameter for which to pass data
                dataType: "json", //responde type
                success: function(msg, success) {
                    if (success == 'success') {
                        $('#' + selector).html('Domain is ' + msg.d); //good response
                    } else {
                        $('#' + selector).html('Not able to check at the moment.  Please try again later.'); //service returned an error
                    }
                },
                error: function(msg, success) {
                    $('#' + selector).html('Not able to check at the moment.  Please try again later.'); //error with ajax call
                }
            });
        }
    </script>
</head>

<body>
<div id="result"></div>
<label for="domainName">Domain:</label><input id="domainName" type="text" onkeyup="getAvailability(displayAvailability, 500);" />.com
</body>
</html>

All feedback is appreciated.