Wednesday, June 16, 2010

Icons for Dijits with the iconClass

This blog will outline several attempts I made to add an my own icon to a dijit.form.Button. At first, I tried something like this:



It ended up looking silly, and the image and text weren't aligned very well. It looks even worse in a TabContainer. Then I found the iconClass property. Unfortunately, the documentation only shows you how to use dojo's icons. It didn't look too difficult to use my own, so I tried this:







This doesn't display the icon at all! The problem is that you have to specify the dimensions of the icon in an iconClass as well, otherwise it gets rendered as a 1px by 1px image. The icons I'm using are 16px by 16px, and I intended to have multiple icons throughout my document. To implement this, I did the following:









So now I can have multiple icons without re-coding a lot of the CSS. Dojo will align everything properly, and make it look really nice if you use this method.

Monday, June 14, 2010

Oracle Flashback Query

Here is a small, usable example of Oracle's Flashback Query feature. This can be a lifesaver when you forget a where clause when updating/deleting records. This example also showcases the TO_TIMESTAMP function, which is required by the flashback syntax:

CREATE TABLE test (test VARCHAR2(20));
INSERT INTO test (test) VALUES ('good value'); 
-- Inserted the value on December 23rd, at 10:20 AM

UPDATE test SET test = ‘bad value’; 
-- I did this at 10:22, OOPS!

SELECT test FROM test; 
-- Returns ‘bad value’

SELECT test FROM test AS OF TIMESTAMP(TO_TIMESTAMP(‘2009-12-23 10:21:00’, ‘YYYY-MM-DD HH24:MI:SS’)); 
-- Returns ‘good value’

Simple, quick and easy! 

Friday, June 11, 2010

ItemFileReadStore and Custom Queries/Filters

By all accounts the ItemFileReadStore is great ... however, the out-of-the-box query syntax is very limited. (Follow the link to see what it can do) In this post, we'll explore a simple way to alter the ItemFileReadStore that allows highly customizable queries with the fetch method.

Let's keep it simple: We are working with a data store that has a list of people and their ages. We need to fetch all of the people that are between the ages of 21 and 40. Given the default query syntax, this would be impossible (well, I'm sure a regular expression guru could probably do it, but that isn't practical. Plus, it may not work when a more complex criteria is needed. ie: comparing two attributes from a given item) Intuitively, this would make a lot of sense:


//myStore is an instance of ItemFileReadStore
myStore.fetch(
{
    query:
    {
        age_filter: function(store, item)
        {
            var age = parseFloat(store.getValue(item, 'age'));
            return age >= 21 && age <= 40;
        }
    },
    onItem: function(item)
    {
        //do whatever with/to your returned items
    }
});


Instead of a query parameter, a function that returns a boolean value would be passed to the fetch method. The fetch method would then execute the function against every item in the ItemFileReadStore passing an instance of the data-store and the current item as arguments. If the function returns false for an item, it would not be included in the results.

This opens up the door to unlimited query possibilities ... however the default ItemFileReadStore does NOT work in this manner. Why not? If I were to guess, it's because it doesn't fit into the specifications of the Read API. For example, this would not work on a QueryReadStore because the server can't interpret a javascript function and therefore wouldn't know how to filter it's results.

You may also be asking why we just don't check the age in the onItem callback and handle this logic there. We'll see a good reason why when we use the DataGrid's filter method.

Now the fun part, lets make a small change to the ItemFileReadStore to implement this feature. I am using Dojo 1.4.3, and, of course, you need the source files (not the "compressed" files) Open up dojo/data/ItemFileReadStore.js and look for the _fetchItems method (should be around line 264) More specifically, we're going to look at a for loop in this function (line 288) This loop basically loops through all of the items in the data-store and determines whether or not they should be returned in the fetch results. Here is what it should look like once we're finished with it (notice the comments for the added lines of code)


for(i = 0; i < arrayOfItems.length; ++i){
    var match = true;
    var candidateItem = arrayOfItems[i];
    if(candidateItem === null){
        match = false;
    }else{
        for(key in requestArgs.query){
            value = requestArgs.query[key];
            // this is the beginning of what we add
            if(dojo.isFunction(value))
            {
                if(!value(self, candidateItem))
                    match = false;
            }
            // and this is the end ... notice the "else" in the next line as well
            else if(!self._containsValue(candidateItem, key, value, regexpList[key])){
                match = false;
            }
        }
    }
    if(match){
        items.push(candidateItem);
    }
}

Pretty simple. We just check each value of the query object to see if it is a function. If it is, we call the function passing this store and the current item as parameters. If it returns false, then we set the match to false and it doesn't get added in the fetch results.


Now we can pass in whatever function we want as a query criteria in the query object. Also notice that the keys in the query object don't matter (under the default query they do) In this example we used "age_filter," which doesn't exist as an attribute on any of the items in the data-store. And the existing functionality still works!


Where this really comes in handy is the DataGrid. With a data-grid that is backed by an ItemFileReadStore, we can now do this:

dataGrid.filter(
{
    age_filter: function(store, item)
    {
        var age = parseFloat(store.getValue(item, 'age'));
        return age >= 21 && age <= 40;
    }
});

So there you have it! 100% custom filtering criteria for a ItemFileReadStore and DataGrid. As an added bonus, since the ItemFileWriteStore inherits from the ItemFileReadStore, this functionality is already included ... if you make the small and simple change.

Thursday, June 10, 2010

Getting started: The purpose

To make a long story short, I've been looking for a way to develop PHP applications in a pragmatic manner. After multiple attempts at building my own framework, exploring existing frameworks, and careful study ... I think I finally found the combination I was looking for: PHP, the Zend Framework, and Dojo. The purpose of this blog is to explore the features, tricks, hacks and other useful tidbits that I have come across in this environment that aren't so obvious. It is my hope that as this blog grows, it will become a valuable resource for other developers like myself.

Thanks for stopping by.