• 2012-11-25 12:00:00

    Building a web app with PhalconPHP and AngularJS Update

    It's been a while since I last wrote a blog post, so I wanted to touch on the effort to upgrade the application that I wrote for Harry Hog Fottball using PhalconPHP and AngularJS

    If you haven't read it, the first two blog posts were here and here.

    The application was written using the 0.4.5 version of PhalconPHP. Since then there have been significant changes to the framework, such as the introduction of a DI container, injectable objects and lately interfaces (in 0.7.0, to be released in a couple of days), I had to make some changes.

    There are a couple of things that I as a developer would like to see in PhalconPHP, which I am pretty sure will appear later on, since let's face it the framework is still very young (not even 1.0 version yet). Despite its "youth" it is a robust framework with excellent support, features and a growing community. One of these features is behaviors which I had to implement myself, and this was something new that came with this upgrade.

    Recently a new repo has been created on Github called the incubator, where developers can share implementations of common tasks, that act as drop ins to the framework and extend it. These implementations are all written in PHP so everyone can just download them and use them. The more submissions come in, the more the framework will grow and eventually these submissions will become part of the framework itself.

    Converting the 0.4.x application to 0.5.x

    The task of converting everything from 0.4 to 0.5 was a bit challenging. The reason behind it was the DI container and how best to use it to suit the needs of the current application. Now these challenges would not even be an issue if one started writing their application from scratch, but since I had everything in place, I ventured into upgrading vs. rewriting. Note that this kind of upgrade will most likely never happen again, since the framework has been changed accordingly so that future upgrades will not require developers to rewrite their code (like I did now). From 0.5.x onward the framework design has been kind of "frozen".

    I decided to create a new library that will help me with my tasks. I therefore created a custom bootstrap class, that would instantiate everything I wanted in my code. A short snippet of the class is below (the full code of course is in my Github repo which you are more than welcome to download and modify to suit your needs)

    namespace NDN;
    
    use \Phalcon\Config\Adapter\Ini as PhConfig;
    use \Phalcon\Loader as PhLoader;
    ....
    use \Phalcon\Exception as PhException;
    
    class Bootstrap
    {
        private $_di;
    
        /**
         * Constructor
         * 
         * @param $di
         */
        public function __construct($di)
        {
            $this->_di = $di;
        }
    
        /**
         * Runs the application performing all initializations
         * 
         * @param $options
         *
         * @return mixed
         */
        public function run($options)
        {
            $loaders = array(
                'config',
                'loader',
                'environment',
                'timezone',
                'debug',
                'flash',
                'url',
                'dispatcher',
                'view',
                'logger',
                'database',
                'session',
                'cache',
                'behaviors',
            );
    
    
            try {
                foreach ($loaders as $service)
                {
                    $function = 'init' . ucfirst($service);
    
                    $this->$function($options);
                }
    
                $application = new PhApplication();
                $application->setDI($this->_di);
    
                return $application->handle()->getContent();
    
            } catch (PhException $e) {
                echo $e->getMessage();
            } catch (\PDOException $e) {
                echo $e->getMessage();
            }
        }
    
        // Protected functions
    
        /**
         * Initializes the config. Reads it from its location and
         * stores it in the Di container for easier access
         *
         * @param array $options
         */
        protected function initConfig($options = array())
        {
            $configFile = ROOT_PATH . '/app/var/config/config.ini';
    
            // Create the new object
            $config = new PhConfig($configFile);
    
            // Store it in the Di container
            $this->_di->set('config', $config);
        }
    
        /**
         * Initializes the loader
         *
         * @param array $options
         */
        protected function initLoader($options = array())
        {
            $config = $this->_di->get('config');
    
            // Creates the autoloader
            $loader = new PhLoader();
    
            $loader->registerDirs(
                array(
                    ROOT_PATH . $config->app->path->controllers,
                    ROOT_PATH . $config->app->path->models,
                    ROOT_PATH . $config->app->path->library,
                )
            );
    
            // Register the namespace
            $loader->registerNamespaces(
                array("NDN" => $config->app->path->library)
            );
    
            $loader->register();
        }
    
        ....
    
        /**
         * Initializes the view and Volt
         *
         * @param array $options
         */
        protected function initView($options = array())
        {
            $config = $this->_di->get('config');
            $di     = $this->_di;
    
            $this->_di->set(
                'volt',
                function($view, $di) use($config)
                {
                    $volt = new PhVolt($view, $di);
                    $volt->setOptions(
                        array(
                            'compiledPath'      => ROOT_PATH . $config->app->volt->path,
                            'compiledExtension' => $config->app->volt->extension,
                            'compiledSeparator' => $config->app->volt->separator,
                            'stat'              => (bool) $config->app->volt->stat,
                        )
                    );
                    return $volt;
                }
            );
        }
        ....
    
        /**
         * Initializes the model behaviors
         *
         * @param array $options
         */
        protected function initBehaviors($options = array())
        {
            $session = $this->_di->getShared('session');
    
            // Timestamp
            $this->_di->set(
                'Timestamp',
                function() use ($session)
                {
                    $timestamp = new Models\Behaviors\Timestamp($session);
                    return $timestamp;
                }
            );
        }
    }
    

    I chose to show a few sections of this bootstrap which I will explain shortly. What this bootstrap class does is it initializes my whole environment and keeps my index.php file small.

    error_reporting(E_ALL);
    
    try {
    
        if (!defined('ROOT_PATH')) {
            define('ROOT_PATH', dirname(dirname(__FILE__)));
        }
    
        // Using require once because I want to get the specific
        // bootloader class here. The loader will be initialized
        // in my bootstrap class
        require_once ROOT_PATH . '/app/library/NDN/Bootstrap.php';
        require_once ROOT_PATH . '/app/library/NDN/Error.php';
    
        // Instantiate the DI container
        $di  = new \Phalcon\DI\FactoryDefault();
    
        // Instantiate the boostrap class and inject the DI container 
        // in it so that services can be registered
        $app = new \NDN\Bootstrap($di);
    
        // Here we go!
        echo $app->run(array());
    
    } catch (\Phalcon\Exception $e) {
        echo $e->getMessage();
    }
    

    As you can see the index.php is very small in terms of code.

    Let's have a look at a couple of the functions that are in the bootstrap.

        /**
         * Initializes the config. Reads it from its location and
         * stores it in the Di container for easier access
         *
         * @param array $options
         */
        protected function initConfig($options = array())
        {
            $configFile = ROOT_PATH . '/app/var/config/config.ini';
    
            // Create the new object
            $config = new PhConfig($configFile);
    
            // Store it in the Di container
            $this->_di->set('config', $config);
        }
    

    Pretty straight forward. The config INI file is read from its location and stored in the DI container. I need to do this first, since a lot of the parameters of the application are controlled from that file.

        /**
         * Initializes the loader
         *
         * @param array $options
         */
        protected function initLoader($options = array())
        {
            $config = $this->_di->get('config');
    
            // Creates the autoloader
            $loader = new PhLoader();
    
            $loader->registerDirs(
                array(
                    ROOT_PATH . $config->app->path->controllers,
                    ROOT_PATH . $config->app->path->models,
                    ROOT_PATH . $config->app->path->library,
                )
            );
    
            // Register the namespace
            $loader->registerNamespaces(
                array("NDN" => $config->app->path->library)
            );
    
            $loader->register();
        }
    

    The loader is what does all the discovery of classes for me. As you can see I store a lot of the paths in the config INI file, and I register my custom namespace NDN.

        /**
         * Initializes the view and Volt
         *
         * @param array $options
         */
        protected function initView($options = array())
        {
            $config = $this->di->get('config');
            $di     = $this->_di;
    
            $this->_di->set(
                'volt',
                function($view, $di) use($config)
                {
                    $volt = new PhVolt($view, $di);
                    $volt->setOptions(
                        array(
                            'compiledPath'      => ROOT_PATH . $config->app->volt->path,
                            'compiledExtension' => $config->app->volt->extension,
                            'compiledSeparator' => $config->app->volt->separator,
                            'stat'              => (bool) $config->app->volt->stat,
                        )
                    );
                    return $volt;
                }
            );
        }
    

    This is an interesting one. Registering the view and Volt. Volt is the template engine that comes with Phalcon. It is inspired by Twig and written in C, thus offering maximum performance. I set the compiled path, extension and separator for the template files and also I have a variable (set in the config of course) to allow the application to always create template files or not. In a production environment that variable (stat) will be set to false since templates do not change.

        /**
         * Initializes the model behaviors
         *
         * @param array $options
         */
        protected function initBehaviors($options = array())
        {
            $session = $this->_di->getShared('session');
    
            // Timestamp
            $this->_di->set(
                'Timestamp',
                function() use ($session)
                {
                    $timestamp = new Models\Behaviors\Timestamp($session);
                    return $timestamp;
                }
            );
        }
    

    The above is my implementation of behaviors. Of course it is far from perfect but it works the way I want to. A better implementation of this has been written by Wojtek Gancarczyk and is available in the incubator. All I do here is go through the behaviors I have (Timestamp only for now) and register them in the DI container so that I can reuse them later on with any model that needs them.

    Models

    Every model I have that interacts with my database tables extends the NDN\Model.

    class Model extends \Phalcon\Mvc\Model
    {
        protected $behaviors = array();
    
        /**
         * Adds a behavior in the model
         *
         * @param $behavior
         */
        public function addBehavior($behavior)
        {
            $this->behaviors[$behavior] = true;
        }
    
        public function beforeSave()
        {
            $di   = Di::getDefault();
    
            foreach ($this->behaviors as $behavior => $active)
            {
                if ($active && $di->has($behavior))
                {
                    $di->get($behavior)->beforeSave($this);
                }
            }
        }
    
        /**
         * @param array $parameters
         *
         * @static
         * @return Phalcon_Model_Resultset Model[]
         */
        static public function find($parameters = array())
        {
            return parent::find($parameters);
        }
    
        /**
         * @param array $parameters
         *
         * @static
         * @return  Phalcon_Model_Base   Models
         */
        static public function findFirst($parameters = array())
        {
            return parent::findFirst($parameters);
        }
    }
    

    The class itself is pretty simple, offering find and findFirst to the class that extends this. The interesting thing is that it also registers behaviors and calls the relevant validator function. So for instance the beforeSave validator checks the registered behaviors ($behaviors array), checks if they are active, checks if they exist in the DI container and gets them from there and then calls the beforeSave in the behavior class.

    The behavior class is equally simple:

    class Timestamp
    {
        protected $session;
    
        public function __construct($session)
        {
            $this->session= $session;
        }
    
        /**
         * beforeSave hook - called prior to any Save (insert/update)
         */
        public function beforeSave($record)
        {
            $auth     = $this->session->get('auth');
            $userId   = (isset($auth['id'])) ? (int) $auth['id'] : 0;
            $datetime = date('Y-m-d H:i:s');
            if (empty($record->created_at_user_id)) {
                $record->created_at         = $datetime;
                $record->created_at_user_id = $userId;
            }
            $record->last_update         = $datetime;
            $record->last_update_user_id = $userId;
        }
    }
    

    So effectively every time I call the save() function on a model, this piece of code will be executed, populating my fields with the date time and the user that created the record and/or updated it.

    In order to get this functionality to work, all I have to do in my model is to register the behavior like so:

    class Episodes extends \NDN\Model
    {
        /**
         * Initializes the class and sets any relationships with other models
         */
        public function initialize()
        {
            $this->addBehavior('Timestamp');
            $this->hasMany('id', 'Awards', 'episode_id');
        }
    }
    

    Controllers

    Very little has changed in the controller logic, so that was the easiest part of the upgrade. Of course I tweaked a few things but the code works as is. I still extended my custom NDN\Controller class which takes care of my breadcrumbs (NDN\Breadcrumbs) as well as the construction of the top menu. The biggest difference with the previous version is that I stopped using AngularJS to populate the menu (so I am no longer sending a JSON array in the view) and used Volt instead. It was a matter of preference and nothing more.

    Views

    Quite a bit of work had to be done in the views to switch everything to use Volt. Of course every view extension had to be changed to .volt but that was not the only change. I split the layout to use partials so that the header, navigation and footer are different sections (organizing things a bit better) and kept the master layout index.volt.

    I started using the built in Volt functions to generate content as well as tags and it was a nice surprise to see that everything was easy to use and it worked!

    <!DOCTYPE html>
    <html ng-app='HHF'>
        {{ partial('partials/header') }} 
        <body>
            <div id="spinner" style="display: none;">
                {{ image('img/ajax-loader.gif') }} Loading ...
            </div>
    
            {{ partial('partials/navbar') }}
    
            <div class='container-fluid'>
                <div class='row-fluid'>
                    <ul class='breadcrumb'>
                        <li>
                            {% for bc in breadcrumbs %}
                            {% if (bc['active']) %}
                            {{ bc['text'] }}
                            {% else %}
                            <a href='{{ bc['link'] }}'>{{ bc['text'] }}</a> 
                            <span class='divider'>/</span>
                            {% endif %}
                            {% endfor %}
                        </li>
                    </ul>
                </div>
    
                <?php echo $this->flash->output() ?>
    
                <div class="row-fluid">
                    <?php echo $this->getContent() ?>
                </div> <!-- row -->
    
                {{ partial('partials/footer') }}
            </div>
    
            {{ javascript_include(config.app.js.jquery, config.app.js.local) }}
            {{ javascript_include(config.app.js.jquery_ui, config.app.js.local) }}
            {{ javascript_include(config.app.js.bootstrap, config.app.js.local) }}
            {{ javascript_include(config.app.js.angular, config.app.js.local) }}
            {{ javascript_include(config.app.js.angular_resource, config.app.js.local) }}
            {{ javascript_include(config.app.js.angular_ui, config.app.js.local) }}
            {{ javascript_include('js/utils.js') }}
    
        </body>
    </html>
    

    The above is the index.volt. As you can see I call on the partials/header.volt, then the partials/navbar.volt (where the menu is generated) and then I construct the breadcrumbs (note the {% for bc in breadcrumbs %} block). After that the flash messenger comes into play, the main content displayed, the footer and finally the javascript includes that I need.

    I am still using AngularJS to make the necessary AJAX calls so that the relevant controller to retrieve the data but also to display this data on screen (which is cached to avoid unnecessary database hits).

    The Episodes view became

    {{ content() }}
    
    <div>
        <ul class='nav nav-tabs'>
            <li class='pull-right'>
                {{ addButton }}
            </li>
        </ul>
    </div>
    
    <div ng-controller='MainCtrl'>
        <table class='table table-bordered table-striped ng-cloak' ng-cloak>
            <thead>
            <tr>
                <th><a href='' ng-click="predicate='number'; reverse=!reverse">#</a></th>
                <th><a href='' ng-click="predicate='air_date'; reverse=!reverse">Date</a></th>
                <th><a href='' ng-click="predicate='outcome'; reverse=!reverse">W/L</a></th>
                <th><a href='' ng-click="predicate='summary'; reverse=!reverse">Summary</a></th>
            </tr>
            </thead>
            <tbody>
                <tr ng-repeat="episode in data.results | orderBy:predicate:reverse">
                    <td>[[episode.number]]</td>
                    <td width='7%'>[[episode.air_date]]</td>
                    <td>[[episode.outcome]]</td>
                    <td>[[episode.summary]]</td>
                    {% if (addButton) %}
                    <td width='1%'><a href='/episodes/edit/[[episode.id]]'><i class='icon-pencil'></i></a></td>
                    <td width='1%'><a href='/episodes/delete/[[episode.id]]'><i class='icon-remove'></i></a></td>
                    {% endif %}
                </tr>
            </tbody>
        </table>
    </div>
    

    The beauty of AngularJS! I only have to pass a JSON array with my results. ng-repeat with the orderBy filter allows me to present the data to the user and offer sorting capabilities per column. This is all done at the browser level without any database hits! Pretty awesome feature!

    For those that have used AngularJS in the past, you will note that I had to change the interpolate provider (i.e. the characters that wrap a string or a piece of code that AngularJS understands). Usually these characters are the curly brackets {{ }} but I changed them to [[ ]] to avoid collisions with Volt.

    This was done with a couple of lines of code in my definition of my AngularJS model:

    var ngModule = angular.module(
            'HHF', 
            ['ngResource', 'ui']
        )
        .config(
            function ($interpolateProvider) {
                $interpolateProvider.startSymbol('[[');
                $interpolateProvider.endSymbol(']]');
            }
        )
    

    Conclusion

    I spent at most a day working on this mostly because I wanted to try various things and see how it works. The actual time to convert the application (because let's face it, it is a small application) was a couple of hours inclusive of the time it took me to rename certain fields, restructure the folder structure, compile the new extension on my server and upload the data upstream.

    I am very satisfied with both AngularJS, which helps tremendously in my presentation layer, as well as with Phalcon. Phalcon's new design makes implementation a breeze, while AngularJS offers a lot of flexibility on the view layer.

    As written before, you are more than welcome to download the source code of this application here and use it for your own needs. Some resources are:

    References

  • 2012-09-02 12:00:00

    AngularJS - Simplicity in your browser

    Recently I was contacted by an acquaintance through my Google+ circles, who needed some help with a project of hers.

    Her task was to redesign a church website. Pretty simple stuff, CSS, HTML and content.

    Scope

    The particular church videotapes all the sermons and posts them on their channel in LiveStream for their followers to watch. One of the requirements was to redo the video archives page and to offer a link where followers can download the audio of each sermon for listening.

    Design (kinda)

    After the initial contact, I decided to get rid of all the bloated jQuery code that was there to control the video player and use AngularJS to control the generation of content. There were two key facts that influenced my decision:

    • the use of ng-repeat to generate the table that will list all the available sermons and
    • the variable binding that AngularJS offers to play the video in the available player.

    I also decided to switch the player to a new updated one that LiveStream offered, which features a slider to jump through the video, volume control and more.

    Previous code

    The previous code for that page was around 300 lines. The file had some CSS in it, quite a few lines of HTML but was heavy on javascript. There were a lot of jQuery functions which controlled the retrieval of the available videos per playlist. Each playlist would be effectively a collection of videos for a particular year. jQuery was observing clicks on specific links and make an AJAX call to the LiveStream API to retrieve the list of available data in JSON format, and output the formatted results (as a table) on screen. It was something like this:

    head
    title
    link - css
    style (inline)
    ....
    end inline style
    script jQuery
    script jQueryUI
    script jquery.timer
    link jquery-ui CSS
    link slider CSS
    style (inline)
    ....
    end inline style
    script jQuery
    $(document).ready ... // Document ready
    ....
    $("#div_col dd").click // Bind click to a year
    .....
    getPlaylists(year) // Not sure why this was ever here, not used
    ....
    getPlaylistClips(playlistID) // Gets the clips of the playlist
    .....
    playClip(clipID) // Plays the clip in the player
    .....
    end jQuery script
    script Video Player
    ....
    end head
    body
    navigation
    main container
    list of years
    instructions
    container to show video player
    container to show video list
    end body
    end html
    

    Enter AngularJS

    I checked the latest video player from LiveStream. The code was much cleaner and all I had to do is bind one variable, the GUID of the video, in the relevant call so that the video can be played. I also bound another variable (the video title) above the video so as to offer more information to the user.

    With a bit of Bootstrap CSS, I created two tabs and listed the two years 2012, 2011. A function was created in my AngularJS module to accept the year and make the relevant call to the LiveStream API to receive the data a a JSON object.

    ng-repeat (with >ng-cloak) was used to "print" the data on screen and the application was ready.

    I removed all the cruft and created one small file that is loaded and offers the functionality that we need. It is 50 lines of code (just the javascript part. The code is below with added comments for the reader to follow:

    // Create the module and inject the Resource object
    var ngModule = angular.module("CHF", ['ngResource']);
    
    // The main controller that needs the scope and resource
    ngModule.controller("MainCtrl", function ($scope, $resource) {
    
        // Calculates the current year
        //  ensures we always get the last year on first load
        $scope.currentYear = function () {
            var currentDate = new Date();
            return currentDate.getFullYear();
        };
    
        // This is the playlist array. This is obtained by 
        //  LiveStream and it changes once every year. 
        //  Hardly an effort by the administrator
        $scope.playlists = [
            {"year":"2012", "guid":"63426-xxx-xxx-xxx"},
            {"year":"2011", "guid":"84f84-xxx-xxx-xxx"}
        ];
    
        // This couldn't be simpler. It merely sets some variables 
        //  in the scope. By doing so, the binding in the relevant
        //  variables will allow the video to play and the title 
        //  to update.
        $scope.playVideo = function (element) {
            $scope.currentVideo  = element.guid;
            $scope.currentTitle  = element.title;
        };
    
        // This is the core. It makes the AJAX request to the 
        // LiveStream API so that it can get the JSON data back
        $scope.makeRequest = function (year) {
    
            // Calculating the current year and the year selected.
            // Their difference offers an offset which effectively 
            // is the offset of the array stored in $scope.playlists
            var thisYear    = $scope.currentYear();
            var diff        = thisYear - year;
    
            var objData = $scope.playlists[diff];
    
            // Just in case something was passed that is not valid
            if (objData.guid)
            {
                var reqData = $resource(
                    "http://livestream_url/2.0/:action",
                    {
                        action:'listclips.json', 
                        id:objData.guid,
                        query: {isArray: true},
                        maxresults:"500",
                        callback:'JSON_CALLBACK'
                    },
                    {get:{method:'JSONP'}}
                );
    
                // Set the year and get the data
                $scope.year    = year;
                $scope.listData = reqData.get();
           }
        };
    
        // This is the first load - load the current year
        $scope.makeRequest($scope.currentYear());
    
    });
    

    Now moving into the HTML side of things:

    <div id="playerContainer" style='text-align:center;'>
        <p ng-cloak>
            {{currentTitle}}
        </p>
        <iframe 
            width="560" 
            height="340" 
            src="http://ls_url?clip={{currentVideo}}&params" 
            style="border:0;outline:0" 
            frameborder="0" 
            scrolling="no">
        </iframe>
    </div>
    
    <br />
    <div>
        <ul class="nav nav-tabs">
            <li ng-repeat="playlist in playlists" 
                   ng-class="{true:'active',false:''}[year == playlist.year]">
                <a ng-click="makeRequest(playlist.year)">{{playlist.year}}</a>
            </li>
        </ul>
        <table class="table table-bordered" style="width: 100%;">
            <thead>
                <tr>
                    <th>Date/Title</th>
                    <th>Audio</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-cloak ng-repeat="video in listData.channel.item">
                    <td ng-click="playVideo(video)">{{video.title}}</td>
                    <td>
                        <span ng-show="video.description">
                            <a href="{{video.description}}" title="Download Audio">
                                <i class="icon-download-alt"></i>
                            </a>
                        </span>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
    

    That is all the HTML I had to change. The full HTML file is 100 lines and 50 for the AngularJS related javascript, I can safely say that I had a 50% reduction in code offering the same functionality - and if I might say so, it is much much cleaner.

    The final page looks something like this:

    Pointers

    <div id="playerContainer" style='text-align:center;'>
        <p ng-cloak>
        {{currentTitle}}
        </p>
        <iframe 
            width="560" 
            height="340" 
            src="http://ls_url?clip={{currentVideo}}&params" 
            style="border:0;outline:0" 
            frameborder="0" 
            scrolling="no">
        </iframe>
    </div>
    

    This block displays the video player and due to the variable binding that AngularJS offers, the minute those variables change, the video is ready to be played.

    <ul class="nav nav-tabs">
        <li ng-repeat="playlist in playlists" 
               ng-class="{true:'active',false:''}[year == playlist.year]">
            <a ng-click="makeRequest(playlist.year)">{{playlist.year}}</a>
        </li>
    </ul>
    

    This block shows the tabs depicting each playlist. In our case these are years. ng-repeat does all the hard work, printing the data that is defined in our JS file. The ng-class is there to change the class of the tab to "active" when the tab is clicked/selected. The ng-click initiates a request through makeRequest, a function defined in our javascript file (see above).

    <tbody>
        <tr ng-cloak ng-repeat="video in listData.channel.item">
            <td ng-click="playVideo(video)">{{video.title}}</td>
            <td>
                <span ng-show="video.description">
                    <a href="{{video.description}}" 
                       title="Download Audio">
                        <i class="icon-download-alt"></i>
                    </a>
                </span>
            </td>
        </tr>
    </tbody>
    

    Finally the data is displayed on screen. ng-cloak makes sure that the content is displayed only when the data is there (otherwise browsers might show something like {{video.description}} which is not nice from a UI perspective). ng-repeat loops through the data and "prints" the table.

    The description of the video is used as a storage for the URL that will point to the MP3 audio file so as the users can download it. Therefore I use ng-show to show the link, if it exists.

    Conclusion

    This whole project was no more than 30 minutes, which included the time I had to research and experiment a bit with the LiveStream API. This is a flexible design, with much much cleaner code (and a lot less of it). When the admin needs to add a new playlist (aka year), all they have to do is open the JS file and type a new element in the $scope.playlists array. The application will take care of the rest automatically.

    I cannot think of doing this with less lines of code than this.

    If you haven't heard of AngularJS or used it, I would highly encourage you to give it a shot. Great project, awesome support and a very very responsive, helpful and polite community.

  • 2012-07-12 12:00:00

    Building a web app with PhalconPHP and AngularJS Part II

    This is Part II of a series of posts on building an application using Phalcon and AngularJS. Part I is located here.

    Preface

    I have recently discovered Phalcon and I was impressed with its speed and ease of use. At the time of this writing, PhalconPHP is at version 0.4.2, with some serious redesign coming down the line on 0.5.x.

    Phalcon takes a different approach than any other PHP framework (see Zend, Symfony, CakePHP etc.). It is written in C and compiled as a module which is then loaded on your web server. Effectively the whole framework is in memory for you to use, without needing to access the file system so that you can include a file here or a file there.

    Advantages

    The core advantage of this approach is speed. The framework is in memory, ready to deliver its functionality, so your application is now only concerned about its files and not the framework itself. Once a framework is mature enough for usage, its files don't change that much. Yet for any of the traditional frameworks, PHP needs to scan the files, load them and the interpret them. This in effect has a serious impact on performance, especially for large projects.

    Another advantage is that since the framework is a module on your web server, you don't need to upload library files to each and every application you install on your host.

    Finally, you can mix and match whatever you need, using any of the components as 'glue' components rather than the whole framework. Most of the major frameworks also use this methodology for most of their components, however performance still is an issue. Additionally, in the case of any other framework, one might need to upload a very complicated and deep file structure on their web server so as to take advantage of one component to be used in an application.

    Disadvantages

    Support and bug tracing are the two weaknesses of Phalcon. By support I do not mean support from the developers. On the contrary, the developers are doing a great job listening to the relatively young community, and issuing fixes. However, as with any framework, if you find a bug, you will try to trace the code back to each component in an effort to find a solution to your problem. When developing an application and have access to the source files (the library PHP files like Zend Framework has), not only you can learn from those implementations, but you can quickly fix something that might be broken and continue working. With Phalcon you will need to wait until the next version is released, unless you are fluent in C and play around with the source code. For most PHP programmers (like myself), the process will be report the bug and wait for the fix.

    Since the framework is a module on your web server, you will need to be careful on upgrades. If your applications do not take advantage of the latest functionality the framework offers, you might fix something in one application, while breaking something in another. You cannot mix and match versions of Phalcon per application.

    Consideration

    Phalcon is very young as a framework. It does have a lot of power, but there are a lot of things still missing (for instance relationships between models and a query builder). In time these pieces will be implemented and the framework will grow stronger :)

    Implementation

    I downloaded the INVO sample application and set it up on my web browser. Using that as a starting point, I started modifying it to fit my needs. I also set up the PhalconPHP developer tools and PHPStorm support.

    For this application, I needed a table to store information about every podcast episode, a table to store all players and a table to store the users (namely Aaron, Josh and John). The Awards table would be the one that would store all the information regarding the game balls and kick in the balls awards.

    Models

    Once those were in place I started building my models and relevant controllers/views. Setting a model up was really easy. I would create the table in my database and then run

    phalcon create-model --table-name episodes
    

    and my model would be ready for me to use (example below for Episodes).

    class Episodes extends Phalcon_Model_Base 
    {
        public $id;
        public $number;
        public $summary;
        public $airDate;
        public $outcome;
        public $createdAt;
        public $createdAtUserId;
        public $lastUpdate;
        public $lastUpdateUserId;
    }
    

    After a while I decided I wanted to keep a track on who created a record and when, and who last updated a record and when for certain tables. After some refactoring I created my own model class that would give me the functionality I needed, and extended that class in relevant models.

    My custom class (that would take care of the createdAt, createdAtUserId, lastUpdated, lastUpdatedUserId fields) also took advantage of the beforeSave hook to ensure that these fields were transparently updated. The find and findFirst static functions are used throughout the models and there is no reason to repeat them in each model, so they end up in this custom class. (Comments removed to preserve space)

    use NDN_Session as Session;
    
    class NDN_Model extends Phalcon_Model_Base
    {
        public $createdAt;
        public $createdAtUserId;
        public $lastUpdate;
        public $lastUpdateUserId;
    
        public function beforeSave()
        {
            if (empty($this->createdAtUserId)) {
                $auth     = Session::get('auth');
                $datetime = date('Y-m-d H:i:s');
    
                $this->createdAt        = $datetime;
                $this->createdAtUserId  = (int) $auth['id'];
            }
        }
    
        static public function find($parameters = array())
        {
            return parent::find($parameters);
        }
    
        static public function findFirst($parameters = array())
        {
            return parent::findFirst($parameters);
        }
    }
    
    Session

    Although Phalcon provides a flash messenger utility, I had an issue with using the _forward function on a controller, after an action (say Add or Edit) was completed. Effectively the data would not refresh on screen. To combat that I used _redirect. However, all the messages that I had in the flash messenger (Phalcon_Flash) would disappear. An easy solution was to extend the Phalcon_Session and create two new functions setFlash and getFlash. The setFlash is called whenever I want to set a message for the user to see. The function stores the message in a session variable. Before the controller is dispatched, the getFlash is called to return any messages waiting to be displayed, and after that the messages are cleared from the session and displayed on screen.

    class NDN_Session extends Phalcon_Session
    {
        public static function setFlash($class, $message, $css)
        {
            $data = array(
                'class'   => $class,
                'message' => $message,
                'css'     => $css,
            );
            self::set('flash', $data);
        }
    
        public static function getFlash()
        {
            $data = self::get('flash');
            if (is_array($data)) {
                self::remove('flash');
                return $data;
            } else {
                return null;
            }
        }
    }
    
    Breadcrumbs

    I wanted to show breadcrumbs to the user, as a way to easily navigate throughout the application. To do so, I created my own Breadcrumbs class which holds an array of areas that the user is in. The class has a generate function, which returns back a JSON string. This is to be parsed by AngularJS so as to display the breadcrumbs.

    Controllers

    I created my controllers using the Phalcon Developer Tools. Whether you use the webtools or the command line makes no difference. The skeleton of the controller is generated for you to use.

    Based on the flash messenger and _redirect that I mentioned in the previous section, I had to extend the base controller, so as to add functionality that would allow me to show messages on screen after a redirect. Other reasons for this new class were to allow for a prefix on each page title, generate breadcrumbs and menus.

    use Phalcon_Tag as Tag;
    
    use Phalcon_Flash as Flash;
    use NDN_Session as Session;
    
    class NDN_Controller extends Phalcon_Controller 
    {
        protected $_bc = null;
        public function initialize()
        {
            Tag::prependTitle('HHF G&KB Awards | ');
            $this->_bc = new NDN_Breadcrumbs();
        }
    
        public function beforeDispatch()
        {
            $message = Session::getFlash();
            if (is_array($message)) {
                Flash::$message['class'](
                    $message['message'], $message['css']
                );
            }
            $this->view->setVar('breadcrumbs', $this->_bc->generate());
        }
    
        protected function _constructMenu($controller)
        {
            $commonMenu = array(
                'index'      => 'Home', 
                'awards'     => 'Awards', 
                'players'    => 'Players', 
                'episodes'   => 'Episodes', 
                'about'      => 'About', 
                'contact'    => 'Contact Us', 
            ); 
            $auth = Session::get('auth'); 
    
            $class  = get_class($controller); 
            $class  = str_replace('Controller', '', $class); 
            $active         = strtolower($class); 
            $sessionCaption = ($auth) ? 'Log Out'         : 'Log In'; 
            $sessionAction  = ($auth) ? '/session/logout' : '/session/index'; 
    
            $leftMenu = array(); 
            foreach ($commonMenu as $link => $text) { 
                $isActive   = (bool) ($active == $link); 
                $newLink  = ('index' == $link) ? '/' : '/' . $link; 
                $leftMenu[] = array( 
                    'active' => $isActive, 
                    'link'   => $newLink, 
                    'text'   => $text, 
                ); 
            } 
    
            $menu = new StdClass(); 
            $menu->current = $active; 
            $menu->left    = $leftMenu; 
    
            if ($auth != false) { 
                $sessionCaption .= ' ' . $auth['name']; 
            } 
    
            $menu->rightLink = $sessionAction; 
            $menu->rightText = $sessionCaption; 
    
            return json_encode($menu); 
        } 
    }
    

    Each controller would extend my base controller. In the initialize function:

    • the page title is set,
    • the breadcrumbs are added (and generated later on in the beforeDispatch of the base controller),
    • the menu is generated and passed to the view for AngularJS to process,
    • additional variables would be generated for displaying elements based on whether a user is logged in or not.
    Views

    Creating the views was really easy. I already had the structure ready from the sample application (INVO) and with the help of Bootstrap CSS, I was done in no time. The views inherit from a base view (index.phtml) located at the root of the views folder. That view holds the skeleton of the web page and content is injected accordingly based on each controller (and its view).

    In that file I added the relevant variables that will be used by AngularJS as well as variables that hold conditional elements (i.e. elements that appear when a user is logged in).

    More on the views in the next installment of these series.

    Conclusion

    With all that the application was ready as far as the main structure was concerned. Tying everything with AngularJS was the next step, which will be covered in part III of this How-To.

    The whole application, from start to finish, took less than 4 hours to develop. This included breaks, reading the manual and making design decisions based on my ever changing requirements.

    References

  • 2012-07-10 12:00:00

    Building a web app with PhalconPHP and AngularJS Part I

    There are ample frameworks on the Internet, most free, that a programmer can use to build a web application. Two of these frameworks are PhalconPHP and AngularJS.

    I decided to use those two frameworks and build a simple application which will keep track of the Game Balls and Kick in the Balls awards of Harry Hog Football.

    Harry Hog Football is a podcast that has been going strong since 2005, created by Redskins fans for Redskins Fans. (for those that do not know, Washington Redskins is a team on the National Football League in the USA).

    Every week during the regular season, Aaron, Josh and John create a podcast, where they discuss the recent game, the injuries, the cuts, the new signings and they offer their Game Balls to the best players of the week as well as the Kick in the Balls awards for the ones that (according to the podcasters) 'suck'.

    I therefore created an application to record all those game balls and kick in the balls awards, so that we can all see, who is the most valuable player and who is the least valuable player for the Redskins throughout the years (the term valuable is used loosely here).

    As a starting point I used the INVO application that PhalconPHP showcases as an easy application to get you started. I modified it significantly to address my needs, refactoring classes as much as possible to get the least amount of code with maximum usability.

    After building the application, I listened to all the episodes I could find, and entered the game balls and kick in the balls in the database. The models use the PhalconModelBase class to handle data, while the rest of the application is handled by the Phalcon_Controller (and view of course).

    The data transfer between the application and the relevant sections is primarily handled by AngularJS, which is dominant in the view layer. AngularJS controllers handle menu creation, breadcrumbx as well as displaying results on screen.

    Twitter's Bootstrap CSS> is used to put the final touches for the application.

    In subsequent posts I will explain each layer in turn, starting with PhalconPHP and continuing with AngularJS.

    This of course is by no means the perfect implementation. It has been a fun project for me, working on it on my own free time. You are more than welcome to fork the project and make any modifications you need. For those that are interested in getting straight to the code, it is available on Github here.

    NOTE: The Github repository contains code that works with nginx. If you are having problems with Apache, check the public/index.php - there is a note there for nginx (probably will need to remove it)

    References

  • 2009-12-01 14:00:00

    The world with Angular - Part III

    Continued from Part II

    Presentation of Data

    Presenting data with <angular/> is really easy. All we need to do is to tell <angular/> how we want the data to be presented and where.

    From Part II you have seen that I have declared an entity called Incident. Also in the <body> tag I have initialized the <angular/> engine, requested all() the Incident objects and stored them in the incidents variable.

    <body ng-entity="incident=Incident" ng-init="incidents=Incident.all()">
    

    I am creating a HTML table where I will display all the records. The header of the table is as follows:

    <tr>
      <th> </th>
      <th>Date</th>
      <th>Shift Start/End</th>
      <th>Animal Code</th>
      <th>Situation Code</th>
      <th>Resolution Code</th>
      <th>City</th>
      <th>State</th>
      <th>Description</th>
    </tr>
    

    Now I need to show the data on screen. I can reference the variables that I have used earlier in my input elements i.e. incident.shiftstart, incident.shiftend, incident.animalcode etc. The variables represent the fields of each record so incident is the document/record if you like and animalcode is the field. If I add them just like that, <angular/> will correctly show me data but only one record since it will not know otherwise. As usual I will need a loop for this. The loop will work directly on the <tr> definitions to give me a row per record.

    <tr ng-repeat="record in incidents">
      <td class="centercell" style="white-space:nowrap;">
        E D
      </td>
      <td class="centercell">{{record.shiftdate}}</td>
      <td class="centercell">{{record.shiftstart}}<br />{{record.shiftend}}</td>
      <td class="centercell">{{record.animalcode}}</td>
      <td class="centercell">{{record.situationcode}}</td>
      <td class="centercell">{{record.resolutioncode}}</td>
      <td class="centercell">{{record.city}}</td>
      <td class="centercell">{{record.state}}</td>
      <td>{{record.details}}</td>
    </tr>
    

    I am using the ng-repeat directive to repeat that block of code. Since I am using it in a <tr> the ng-repeat will match the closing tag for that element i.e. </tr>. ng-repeat repeats that block of code for every recordin incidents. incidentsis the variable I have stored all the results earlier from the <body> declaration. Each piece of information is stored in the database and referenced the same way that it was stored. Note that I have used the variable name recordto access the loop elements but I could have used anything I liked. recordrefers to one object of the incidentsvariable at a time.

    The full block of code is below:

    <table style='width:100%'>
    <tr>
      <th> </th>
      <th>Date</th>
      <th>Shift Start/End</th>
      <th>Animal Code</th>
      <th>Situation Code</th>
      <th>Resolution Code</th>
      <th>City</th>
      <th>State</th>
      <th>Description</th>
    </tr>
    <tr ng-repeat="record in incidents">
      <td class="centercell" style="white-space:nowrap;">
        E D
      </td>
      <td class="centercell">{{record.shiftdate}}</td>
      <td class="centercell">{{record.shiftstart}}<br />{{record.shiftend}}</td>
      <td class="centercell">{{record.animalcode}}</td>
      <td class="centercell">{{record.situationcode}}</td>
      <td class="centercell">{{record.resolutioncode}}</td>
      <td class="centercell">{{record.city}}</td>
      <td class="centercell">{{record.state}}</td>
      <td>{{record.details}}</td>
    </tr>
    </table>
    

    So now I have a basic application that allows me to store and display data. If you have tried this code so far and run it, you will see that the data is entered immediately on the screen and there are no page refreshes which significantly enhances the user experience.

    You might have noticed that I have "E D" in the first column of every row of the HTML table. These are for Edit and Delete.

    Editing Data

    Each record has a unique identifier as I showed you earlier when adding records. In order for the "E" to link to the respective record, I need to use that id. The hyperlink around the "E" becomes then:

    <a href="#incident={{record.id}}">E</a>
    

    Every document/record has an id field. I can it as usual with the double brackets and referencing the incident loop variable (inside the <tr> loop). Note that since I have decided to name my entity incident, I am referencing each record with that parameter in the URL. If my entity's name was different, say testentity, then the URL would have changed to:

    <a href="#testentity={{record.id}}">E</a>
    

    html

    Deleting Data

    To delete data, instead of referencing the unique identifier of each record, I will use the record variable and the $delete() method on it. Note that the recordvariable is the one that allows us to have access to every object in the incidentsvariable and it is used in the display data loop.:

    <a href="#" ng-action="record.$delete()">D</a>
    
    Searching Data

    The initial design never catered for emptying the database in regular intervals. Therefore a search function is in order to ensure quick retrieval of information. As you will see it is really easy and it does not need any <form> elements or anything else.

    The search box is just an <input> element with a specific name (which can be whatever you like)

    [ Search: <input name="wrlfilter" /> ]
    

    This piece of code appears above the table that displays data in my example. I need to make one more change to ensure that the data is filtered. The change is in the <tr> statement where I run the loop to display the data. So the block of code:

    <tr ng-repeat="record in incidents">
    

    becomes

    <pre class="brush:html"><tr ng-repeat="record in incidents.$filter(wrlfilter)">
    

    As you can see I am using the $filter() method and the parameter passed is the name of the input box that I have defined earlier. This way whatever I type in the search box, <angular/> will try to match it with the currently displayed data and filter accordingly, thus giving me the search functionality that I want.

    Sorting data

    Another easy task in <angular/> is sorting. Since I already have a table that I present data with, I am going to use that and its table headings to allow my user to sort. Also I am going to have a default sorting option of Shift Date descending and Shift Start descending.

    In general sorting is done by the $orderBy(), $orderByToggle() and $orderByDirection() methods. The + or - prefixing the name of a field passed defines ascending or descending order. To create a compound sorting key with multiple fields I need to enclose the field names in quotes and separate them with commas.

    First of all I need to initialize the sorting mechanism. The best place for that is the table that displays the data. Therefore I get:

    <table style='width:100%' ng-init="wrlorder=['-shiftdate','-shiftstart']">
    

    This statement initializes the wrlorder variable to contain a shiftdate and a shiftstart field in descending order. Note that the shiftdate and shiftstart are the same names of the fields that I have used throughout this article.

    I save my changes and reload the page but there is no sorting. I actually haven't told <angular/> what data to sort. Since the data that I want to sort are in the table, I will enhance my $filter() method with an $orderBy(). This is the statement that we have worked with earlier.

    <tr ng-repeat="record in incidents.$filter(wrlfilter).$orderBy(wrlorder)">
    

    Save and refresh and voila! The results are sorted by Shift Date descending and Shift Start descending. If I enter a new record, it will be displayed on screen sorted in the correct position. Note that the statement above allows for a combination of methods to be run against the incidents variable (filter and sort).

    For the purposes of this exercise and for debugging, I have also added the wrlorder variable next to the search box so that I know what my sort fields are.

    [ Search: <input name="wrlfilter" /> ] - [ Order: {{wrlorder}} ]
    

    Refreshing the page shows me now

    - [ Order: -shiftdate, -shiftstart ]
    

    I am nearly there. All I need to do now is to make the table headings clickable so that the data is sorted anyway I want to. I will use the $orderByToggle() and $orderByDirection() methods on the wrlorder variable. My table heading becomes:

    <th> </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('shiftdate')" 
           ng-class="order.$orderByDirection('shiftdate')">Date</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('shiftstart')" 
           ng-class="order.$orderByDirection('shiftstart')">Shift Start</a>/
        <a href="" 
           ng-action="order.$orderByToggle('shiftend')" 
           ng-class="order.$orderByDirection('shiftend')">End</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('animalcode')" 
           ng-class="order.$orderByDirection('animalcode')">Animal Code</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('situationcode')" 
           ng-class="order.$orderByDirection('situationcode')">Situation Code</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('resolutioncode')" 
           ng-class="order.$orderByDirection('resolutioncode')">Resolution Code</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('city')" 
           ng-class="order.$orderByDirection('city')">City</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('state')" 
           ng-class="order.$orderByDirection('state')">State</a>
    </th>
    <th>
        <a href="" 
           ng-action="order.$orderByToggle('details')" 
           ng-class="order.$orderByDirection('details')">Description</a>
    </th>
    

    The ng-action allows for the interchange in sorting order (ascending/descending) while the ng-class shows a nice arrow next to the header name, indicating the current sort order.

    Refreshing the page shows me the final product. I can now sort in any way I like and the sorting is compounded.

    Widgets

    <angular/> has a lot of widgets that can be used as validators but also as means to enhance the user experience. One of them is the DatePicker. I am going to use it to collect data in the shiftdate field. So the:

    <input name="incident.shiftdate" />
    

    becomes:

    <input name="incident.shiftdate" ng-widget="datepicker" size="8" />
    

    and that's it. Now when I click on the input box or when it gets focus, a nice dropdown calendar appears that allows me to select the date.

    Final thoughts

    The example above is not the final product for the WRL. There are some things missing, such as clearing up the order, enhancing the search, expanding/collapsing the add new record etc. This article is not meant as the final solution but more as a guide on what is feasible.

    <angular/> is definitely a new way of looking at web programming. It is fast, agile and easy to learn. <angular/> provides the hobbyist a tool that they can use to create an online application that will suit their needs without complex installations, expensive hosting companies, RDBMS management etc. I can also see experienced programmers using it to address a quick fix or a very urgent requirement that demands RAD.

    I encourage you to visit getangular.com} and give <angular/> a try. I am sure you will not be disappointed.

  • 2009-12-01 13:00:00

    The world with Angular - Part II

    Continued from Part I

    The Wildlife Rescue League application

    Design

    For those that do not know, the Wildlife Rescue League

    is a non-profit organization providing care for sick, injured and orphaned wildlife in order to return them to the wild. Our licensed rehabilitators, located throughout Virginia and suburban Maryland, work with animal shelters, humane societies, wildlife groups, nature centers and veterinary hospitals to provide care to creatures in need. WRL operates a wildlife hotline in the Northern Virginia and surrounding areas to assist the public in obtaining information and assistance in locating a wildlife rehabilitator. WRL is also committed to educating the public about the natural history of native wildlife, coexisting with it and preventing the need for wildlife rehabilitation. We can provide brochures, educational material and educational programs to suit your needs.

    The WRL hotline records all phone calls that the volunteers answer and organizes that data in a manner that would help the organization in the future (anticipated call volume etc.) In the past the data collection method was a simple sheet of paper that was mailed to one volunteer, who then had to decode everything and create the relevant spreadsheet for data analysis. Later on this model evolved into a spreadsheet which was copied and distributed to the volunteers. Again the data had to be collected (via email now) and merged for a meaningful analysis to take place.

    I have therefore created a simple application using <angular/> to allow the volunteers to enter their data in a centralized repository. I am going to explain in detail how I utilized <angular/> to perform this task, needing only a few hours (mostly spent in cosmetic changes) until I had a working copy of what I wanted.

    This blog post will be missing the authentication mechanism and a couple of other bits and pieces. I am hoping to show you a different way or programming and encourage you to explore <angular/> and its power.

    Thinking about my application I will need to store the data. I will need the following fields:

    Date
    Shift Start
    Shift End
    Animal Code
    Situation Code
    Resolution Code
    City
    State
    Description of incident
    
    Creating the database

    The data needs to be stored in the database. So logging into my account in getangular.com (do so if you haven't done this already) I created my library and my new database (both called testwrl) and I am set. Everything else will be controlled in the HTML document.

    Initial HTML file

    My HTML file is very simple.

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
    <meta http-equiv="Content-Type"
          content="text/html; charset=utf-8" />
    <meta name="robots" content="index, follow" />
    <style type="text/css">
    body {
        font-family:Verdana,Arial,Helvetica;
        font-size:10pt;
    }
    th,td {
        text-alignment:center;
    }
    th, .inputBox {
        background:#000066;
        color:#00FF00;
        font-weight:bold;
    }
    .incident {
        border:1px solid #000000;
    }
    .incidentheader {
        padding:5px;
        border:1px solid #000000;
        font-weight:bold;
        background:#000099;
        color:#00EE00;
    }
    .centercell {
        text-align:center;
    }
    img {
        border:none;
    }
    </style>
    <script type="text/javascript"
            src="http://testwrl.getangular.com/angular-1.0a.js#database=testwrl"></script>
    </head>
    <body>
    </body>
    </html>
    

    Note the JavaScript line at the bottom part of the snippet. It references a subdomain of getangular.com (testwrl.getangular.com) as well as the database I am using to store data (database=testwrl).

    Creating the HTML input elements

    I need to create a form to store the data. The programming is done with HTML <input> elements. Once I have everything mapped nicely on screen I need to bind them in the database. First of all I need to describe what I want to work with. I will use the ng-entity attribute in my body element. The ng-entity uses the expression [instance=]Entity[:template]. Effectively Entity is the name of the entity that will be stored in my database under that name.

    Therefore in my HTML file I need to change the body element:

    <body ng-entity="incident=Incident" ng-init="incidents=Incident.all()">
    

    So I am storing the Incident in the database. incident is the document of this entity (Incident) that is stored under the name of that instance.

    The ng-init declaration in the body element assigns all the records of the entity Incident (Incident.all()) to a variable (incidents). The name of the variable is arbitrary - you can choose whatever you like.

    Since incident is the document, I need to reference all my elements (the input ones) towards that. The way this is done is via the name attribute of each of the HTML elements. So for instance for the Date that I want to store, my HTML input declaration changes to:

    <input name="incident.shiftdate" />
    

    I repeat the same methodology and I name my elements incident.shiftstart, incident.shiftend, incident.animalcode etc.

    Notable is the fact that I haven't used the <form> element at all. I am going to add a Save button at the end of this HTML block which is nothing more than a submit element:

    <input type="submit" value="Save" />
    

    <angular/> takes care of all the data posting from the browser to the server so I do not need any <form> elements and POST control.

    The HTML script as is right now is below:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
    <meta http-equiv="Content-Type"
             content="text/html; charset=utf-8" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="-1" />
    <meta http-equiv="Cache-Control" content="no-cache" />
    <meta name="description"
             content="Wildlife Rescue League Hotline" />
    <meta name="keywords"
             content="WRL, wildlife, hotline, volunteer work" />
    <meta name="robots" content="index, follow" />
    <style type="text/css">
    body {
      font-family:Verdana,Arial,Helvetica;
      font-size:10pt;
    }
    th,td {
      text-alignment:center;
    }
    th, .inputBox {
      background:#000066;
      color:#00FF00;
      font-weight:bold;
    }
    .incident {
      border:1px solid #000000;
    }
    .incidentheader {
      padding:5px;
      border:1px solid #000000;
      font-weight:bold;
      background:#000099;
      color:#00EE00;
    }
    .centercell {
      text-align:center;
    }
    img {
      border:none;
    }
    </style>
    <script type="text/javascript"
      src="http://testwrl.getangular.com/angular-1.0a.js#database=testwrl"></script>
    </head>
    <body ng-entity="incident=Incident" ng-init="incidents=Incident.all()">
      <span style='float:right;font-size:10px;'>
        Powered by <a href='http://www.getangular.com'>`<angular/>`</a>
      </span>
      <br />
      <div class="incident">
        <div class="incidentheader">WRL Hotline Incidents</div>
        [ <a href="#">New Incident</a> ]
        <br />
        <table style='width:100%'>
        <tr>
          <td style='text-align:right;'>
            <label>Date</label>
          </td>
          <td>
            <input name="incident.shiftdate" />
          </td>
          <td style='text-align:right;'>
            <label>Shift Start/End</label>
          </td>
          <td>
            <select name="incident.shiftstart">
              <option value=""></option>
              <option value="08:00 AM">08:00 AM</option>
              <option value="08:30 AM">08:30 AM</option>
              <option value="09:00 AM">09:00 AM</option>
              <option value="09:30 AM">09:30 AM</option>
              <option value="10:00 AM">10:00 AM</option>
              <option value="10:30 AM">10:30 AM</option>
              <option value="11:00 AM">11:00 AM</option>
              <option value="11:30 AM">11:30 AM</option>
              <option value="12:00 PM">12:00 PM</option>
              <option value="12:30 PM">12:30 PM</option>
              <option value="13:00 PM">01:00 PM</option>
              <option value="13:30 PM">01:30 PM</option>
              <option value="14:00 PM">02:00 PM</option>
              <option value="14:30 PM">02:30 PM</option>
              <option value="15:00 PM">03:00 PM</option>
              <option value="15:30 PM">03:30 PM</option>
              <option value="16:00 PM">04:00 PM</option>
              <option value="16:30 PM">04:30 PM</option>
              <option value="17:00 PM">05:00 PM</option>
              <option value="17:30 PM">05:30 PM</option>
              <option value="18:00 PM">06:00 PM</option>
              <option value="18:30 PM">06:30 PM</option>
              <option value="19:00 PM">07:00 PM</option>
              <option value="19:30 PM">07:30 PM</option>
              <option value="20:00 PM">08:00 PM</option>
              <option value="20:30 PM">08:30 PM</option>
            </select>
             /
            <select name="incident.shiftend">
              <option value=""></option>
              <option value="08:00 AM">08:00 AM</option>
              <option value="08:30 AM">08:30 AM</option>
              <option value="09:00 AM">09:00 AM</option>
              <option value="09:30 AM">09:30 AM</option>
              <option value="10:00 AM">10:00 AM</option>
              <option value="10:30 AM">10:30 AM</option>
              <option value="11:00 AM">11:00 AM</option>
              <option value="11:30 AM">11:30 AM</option>
              <option value="12:00 PM">12:00 PM</option>
              <option value="12:30 PM">12:30 PM</option>
              <option value="13:00 PM">01:00 PM</option>
              <option value="13:30 PM">01:30 PM</option>
              <option value="14:00 PM">02:00 PM</option>
              <option value="14:30 PM">02:30 PM</option>
              <option value="15:00 PM">03:00 PM</option>
              <option value="15:30 PM">03:30 PM</option>
              <option value="16:00 PM">04:00 PM</option>
              <option value="16:30 PM">04:30 PM</option>
              <option value="17:00 PM">05:00 PM</option>
              <option value="17:30 PM">05:30 PM</option>
              <option value="18:00 PM">06:00 PM</option>
              <option value="18:30 PM">06:30 PM</option>
              <option value="19:00 PM">07:00 PM</option>
              <option value="19:30 PM">07:30 PM</option>
              <option value="20:00 PM">08:00 PM</option>
              <option value="20:30 PM">08:30 PM</option>
            </select>
          </td>
        </tr>
    
        <tr>
          <td style='width:20%;text-align:right;'>
            <label>Animal Code</label>
          </td>
          <td>
            <select name="incident.animalcode">
              <option value=""></option>
              <option value="B">Songbird</option>
              <option value="C">Corvine</option>
              <option value="M">Mammal</option>
              <option value="R">Raptor</option>
              <option value="RE">Reptile</option>
              <option value="U">Unknown</option>
              <option value="W">Waterfowl</option>
    
              <option value="OTH">Other</option>
            </select>
          </td>
          <td style='text-align:right;'>
            <label>Situation Code</label>
          </td>
          <td>
            <select name="incident.situationcode">
              <option value=""></option>
              <option value="A">Attacked (details for attacker)</option>
              <option value="I">Injured</option>
              <option value="K">Killed</option>
              <option value="N">Nuisance (explain in details)</option>
              <option value="O">Orphaned</option>
              <option value="OTH">Other (explain in details)</option>
              <option value="U">Unknown</option>
            </select>
    
            <label>Resolution Code</label>
    
            <select name="incident.resolutioncode">
              <option value=""></option>
              <option value="D">Died</option>
              <option value="GA">Gave advice only</option>
              <option value="LM">Left Message</option>
              <option value="OTH">Other</option>
              <option value="RR">Referred to Rehabber</option>
              <option value="RS">Referred to Shelter</option>
              <option value="RV">Referred to Vet</option>
              <option value="WCB">Watch and Call Back</option>
            </select>
          </td>
        </tr>
        <tr>
    
          <td style='width:20%;text-align:right;'>
            <label>City</label>
          </td>
          <td>
            <input name="incident.city" type="text" />
          </td>
          <td style='text-align:right;'>
            <label>State</label>
          </td>
          <td>
            <select name="incident.state">
              <option value=""></option>
              <option value="MD">Maryland</option>
              <option value="VA">Virginia</option>
              <option value="DC">Washington DC</option>
            </select>
          </td>
        </tr>
        <tr>
          <td style='width:20%;text-align:right;'>
            <label>Description</label>
          </td>
          <td colspan="3">
            <textarea name="incident.details" rows="5" cols="80"></textarea>
          </td>
        </tr>
        </table>
        <input type="submit" value="Save" class="inputBox" />
      </div>
    
    Adding data

    The entry screen is complete and if you type some data and click the Save button, you will store that data in the database. You will notice that this is the case since your URL will change to something like

    .../#incident=abcdefg7896456464532132135498a
    

    That is effectively the id of that record. If you delete that #incident..... and reload the page, you will be presented with an empty form where you can add more data. Clicking the Save button will save that record and you will notice that the new id is different than the previous one.

    I really don't want to have to delete the #incident.... from my address bar every time I want to add a new record. I am sure that the users of the WRL will not like it either. For that, I have added a link at the top of the screen which is already in the script that I have a few lines up. The line:

    [ <a href="#">New Incident</a> ]
    

    is the one that will reload the page allowing me to add a new record whenever I click that link.

    Continued in Part III

  • 2009-12-01 12:00:00

    The world with Angular - Part I

    If you haven't heard about <angular/>, this article is intended to give you a brief overview of the features and functionality that it exposes.

    <angular/> is a declarative programming language that provides the programmer with a wealth of tools using a simplistic model. <angular/> lives in the HTML file and references all the necessary libraries and tools using a single JavaScript file. This approach removes the need of complex scripts, installations of development/production environments (like AMP, Microsoft equivalent or other) and makes upgrading a breeze since the developer doesn't have to deal with it - it's all managed in the cloud. Development IDEs are rendered obsolete and all the developer needs is Notepad/Textmate/gEdit or a similar text editor.

    Backed by the latest cloud technologies, <angular/> is built for scalability. It can handle virtually any task or load right off the box but should the developer need more resources, they can easily contact Brat Tech LLC. and additional arrangements can be made. Ruby on Rails and Amazon EC2 along with CouchDB are the core components that utilizes, making it highly scalable, reliable and extremely fast!

    According to their website (http://www.getangular.com/) <angular/> provides:

    • A managed database - manages data for you
    • HTML & CSS - Full control of the look and feel
    • Security - Authentication and permissions
    • Embeddable - In third-party sites such ad blogs and wikis
    • Declarative - No need to learn a new language
    • Rich Widgets - Date-picker, Barcodes, Charts, etc.

    All of the above (and much more available through the documentation make <angular/> a really good and wise choice for the starter in web programming, the hobbyist who wants some variety in his/her website, the knowledgeable web developer or the expert in web programming.

    So why am I excited about this? For starters it is a really easy language to learn. It is by no means intimidating to any user and people that always wanted to enhance their website with some 'scripting' magic, can do so now with with no installations of any software on their sites.

    To start you need to go and create an account on the website (http://www.getangular.com/). In there you can select your subscription level. There is a free subscription (the default) and two paid ones at 14.95 USD and 45.95 USD per month respectively. The different subscription levels provide more functionality (i.e. SSL support, versioning, more documents etc.)

    Continued in Part II