Monday, May 6, 2013

KnockoutJS foreach binding and an old-school ASP.NET Repeater

Stuck with ye olde ASP.NET Webforms? It doesn't mean you have to miss out on all the good javascript fun (which is, in fact, also an old technology). At my current client (being a consultant and all), we have carefully introduced Knockout.js for the more complex screens. To be honest, it's only used in two screens at the moment, but the team is open to it being used more and more. That being said, we don't regard it as the silver bullet, and will choose our technology based on speed of development and maintainability.

Anyway, Knockout.js is great and has made certain portions of javascript much easier and cleaner. What remained a challenge, was how to integrate it with the way we did grids (tables of data). We used a Repeater to render a regular HTML table. Also, some custom scripts were used to add certain common behavior to all our tables.

This made it hard if not impossible for us to use the foreach binding. In essence, we wanted to keep the code for building and populating the table on the server side, but then have the rows bound to our Knockout.js viewmodels.

It turns out this is very easy. Instead of using the aforementioned foreach binding, you can use the with binding. Just put the following in your markup (in the ItemTemplate of your Repeater):
<tr data-bind="with: myItems()[<%# Container.ItemIndex %>]"></tr>
Then, inside your row, you can bind your cells or other controls to the properties of your viewmodel.

If you want to go the extra mile, you could have custom bindings that don't set the value/text/whatever of the controls initially, so the initial rendering is handled 100% server-side. With the normal bindings, Knockout.js will set the values when you call applyBindings(), even though the server already set these values. But in our case, it didn't slow things down. The important thing was that the bindings worked, so we could use it to hide/show/calculate certain values in certain cells.

So you see, the old lady (WebForms) just won't sink! Which is testament to the great work the ASP.NET devs did from the get-go.

Edit: You can find a working example on my GitHub.

Thursday, April 18, 2013

RhinoMocks and Use Arg T only within a mock method call while recording exception

Ouch, pained my brain over this one for the last half hour or so, but finally found the solution.

I had a call similar to:

repository.Stub(x => x.TryGet(
    Arg<Specification>.Matches(y => y.Something),
    out Arg<Customer>.Out(customerToReturn).Dummy))
.Return(true);

Because my first argument had a fairly large Matches call (it's simplified here), I refactored it to:

var specification = Arg<Specification>.Matches(y => y.Something);
repository.Stub(x => x.TryGet(
    specification, 
    out Arg<Customer>.Out(customerToReturn).Dummy))
.Return(true);

Ah, much more readable! Only, it didn't work. The exception I got was:

Use Arg<T> ONLY within a mock method call while recording. 2 arguments expected, 3 have been defined.

I could not for the life of me see where I was defining three arguments. I was looking at the second argument (the out argument) because that was the more exotic of the two.

I finally realized the exception message is actually rather correct. You have to use Arg inside the method you're stubbing. So you can't refactor like I did. I had to leave the call as it was:

repository.Stub(x => x.TryGet(
    Arg<Specification>.Matches(y => y.Something),
    out Arg<Customer>.Out(customerToReturn).Dummy))
.Return(true);

I hope this saves you (and me!) some time in the future (as the few posts I found focus on the fact that the method has to be virtual, which doesn't help if you're stubbing an interface).

Friday, April 12, 2013

Displaying your Picasa web albums on your site, with video support


In my spare time, I help maintain the website of the Ultimate Frisbee team where I play, the Ghent-based Gentle Ultimate. We have a fairly extensive amount of pictures and videos in a Google Picasa web album, which I wanted to integrate in our Drupal site.

Funnily enough, there is no (good) Drupal module for integrating Picasa into Drupal. Sure, there are modules for adding albums to nodes, but none to just show all albums and let the visitor browse through them.

There are two good (non-CMS-specific) libraries that help out: Picasa Web Album Integrator and pwa.js. I went with pwa.js because there was less javascript and it was simpler. Also, it fitted better in our site as it doesn't use lightbox/fancybox/shadowbox. Okay, and I found that one before I found PWI.

Nevertheless, pwa.js hasn't been updated in a while so I tweaked it to be a bit more modern:
  • cleaned up old code, commented code, unnecessary comments
  • use div tags to display single items (albums are still displayed in tables)
  • use css for style (like centering images)
  • use div and css for the navigation
But most importantly for us, I added support for Picasa videos. With more and more videos of our games, it was important that visitors could view the videos directly in our site, instead of having to click through to Picasa.

An extra advantage of pwa.js is that it isn't CMS-specific. You could make a Drupal module for this (and if you know how, I encourage you to do so), but with just one javascript file, you can use it on any kind of site.

The result can be found in on GitHub. If I find the time and need, I'll add some more changes:
  • flash video support (definitely doing this one)
  • possibly put the albums in divs also
  • more options for link texts, captions in albums (and not just when viewing a single item), etc.
  • optionally use lightbox or similar
  • easily show just one album
  • slideshow
Leave a comment below (or even better, on GitHub) if there are any requests or remarks!

Friday, April 5, 2013

jQuery, ASP.NET and subscribing to (pre-)postbacks

ASP.NET (WebForms) has a concept of postbacks. This is not entirely the same a submitting the current form. A LinkButton for example, when clicked, will call a __doPostBack javascript function which was added by ASP.NET.

This means that you can't always subscribe to the 'submit' event with jQuery:

$('form').on('submit', function() {});

You might want to subscribe to the postback because, just before calling the server, you need to fill some hidden field. At least, that was my case.

So, what we need is an event to subscribe to. This can be done thanks to the magic of javascript and the fact that you can replace functions on the fly:


var old__doPostBack = __doPostBack;
__doPostBack = function (eventTarget, eventArgument) {
    $('form').trigger('beforePostBack');
    old__doPostBack(eventTarget, eventArgument);
};

What we do here is simply put the __doPostBack function in a variable and the __doPostBack function with our own logic. That logic consists of raising a 'beforePostBack' event and then calling the actual post back logic.

To be able to reuse this simple logic, I created a jQuery plugin (which was interesting in its own), hosted on GitHub.

Wednesday, March 13, 2013

KnockOutJs custom bindings and data-attributes in a dropdown

At the day-job, we (finally!) get to use Knockout in our old-school WebForms application, albeit on one page to begin with (which is fine; one step at a time). If you're not familiar with Knockout, check out their site and great documentation. If you have a Pluralsight account, even better! They have an excellent course that will get you started very quickly.

Anyway, I came across a situation where I needed to bind a property of my viewmodel to the selected option of a dropdownlist. But not to the value of this selected option, but to a data-attribute. A custom binding is the solution, which could give you the following code:

<select data-bind="value: cityId, selectedDataCountry: country">
    <option data-country="Belgium" value="1">Brussels</option>
    <option data-country="United Kingdom" value="2">London</option>
    <option data-country="France" value="3">Paris</option>
</select>

So, if you selected 'Paris', your viewmodel would be updated by setting its country property to 'France'.

However, your custom binding would specifically have to search for the 'data-country' attribute. To do it a bit more generic, I wanted to be able to do something like the event binding in Knockout:

<select data-bind="value: cityId, selectedDataAttribute: { bind: 'data-country', to: 'country' }"></select>
A look into how the event binding works, and a train trip later, I had it working! The custom binding looks like this:
ko.bindingHandlers.selectedDataAttribute = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var dataAttribute = ko.utils.unwrapObservable(valueAccessor().bind);
        var viewModelProperty = valueAccessor().to;
        var dataAttributeValue = $(element).find(':selected').attr(dataAttribute);
        viewModel[viewModelProperty](dataAttributeValue);
  
        $(element).change(function() {
            var dataAttribute = ko.utils.unwrapObservable(valueAccessor().bind);
            var viewModelProperty = valueAccessor().to;
            var dataAttributeValue = $(element).find(':selected').attr(dataAttribute);
            viewModel[viewModelProperty](dataAttributeValue);
        });
    },
    update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var dataAttribute = ko.utils.unwrapObservable(valueAccessor().bind);
        var viewModelProperty = valueAccessor().to;
        var value = viewModel[viewModelProperty]();
        $("option", element).filter(function(option) { return $(option).attr(dataAttribute) === value; }).prop("selected", "selected");
    }
}

What it does is:
  • on init, get the name of the attribute, get the name of the observable on your viewmodel, get the value of the attribute, and set the value of the viewmodels property
  • do the same thing when the selected item of the dropdown changes
  • on update (when the viewmodel updates), get the name of the attribute, get the value of the viewmodel's observable, set the selected option in the dropdown.
Come to think of it, this would work with any attribute. Also, it relies on jQuery for simplicity, but you could do it in pure javascript.

Now, this custom binding allows us to use this markup:

<select data-bind="value: cityId, selectedDataAttribute: { bind: 'data-country', to: 'country' }">
    <option data-country="Belgium" value="1">Brussels</option>
    <option data-country="United Kingdom" value="2">London</option>
    <option data-country="France" value="3">Paris</option>
</select>

Now we can reuse the selectedDataAttribute binding with different data-attributes. You can find the code in my GitHub repository.

Monday, March 4, 2013

Poor man's (or woman's) code signing and auto-updating a Silverlight OOB XAP


To get Windows to trust your code (in my case a Silverlight Out-of-Browser XAP file), you need to sign it with a certificate. With a Silverlight OOB app, this makes the difference when installing between seeing this:


or this:


Also, trusted applications can automatically update themselves (when adding the necessary code of course) while untrusted ones can't. That's when you get the following error:

"Cannot update application, the installed application and update candidate differ in certificate/signature state."

To avoid this you must sign your XAP. This is fairly easy if you just buy a certificate, for which you can easily pay €100+ per year. However, if you (and your users) can live with the first dialog (the warning), you can at least get the auto-update feature.

First, we need to create a PFX file. Visual Studio can do this for you, when you indicate you want to sign your assembly. This is not the same as signing your XAP, but we need this step to create the PFX file. Go to the project properties and choose the signing tab. Then, mark the option to sign the assembly and select 'New...' in the dropdown:


Give your file a name and a password. If you don't provide a password, it will be a .snk file and we can't sign our XAP with that.


Now, uncheck the option for signing your assembly and choose to sign your XAP. Click on the 'Select from File...' button and choose your newly created PFX file (it will be in the folder of your current project).


You might also want to enter a Timestamp server URL (more on that below).

If you want more control, leave it unchecked and sign the XAP in a post-build event. Something like the following is what I used:

if $(Configuration) == Release (
  call "$(DevEnvDir)..\Tools\vsvars32.bat"
  signtool sign /v /f $(ProjectDir)MyKey.pfx /p MyPassPhrase /t http://timestamp.comodoca.com/authenticode TheApplication.xap
)

What this does is sign the XAP only when we're in Release mode. It first calls a batch script to set the necessary path variables, so the post-build event can call the signtool executable.

Then it signs TheApplication.xap with the MyKey.pfx file, 'MyPassPhrase' as password and it uses the Comodo timestamp server. Timestamping is necessary if you wan't to be able to continue to update the XAP, even when your certificate has expired.

When installing the application, the user will still get the warning that the source isn't trusted. This is because the certificate that signed the XAP hasn't been installed in the certificate store of the user's computer. But at least the auto-updating now works.

Saturday, January 19, 2013

Setup and Teardown in QUnit with modules

If you're testing your javascript with QUnit, you'll probably run into the case where you need to initialize variables, objects, ... before every test. You'll want to run every test with the same baseline.

In NUnit, you can use the SetUp attribute for this. In QUnit, it's a little different, but nothing too hard.

With the module function, you can group tests and have them run a function before starting each test. The module requires at least a string containing the name of the group, but can accept an object containing a setup and teardown property, which are both functions:

var tv;

module("Television channel tests", {
    setup: function () {
        tv = new Television();
    }
});
    
test("When moving the channel up", function() {
    tv.channelUp();
    equal(tv.getChannel(), 2, "The channel must be two.");
});
    
test("When moving the channel down", function() {
     tv.channelDown();
     equal(tv.getChannel(), 0, "The channel must be zero.");
});

module("Television brand tests", {
    setup: function () {
    tv = new Television("Sony");
    }
});
 
test("When requesting the brand", function() {
    tv.channelDown();
    equal(tv.brand, "Sony", "The brand must be Sony.");
});

Just put the module before any tests you want to 'contain' in the module. Any tests after a new call to module will be seen as part of this new module. This is what you'll see as a result:



If your interested in all the code, you can check out my BitBucket repository.