LINQ to JS

A couple of days ago I got to know about LINQ to JavaScript plug-in. LINQ itself has been like – the more I play with it, the more I want to play with it and the more there is to learn about it. So this LINQJS comes as a new geography for me to explore. Here’s what I’ve learnt till now.

Codeplex.com is where you can download all the libraries. You can include the ones you need in your solution and for this blog, I’ve created an ASP.NET MVC application and referenced the following files into my site.master (not necessary that you need all three all the time):

   1: <script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
   2: <script src="../../Scripts/linq.js" type="text/javascript"></script>
   3: <script src="../../Scripts/jquery.linq.js" type="text/javascript"></script>

The first experiment was to get the code that’s on the site to work. Part of my Index.aspx looks like this:

   1: <td>Load a static list of numbers into the dropdownlist</td>
   2: <td style="width:300px;"><select id="select1" name="select1" /></td>
   3: <td><a href="#" onclick="loadList();">Load List</a></td>

So there’s a dropdownlist and a hyperlink that calls the javascript function ‘loadList()’ upon clicking.

 

 

   1: function loadList() {
   2:     $.Enumerable.Range(1, 10)
   3:             .Where(function (i) { return i % 2 == 0 })
   4:             .OrderByDescending(function (i) { return i })
   5:             .Select(function (i) { return $("<option>").text(i).val(i) })
   6:             .TojQuery()
   7:             .appendTo("#select1");
   8:     return false;
   9: }

Just to go over the code, this snippet loads 10 numbers (1-10) into an enumerable collection. The Where clause has a function that checks if the number in the collection is even. The OrderByDescending clause sorts the collection in descending order (duh!!). The Select clause is quite interesting. It actually returns an option tag with its text and value fields set to the individual item of the collection. This is then converted to an array (TojQuery()) and then is appended to the ‘select1’ select list. The last line just says no to submitting the form. The output produced looks as below:

image1 

Hmm.. interesting, but what’s the practical use of this example? And at the time of writing this blog, this is pretty much what the articles in the internet talked about LINQJS. I decided to take this to the next level.

 

 

I wanted to see how I’ll be able to loop through a custom array and here’s my set up for that:

   1: <td>Parse a custom array and populate the dropdownlist</td>
   2: <td style="width:300px;"><select id="select2" name="select2" /></td>
   3: <td><input type="submit" name="click" id="click" value="Load Domains" onclick="return loadDomains();" /></td>
   1: function loadDomains() {
   2:     var customArray = [
   3:                 { "domain": { "id": 21, "text": "HTML"} },
   4:                 { "domain": { "id": 35, "text": "C"} },
   5:                 { "domain": { "id": 47, "text": "C++"} },
   6:                 { "domain": { "id": 103, "text": "C#"} },
   7:                 { "domain": { "id": 100, "text": "MVC"} },
   8:                 { "domain": { "id": 130, "text": "ASP.NET"} },
   9:                 { "domain": { "id": 155, "text": "LINQJS"} },
  10:                 { "domain": { "id": 301, "text": "LINQ"} }
  11:             ]
  12:  
  13:     $.Enumerable.From(customArray)
  14:     .Where(function (x) { return x.domain.id > 50 })
  15:     .OrderBy(function (x) { return x.domain.text })
  16:     .Select(function (x) { return $("<option>").text(x.domain.text).val(x.domain.id + ' - ' + x.domain.text) })
  17:     .TojQuery()
  18:     .appendTo("#select2");
  19:  
  20:     return false;
  21: }

 

I have my ‘domains’ initialized in the first few lines. Enumerable.From() accepts any type that can be enumerated and loops through its items (the implementation of this function is quite complex… at least for me). The Where clause only selects items whose id is greater than 50. The selected items are then added to the ‘select2’ control on the page.

image2

Look at that.. filtered and neatly sorted out. I think this example is a little more useful than the first. But I did want to go one more level: parsing XML using LINQJS.

   1: <td>Parse xml and populate the dropdownlist</td>
   2: <td style="width:300px;"><select id="select3" name="select3" /></td>
   3: <td><input type="submit" name="click" id="Submit2" value="Parse Xml" onclick="return parseXml();" /></td>

 

   1: function parseXml() {
   2:     txt = "<bookstore><book><id>1</id><title>Selfish Gene</title><author>Richard Dawkins</author></book><book><id>1</id><title>Elegant Universe</title><author>Brian Greene</author></book></bookstore>";
   3:  
   4:     var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
   5:     xmlDoc.async = "false";
   6:     xmlDoc.loadXML(txt);
   7:     $.Enumerable.From(xmlDoc.childNodes[0].childNodes)
   8:     .OrderBy(function (x) { return x.getElementsByTagName("title")[0].text })
   9:     .Select(function (x) {
  10:         var id = x.getElementsByTagName("id")[0].text;
  11:         var title = x.getElementsByTagName("title")[0].text;
  12:         var author = x.getElementsByTagName("author")[0].text;
  13:         return $("<option>").text(title + ' - ' + author).val(id + ' - ' + title + ' - ' + author);
  14:     })
  15:     .TojQuery()
  16:     .appendTo("#select3");
  17:  
  18:     return false;
  19: }

I’m sure you’re already familiar with the code now, so there’s no need to explain, except for one thing.

This code worked successfully on IE8 as different browsers handle this differently. The aim was not to show the loading of XML, but how LINQ parses the document.

This one gives the following output:

image3

This time LINQJS has successfully parsed and processed an xml text and populated a dropdownlist.

What’s the point of a web page without a postback? So, I added a button on the form. Load all the ‘select’ controls and click on the ‘very descriptive’ ‘Click’ button and I do some processing in the controller:

   1: [HttpPost]
   2: public ActionResult Index(FormCollection formCollection)
   3: {
   4:     StringBuilder messageBuilder = new StringBuilder();
   5:     if (formCollection["select1"] != null)
   6:     {
   7:         messageBuilder.AppendFormat("Selected Number: {0}<br/>", formCollection["select1"]);
   8:     }
   9:     if (formCollection["select2"] != null)
  10:     {
  11:         messageBuilder.AppendFormat("Selected Domain: {0}<br/>", formCollection["select2"]);
  12:     }
  13:     if (formCollection["select3"] != null)
  14:     {
  15:         messageBuilder.AppendFormat("Selected Book: {0}<br/>", formCollection["select3"]);
  16:     }
  17:  
  18:     ViewData["SelectedKey"] = messageBuilder.ToString();
  19:     return View();
  20: }

The selection:

image4

 

The output:

image5

Just as an FYI, a select list only posts back the value of the selected item (not the text of the selected item and definitely not the entire collection). That’s the reason you’re seeing the id’s for the second and the third controls.

Hope this serves as a decent intro for LINQJS. The code used above can be downloaded from here (right-click and ‘save target as’ does not work) and here's the reference for linq.js.

Verdict: Quite powerful, quite clean and with lots of scope for more incredible things!

4 Comments

  • Well explained. Though, it seems you have missed code snippet for Domain example.

  • Abhishek, thanks but I do see the snippet for the domain example. A browser refresh might fix the issue.

    Arun

  • ohh sorry ... it required scrolling. didn't notice that before :)

  • The final example would datenstrome a useful way to add a sequential index value to a projection. IEnumerable codes = Enumerable.Range(1, int.MaxValue);Note you would not want use a ToArray in this case.@Peter: The Join requires that selectors for Key values (one for each list) is supplied and joins on the basis of a match between keys, its a far more complex and expensive process. Zip does not require a key, it just assumes that item N in list 1 matches item N in list 2.

Comments have been disabled for this content.