Wednesday, April 16, 2014

Login Form

Thanks to this site I have discovered some new CSS and the great Font Awesome library.

This form looks good on a dark gradient background with white text.

#yourdiv {
    background: linear-gradient(#474747, #1D1D1D);
    border-radius: 5px;

    font-size: 13px;
    font-family: 'Lato', Calibri, Arial, sans-serif;
    text-shadow: 0 1px 0 rgba(255,255,255,0.8);
} 

It is common to have errors and warnings displayed.
Red errors and yellow warnings look good like this:

#insideyourdiv.error {
    color: #C00000;
    background: url("error.png"), url("error.png") scroll 0.5em 50% #FFF6DF;
    background-repeat: no-repeat, no-repeat;
    background-position: left top, right top;
    border: 1px solid #C00000;
}

#insideyourdiv.warning {
    color: #514721;
    background: url("warning.png"), url("warning.png") scroll 0.5em 50% #FFF6DF;
    background-repeat: no-repeat, no-repeat;
    background-position: left top, right top;
    border: 1px solid #FFB900;
} 


Friday, April 11, 2014

ExtJS Rant

First, let me say that I have very few negative comments regarding ExtJS/Sencha. I use it and consider it the premier* client-side javascript framework for web applications.

* I know there are many alternatives out there and am not trying to incite a holy war. This is simply my opinion: swayed by many things, not the least of which is the fact that Java is my "native" language. 

However, I finally came across something that just simply irked me. Simply, there is an AJAX/JSON Reader property on the proxy object named 'messageProperty'. One would think that this could be used somewhere/somehow. As near as I can tell, it is virtually useless.

Here is some pretty boilerplate sample code from the common use case of programmatically synchronizing the Store in response to a Button click:

someStore = Ext.create('Ext.data.Store', {
    model: 'someModel'
    ,autoLoad: true // set to true for production
    ,autoSync: false // set to false since we are submitting the form via AJAX, in lieu of using store syncing
    ,sorters: [ { root: 'data' ,property: 'id' ,direction: 'DESC' } ] // ASC | DESC
    ,proxy: {
        type: 'ajax'
        ,listeners: { exception: aCommonExceptionListener }
        ,reader: { type: 'json', root: 'data', successProperty: 'success', messageProperty: 'message' }
        ,writer: { type: 'json', root: 'data', encode: true, allowSingle: false }
        ,api: { 
            read: 'aRestUrl/view' 
            ,update: 'aRestUrl/update' 
        }
    }
});

namespace.doSaveChanges = function() {

    var options = {
        callback: function() { }
        ,success: function() {
            a.LoadMask.hide();            
            Ext.Msg.show( { title: 'Success'
                ,msg: 'Changes were Saved'
                ,buttons: Ext.Msg.OK
                ,icon: Ext.Msg.INFO
            } );
            Ext.Msg.setY(25);                
        }
        ,failure: function(batch, options) { 
            a.LoadMask.hide();            
            Ext.Msg.show( { title: 'Error'
                ,msg: batch.proxy.reader.rawData.message
                ,buttons: Ext.Msg.OK
                ,icon: Ext.Msg.ERROR
            } );
            Ext.Msg.setY(25);
        }
    };
    yourStore.sync(options);
}; 

So... notice the message property defined in the Store's Proxy.  Based on the similar property 'successProperty', I think it is safe to assume that the Proxy will make the JSON object with this name available to your code via a getter [like getMessage() !!!].  Nope,... you will notice that I have to navigate the batch.proxy.reader.rawData.message property manually.  AND... there is no abstraction of 'message' at all at this point... You are forced to use the exact message property that you send!

If I am missing something, please educate me.  Please.  But until then... total fail on this on fellas.

p.s. This is totally unrelated to this rant but I wanted to capture it.  I am using Spring WebMVC and, though this is documented by sencha, it is not exactly obvious.  It is important that you configure your JSON writer with this...

root: 'data', encode: true

to ensure that Spring will put the JSON into the 'data' parameter on the request.

@RequestMapping(value="aRestUrl/update", method=RequestMethod.POST)
public void update(HttpServletRequest request, HttpServletResponse response) throws IOException {

    final UserRequestContext ctx = this.createRequestContext(request, null);
    final String data = request.getParameter("data");        
    
    final ServiceResponse sr = this.getService().updateSpecialQuestions(ctx, data);
    final ExtJSAJAX model = new ExtJSAJAX(sr);       
    
    this.getJSON(response, model);
}    

Wednesday, April 9, 2014

ExtJS Button - Confirm when using HREF

ExtJS4 buttons (Ext.button.Button) have the properties 'href' and 'hrefTarget' to allow a button to act more naturally like a hyperlink.  However, I had the need to display a confirmation dialog (under certain conditions) before navigating away from the page.  This code feels a bit hacky but it works. The most pertinent thing to mention is that the 'allowDefault' property is not an ExtJS property so by using it, it _might_ be subject to a conflict in later versions of ExtJS.  Constructive feedback and questions are welcome.

This is the relevant stack:
ExtJS 4.2

Simply install this function as the click listener (not to be confused with the 'handler' property, which may work but is untested by me):
var button = Ext.create('Ext.button.Button', {   
    text: 'Back to ...'
    ,iconCls: 'clsActionBack'
    ,disabled: false
    ,href: 'href-goes-here'
    ,hrefTarget: '_self'
    ,listeners: { click: the_following_function }
    ,handler: function() { }
    ,scope: this
});


var the_following_function = function(btn, e, eOpts) {        
    if (btn.allowDefault) {
        // nothing special to do; will fall thru to return true
    } else {
        var isdirty = some_boolean_logic_or_function_here();
        if (isdirty) {
            e.preventDefault();

            Ext.Msg.confirm( 'Confirm'
                ,'Do you want to leave this page without saving your changes?'
                ,function(id, value) { 
                    if (id === 'yes') {                        
                        btn.allowDefault = true;
                        btn.btnEl.dom.click();
                    }
                }
            );
            Ext.Msg.setY(25); // (optional) reposition the msgbox

            return false;                 
        }
    }
    
    return true;
};