Archive for February, 2009

Extending dojo query part 2

Wednesday, February 25th, 2009

One of the cool things about the dojo NodeList is that most (all?) of the methods of NodeList return the list so you can chain NodeList operations ala jQuery. I realized that I had overlooked this with my removeAttr method when I wrote this:

dojo.query('#okConfirmBtn', dialogId).removeAttr('disabled').
  connect('onclick', clickFunc);



This would fail because removeAttr doesn’t return anything. It’s a simple change though, here’s the updated code:

//extend nodelist to have a removeAttr function
dojo.extend(dojo.NodeList, {
  removeAttr: function(attribute){
    return this.forEach(function(item) {
      dojo.removeAttr(item, attribute);
    });
  }
});


I also created another extension to the NodeList, a widgets method. This filters the NodeList to only nodes that are widgets and returns a NodeList of the widgets (not the dom nodes, the actual widgets).

/*extend nodelist to have a widgets function that
filters the list and returns a nodelist of dijit widgets*/
dojo.extend(dojo.NodeList, {
  widgets: function(){
    return this.filter(function(item)
    {
      var widget = dijit.byNode(item);
      if (widget) { return true; }
    }).map(function(widget) { return dijit.byNode(widget); });
  }
});

Extending dojo.query

Thursday, February 19th, 2009

I’ve been making more and more use of dojo.query. In case you’re not familiar with it, it basically allows you to query the dom using css syntax. It returns a NodeList which is an array of dom nodes with some extra dojo goodness built in. Like many (all?) of the dojo array processing functions, some of which I discussed here. NodeList also has some handy methods like addClass, removeClass, toggleClass, style, addContent, and a few others. NodeList.attr will set an attribute on every node in the nodelist. Very handy, but there’s no NodeList.removeAttr, I’m not sure why. I needed this functionality in a bunch of places. At first I was just using NodeList.forEach, but then I realized it would be better to just extend NodeList like so:

dojo.extend(dojo.NodeList, {
  removeAttr: function(attribute){
    this.forEach(function(x) { dojo.removeAttr(x, attribute); });
  }
});



That little bit of code adds the removeAttr function to NodeList! Now I can just do this:

dojo.query('foo').removeAttr('bar');



I’m doing something else with this that I think is interesting. I’m using alternative syntax (that probably has a name but I don’t know what it is) to reduce the amount of code I’m writing. For instance, instead of writing this:

if (valid)
{
  dojo.query('.detailSaveButton').removeAttr('disabled');
}
else
{
  dojo.query('.detailSaveButton').attr('disabled', 'disabled');
}



I’m doing this:

var action = (valid) ? 'removeAttr' : 'attr';
dojo.query('.detailSaveButton')[action]('disabled', 'disabled');



Note that the second argument being passed in will be ignored if action == ‘removeAttr’.

Asp.Net MVC + dojo dialog

Thursday, February 5th, 2009

I’ve bee working a ton with dojo and we recently started doing several projects using Asp.Net MVC.

I’ve made fairly extensive use of dojo dialogs (dijit.Dialog) and thus far I’ve usually just declared the dialog in markup because it was easy and most of the samples do it that way. But I was never totally satisfied with that. But last week I realized something cool: you can create a dojo dialog in javascript and set its href property to an Asp.Net mvc controller method that returns a view user control! This is cool for lots of reasons: you can separate the dialog markup from the rest of it, you only create the dialog if you actually need it, and best of all, the view user control can be used to conditionally render the dialog (enable/ disable controls, show different controls, etc) based on what you’ve got in viewdata, your model, or session state.

So I realized this should work in theory but I expected to have to fiddle with it to get it to actually work. Nope, it just works.

Some sample code:

Controller method:

public ActionResult GetDialog()
{
    var model = new RoleDialogModel();

    model.foo = this.foo;

    return View("RoleDialog", model);
}

Pretty straightforward, just create the model, set some properties and return the view, passing it the model. I won’t show you the view – just create a view user control and stick some markup in it.

Javascript:

showDialog: function(title, action, evt)
{
    //first check if it's there so we don't create a duplicate
    var dialog = dijit.byId('fooDialog');
    if (dialog) { dialog.destroyRecursive(); }

    dialog = new dijit.Dialog({
        refreshOnShow: true,
        id: 'fooDialog',
        title: title,
        onDownloadEnd: dojo.hitch(this, this.initializeDialog, action, evt)
    });

    dialog.setHref(this.dialogUrl);

    dialog.show();
}

There are a couple of things to notice here. First, I check if I’ve already created the dialog, dojo will puke if you try to create two objects with the same id. If it’s there I destroy it. I was previously just resetting its properties but the title wouldn’t change. I know I saw a post somewhere about how to do that but for now, I’m just destroying it and re-creating it. I set onDownloadEnd to an initialization function which hooks up ui events, etc. You should do any dijit widget stuff here, to be sure the widgets are there when you go looking for them. The setHref function sets the href of the dialog to the controller method above. I realized after I got this working that setHref is deprecated and you should use the href property instead but it was working so I didn’t want to mess with it.

I think this is a great way to handle dojo dialogs in an Asp.Net MVC app. I plan to continue using this approach and refine it going forward.