Wednesday, 23 September 2015

Semantic UI age Validation

We're doing lots with Semantic UI forms and I'm sort of loving and hating their form validation... more loving than hating after discovering the ability to add validation rules!

We need to check ages before submitting a form and, while we check on the server-side, it's always nice to check on the client-side as well! We've implemented a DateTimePicker and restricted the selectable dates within it but the user can still enter an invalid date manually. To validate this using Semantic UI I wrote this simple Validation RUle and added it to $.fn.form.settings.rules:

$.fn.form.settings.rules.age = function(value, age) {
    if(moment(value, "DD/MM/YYYY").isBefore(moment().subtract(parseInt(age, 10), "year"))){
        return true;
    }else{
        return false;
    }
}

Like most of the things I write these days it requires Moment.js and to use it simply add a rule like this to your form:

"Birthdate": {
    "identifier" : "Birthdate",
    "rules": [
        {
            "type"   : "age[18]",
            "prompt" : "You need to be at least 18"
        }
    ]
}

This will check to see if the date entered is older than 18 years. To change the number of years simply alter 18.

Thursday, 20 August 2015

Datatables and Semantic UI

We're doing loads of work with Semantic UI at the minute at work and so I've spent some time this morning getting Datatables to work nicely with it. With the update of Datatables to 1.10.8 we can now access the numbers pagingType and this is just crying out for tweaking. In order to do this I added this function on the draw call back:

"pagingType": "numbers",
"drawCallback": function() {
    var tableA_paginate = $("#tableA_paginate");
    var page_numbers = tableA_paginate.find("span").children().clone(true);
    tableA_paginate.empty().append(page_numbers);
    tableA_paginate.addClass("right floated ui pagination menu");
    tableA_paginate.children().each(function(k, v){
        $(v).addClass("item");
        if($(v).hasClass("current")){
            $(v).addClass("active");
        }
        if($(v).hasClass("ellipsis")){
            $(v).addClass("disabled");
        }
    })
},

That left the length and search parts though so I got to thinking about grids within Semantic and I altered the dom to (actually, you should probably do this in order for the pagination above to look nice):

"dom": '<"ui grid form"<"four wide column"l><"eight wide column"><"four wide column"f>>t<"ui grid"<"eight wide column"i><"right floated eight wide column"p>>',

And then to turn the length and search parts of the Datatable into proper Semantic UI elements I added this initialization complete callback:

"initComplete": function(settings, json) {
    // Tidy up length select
    var tableA_length = $("#tableA_length");
    var select = tableA_length.find("select").clone(true);
    tableA_length.find("select").remove();
    select.attr("id", select.attr("name") + "_select").addClass("ui dropdown");
    tableA_length.find("label").attr("for", select.attr("name") + "_select");
    tableA_length.append(select);
    tableA_length.addClass("field");
    select.dropdown();
    // Tidy up search
    var tableA_filter = $("#tableA_filter");
    var input = tableA_filter.find("input").clone(true);
    tableA_filter.find("input").remove();
    input.attr("id", tableA_filter.attr("id") + "_input");
    tableA_filter.find("label").attr("for", tableA_filter.attr("id") + "_input");
    tableA_filter.append(input);
 },

A fully working example is on JSFiddle, with a couple of added extras that we needed for our implementation.

I know that there has been some discussion about implementing a Semantic UI theme but I've found this use of callbacks to work a treat... perhaps I should get my finger out and make a proper plug-in though...?

I think I'm correct in putting the pagination into a draw callback as this gets redrawn on each table draw and the other parts of the table don't seem to be so I've kept them in the initialization callback.

Thursday, 23 July 2015

Semantic UI form validation on SalesForce

I spent a couple of hours trying to figure this out this morning so I though it would be worth documenting. When you use apex:inputText within SalesForce there are all sorts of extra stuff added to the id and name, as long as you use apex:outputLabels with the correct for and value values then all is gravy as the labels and inputs match. So markup like this:

<div class="two fields">
    <div class="eight wide field">
        <apex:outputLabel for="firstName" value="First Name"/>
        <apex:inputText value="{!firstName}" id="firstName"/>
    </div>
    <div class="eight wide field">
        <apex:outputLabel for="lastName" value="Last Name"/>
        <apex:inputText value="{!lastName}" id="lastName"/>
    </div>
</div>

Will produce this HTML:

<div class="two fields">
    <div class="eight wide field">
        <label for="somePageName:someTemplate:someForm:firstName">First Name</label>
        <input id="somePageName:someTemplate:someForm:firstName" type="text" name="somePageName:someTemplate:someForm:firstName"/>
    </div>
    <div class="eight wide field">
        <label for="somePageName:someTemplate:someForm:lastName">Last Name</label>
        <input id="somePageName:someTemplate:someForm:lastName" type="text" name="somePageName:someTemplate:someForm:lastName" />
    </div>
</div>

Which is cool for most things as, if you need to reference the field with jQuery you can use:

$("input[id$='firstName']")

Or in regular JavaScript:

document.querySelector("input[id$='firstName']")

But Semantic UI form validation matches against either the name, id or the data-validate attributes. I know from experience that, coming from doing lots of jQuery, I wanted to get the id and add that... perhaps using something like this:

document.querySelector("input[id$='firstName']").id

To pass the generated id to the validation rules for the identifier... don't go that route though, it'll only lead to madness!!!

Rather use the data-validate by adding html-data-validate to the apex:inputText. It's much more readable and will make things ever-so-much easier on you:

<div class="two fields">
    <div class="eight wide field">
        <label for="somePageName:someTemplate:someForm:firstName">First Name</label>
        <input id="somePageName:someTemplate:someForm:firstName" type="text" name="somePageName:someTemplate:someForm:firstName" html-data-validate="firstName"/>
    </div>
    <div class="eight wide field">
        <label for="somePageName:someTemplate:someForm:lastName">Last Name</label>
        <input id="somePageName:someTemplate:someForm:lastName" type="text" name="somePageName:someTemplate:someForm:lastName" html-data-validate="lastName"/>
    </div>
</div>

Really, it's much easier doing it this way and it will save you no end of headaches trying to remember the somePageName and someTemplate values.

Wednesday, 15 July 2015

Testing JavaScript Switch and Object speeds

I've been sort-of anti switch for a while now, initially in terms of JavaScript after reading about a great alternative which uses an object of functions which meant I could create a variable with all my switch conditions in it and test that rather than falling through a switch control, but more latterly for PHP where a similar approach can be used.

Earlier this week I was casting my eye over someone else's code and I started waxing lyrical about alternatives to switch and re-wrote his code to do implement the alternative. He, quite rightly, asked if it was quicker than using a switch... which left me stumped. You see I'd looked at the elegance of the code rather than its efficiency, a question of style over substance, so last night I thought I'd work up some tests to see if it really was as slow as I feared:

// Recording Object
var times = {
    "switch": "",
    "object": ""
};

// Testing array
var a = ["0", "1", "2", "0", "1", "2", "0", "1", "2", "0", "1", "2", "0", "1", "2"];

// Switch test
var start_switch = new Date().getTime();
var return_switch = "";
for (var b = 0; b < 50000; ++b) {
    for (var x = 0; x < a.length; x++) {
        switch (a[x]) {
            case "2":
                return_switch += "two";
                break;
            case "1":
                return_switch += "one";
                break;
            default:
                return_switch += "zero";
                break;
        }
    }
}
var end_switch = new Date().getTime();
times["switch"] = end_switch - start_switch;

// Object test
var switch_replacement = {
    "1": function () {
        return "one";
    },
    "2": function () {
        return "two";
    }
};
var start_object = new Date().getTime();
var return_object = "";
for (var c = 0; c < 50000; ++c) {
    for (var y = 0; y < a.length; y++) {
        return_object += (switch_replacement[a[y]]) ? switch_replacement[a[y]]() : "zero";
    }
}
var end_object = new Date().getTime();
times.object = end_object - start_object;

// Results
alert(JSON.stringify(times));

Most of the time the object approach takes about a third again longer than the switch, though every-so-often the object is significantly quicker... though I'm at a loss for how to explain that. In terms of breaking the code down though, it is much more readable, so I think that I'll stick with it despite it being slightly slower.

Thursday, 2 July 2015

Udemy's Aws Certified Developer Associate DynamoDB scripts

I've been plowing through this course from Udamy and enjoying it loads but I'm also aware that the AWS SDK for PHP has recently been updated. I cloned the files from GitHub but they failed, and failed silently.

After checking the errors with the judicious use of:

<?php
    // Find out what the issues are:
    ini_set('display_errors',1);
    ini_set('display_startup_errors',1);
    error_reporting(-1);
    // Rest of the code

I discovered that a version is now required and that a TimeZone is also wanted so I altered the code to include these requirements.

<?php
    // Find out what the issues are:
    ini_set('display_errors',1);
    ini_set('display_startup_errors',1);
    error_reporting(-1);

    // Date now needs to be set, which I guess is a good thing!
    date_default_timezone_set('Europe/London');
   
    require '/var/www/html/vendor/autoload.php';
    use Aws\DynamoDb\DynamoDbClient;
    
    $client = DynamoDbClient::factory(array(
        'region' => 'eu-west-1',  // replace with your desired region
        'version' => '2012-08-10' // Now needs a version
    ));
    // Rest of the code

However the waitUntilTableExists method was still causing issues so after a little research I changed it.

    // Rest of the code
    foreach($tableNames as $tableName) {
        echo "Waiting for table $tableName to be created." . PHP_EOL;
        $client->waitUntil('TableExists', array('TableName' => $tableName)); // Changed from v2
        echo "Table $tableName has been created." . PHP_EOL;
    }
?>

The script for loading data also required the version and TimeZone so I altered that as well. Adapted source available on GitHub and Pull Request submitted.

Tuesday, 23 June 2015

System.SelectOption to JSON Object

I had a particular need to examine the SelectOptions used on a page and I'm ever so much happier using the Developer Console so, after reading this answer on Stack Overflow to this question (How to parse String with Javascript looks like object) I thought I'd have a go myself. Given the following HTML:

<p><strong>Raw SF SelectOption data:</strong></p>
<div id="sfData"></div>
<p><strong>SF data converted to string with an array of objects using Regex:</strong></p>
<div id="sfDataToStr"></div>
<p><strong>SF data converted to proper array of Objects:</strong></p>
<div id="strToObj"></div>

We can use the following JavaScript (using jQuery just to make things easier):

var sfData = 
    '[System.SelectOption[value="-- None --", label="-- None --", disabled="false"],' + 
    ' System.SelectOption[value="Special 1", label="Special 1", disabled="false"],' + 
    ' System.SelectOption[value="Special 2", label="Special 2", disabled="false"],' + 
    ' System.SelectOption[value="Special 3", label="Special 3", disabled="false"]]';
$(function(){
    $("#sfData").
        append(sfData).
        prepend($("<p></p>", {
            "text": "TYPE: " + typeof sfData}));
    var sfDataToStr = sfData.
        replace(/System.SelectOption\[(\w+)=/g, "{\"$1\":").
        replace(/\],\s?/g, "},").
        replace(/\",\s?(\w+)=/g, "\",\"$1\":").
        replace(/\]\]/g, "}]");
    $("#sfDataToStr").
        append(sfDataToStr).
        prepend($("<p></p>", {
            "text": "TYPE: " + typeof sfDataToStr}));
    var strToObj = JSON.parse(sfDataToStr);
    $("#strToObj").
        append(JSON.stringify(strToObj)).
        prepend($("<p></p>", {
            "text": "TYPE: " + typeof strToObj}));
});

To produce this markup:

<p><strong>Raw SF SelectOption data:</strong></p>
<div id="sfData"><p>TYPE: string</p>[System.SelectOption[value="-- None --", label="-- None --", disabled="false"], System.SelectOption[value="Special 1", label="Special 1", disabled="false"], System.SelectOption[value="Special 2", label="Special 2", disabled="false"], System.SelectOption[value="Special 3", label="Special 3", disabled="false"]]</div>
<p><strong>SF data converted to string with an array of objects using Regex:</strong></p>
<div id="sfDataToStr"><p>TYPE: string</p>[{"value":"-- None --","label":"-- None --","disabled":"false"},{"value":"Special 1","label":"Special 1","disabled":"false"},{"value":"Special 2","label":"Special 2","disabled":"false"},{"value":"Special 3","label":"Special 3","disabled":"false"}]</div>
<p><strong>SF data converted to proper array of Objects:</strong></p>
<div id="strToObj"><p>TYPE: object</p>[{"value":"-- None --","label":"-- None --","disabled":"false"},{"value":"Special 1","label":"Special 1","disabled":"false"},{"value":"Special 2","label":"Special 2","disabled":"false"},{"value":"Special 3","label":"Special 3","disabled":"false"}]</div>

Or this rendered markup:

Raw SF SelectOption data:

TYPE: string

[System.SelectOption[value="-- None --", label="-- None --", disabled="false"], System.SelectOption[value="Special 1", label="Special 1", disabled="false"], System.SelectOption[value="Special 2", label="Special 2", disabled="false"], System.SelectOption[value="Special 3", label="Special 3", disabled="false"]]

SF data converted to string with an array of objects using Regex:

TYPE: string

[{"value":"-- None --","label":"-- None --","disabled":"false"},{"value":"Special 1","label":"Special 1","disabled":"false"},{"value":"Special 2","label":"Special 2","disabled":"false"},{"value":"Special 3","label":"Special 3","disabled":"false"}]

SF data converted to proper array of Objects:

TYPE: object

[{"value":"-- None --","label":"-- None --","disabled":"false"},{"value":"Special 1","label":"Special 1","disabled":"false"},{"value":"Special 2","label":"Special 2","disabled":"false"},{"value":"Special 3","label":"Special 3","disabled":"false"}]

This makes a nice array of objects representing the original SF data which I can use later on, I've got a working example on JSFiddle. Hope it helps.

Sunday, 7 June 2015

Empty data array returned by ssp.class.php

I've just been reading a fantastic article and this is by far one of the best quotes in it:

"A senior developer is intimately familiar with their own failure."

It is perhaps because of the week I've just had that makes me feel like it's particularly brilliant. Let me explain: I've spent a week trying to re-code a really rather cool application (which my team wrote and which we're rightly proud) for another format… and, as you might've guessed, failed. It is not an insurmountable collection of issues but still issues that will take longer than I have to fix. I know just how I would fix them and the time involved in the fixing and I know the number of heartwarming highs and heartbreaking lows that effort would involve. The highs and lows I can live with but the time isn't something we can bear so it'll have to take a sprint or two or perhaps a re-examination of the whole solution just for that one format.

I'm balding and have dry-skin… I also have to moderate my diet and drink intake… I'm this close to dropping my cigarette intake to 3-a-day (I would give up but I sometimes think it's my vices that make me and 3-a-day sounds like I'm not really smoking at all!). Despite all this, when I sort out an issue in a project, I feel like I have the stature of a god (no, not Bhudda, some handsome Greek god). When I'm confronted with something that just doesn't work I feel each and every one of those extra pounds; each and every-flake of skin that lands on my t-shirt is indicative of my failure as not only a coder but member of the human race and each and every drink is a balm to my tortured soul.

I guess what I'm trying to say is that that there article by Matt Briggs has warmed my heart in that what I constantly feel is indicative that I deserve the title of Senior Developer… I guess. Perhaps as I ease myself into it I'll get myself down to 3-a-day, drop some pounds, shave my head and moisturise more. The drink intake has been successfully moderated as I really, really can't code with a hangover and drunken coding is a recipe for disaster!

Getting back to the title of this post though I noticed that a server-side script written to return rows for a DataTable wasn't. It was returning the number of results but the data array was stubbornly empty. I checked the variable array being sent to the server in the ajax code and everything seemed to be fine. I echoed out the queries that the server-side script was making and clocked that the SQL was limiting the result to 0… this was despite the length variable being set… I took to echoing out individual values from the array and everything seemed to be okay until I got to the length… I'd not long ago changed the table as I was showing too much data which wasn't relevant and wasn't showing some data that the client felt was relevant. But I was still returning that data, the new stuff was simply tacked onto the end of the query so the resulting variable was huge. And that was the answer, I was asking far too questions and because the variable array was so large it was getting truncated and I was losing the pagination data, effectively making the query end like this:

LIMIT 0 OFFSET 0

or

LIMIT 0, 0

No wonder I wasn't getting any data despite the pagination still working. I knocked off the extraneous fields from the table columns array and all was well. Always pays to check the length of the data being sent in a GET request though!

Actually I had a brain-wave while I was looking at what I'd just written and clocked that I use that extra data to populate modal dialogs should the user want to edit the data. But I can still return the data and then act upon it, the issue is in the asking for it. I can add all sorts of data to the row but I need to be careful about what I ask for! There was me getting ready to prune the dialog and the returned data and I don't need to, everything it still gravy: I just need to ask for the absolute minimum of what I need.