Skip to content

AngularJS: Example Deckster Directives

baileyreed39 edited this page Aug 15, 2016 · 8 revisions

Angular Directives for Deckster

A custom Angular directive will allow you to insert one piece of HTML into another via an HTML tag or attribute. See here for an explanation of Angular directives. This definition defines a custom directive called 'decksterDeck' and attaches it to a module called 'app,' which should be the module in your app where you wish to place the Deckster deck.

Deckster Deck

angular.module('app').directive('decksterDeck', function ($parse, $timeout) {

Then, within this function, you might want to define your defaults for Gridster, the jQuery plug-in that Deckster is based on. These defaults can be overridden as you configure each deck. See Gridster Documentation for additional options.

    var defaults = {
        gridsterOpts: {
            max_cols: 4, // max columns
            widget_margins: [10, 10], // x, y margins between each card
            widget_base_dimensions: ['auto', 250], // x, y dimensions of rows and columns. 'auto' will set width to be responsive to page size
           responsive_breakpoint: 850 // responsiveness of when cards change position when one card is dragged
        }
    };

Then define the directive itself:

    return {
        restrict: 'EA', // restricts directive to be usable via element tag and attribute
        replace: true, // 
        scope: { // sets two-way binding between parent and isolated scope for 'deck' and 'initialized' variables
            deck: '=',
            initialized: '='
        },

Preferably put this template into a separate HTML file and refer to using templateUrl instead of template.

        template: '<div class="deckster-deck-wrapper"><div class="deckster-deck" deck-options="deck"><deckster-card ng-repeat="cardOpts in deck.cards" card-options="cardOpts"></deckster-card></div></div>',

These controller functions will properly set up your Deckster deck:

       controller: function($scope) {
            $scope.deckInitialized = false;

            $scope.$on('deckster:resize', function () {
                if ($scope.deckster) {
                    $timeout(function () {
                        $scope.deckster.$gridster.recalculate_faux_grid();
                    });
                }
            });

            this.addCard = function (card, callback) {
                $scope.deckster.addCard(card, function (card) {
                    if (callback) callback(card);
                });
            };

            this.init = function (element, opts) {
                $scope.deckster = $(element).deckster(opts).data('deckster');
                $scope.deckInitialized = true;
            };
        },
        link: function (scope, element, attrs, ctrl) {
            var deckOptions = $.extend(true, {}, defaults, scope.deck);
            var $deckEl = $(element).find('.deckster-deck');

            scope.$watch('initialized', function(init) {
                if (init && !scope.deckInitialized) {
                    ctrl.init($deckEl, deckOptions);
                }
            });

            scope.$on('$destroy', function() {
                scope.deckster.destroy();
                scope.deckInitialized = false;
            });
        }
    };
})

Deckster Card

The instantiation of the Deckster Deck directive should call the Deckster Card directive using ng-repeat.

angular.module('app').directive('decksterCard', function ($parse, $q, $http, $timeout) {
        return {
            restrict: 'E',
            require: ['^decksterDeck', 'decksterCard'],
            controller: function ($scope, $compile) {

Within the controller, there are several default functions that you may define if you wish. None of these are completely necessary.

                // Default summaryContentHtml function
                this.getSummaryContent = function (card, cb) {
                  $timeout(function() {
                    cb($compile('<div></div>')($scope));
                  });
                };
                
                // Default detailsContentHtml function
                this.getDetailsContent = function (card, cb) {
                  $timeout(function() {
                    cb($compile('<div></div>')($scope));
                  });
                };

                // Default leftControlsHtml function
                this.getLeftControlsContent = function (card, cb) {
                    $timeout(function() {
                        cb($compile('<div></div>')($scope));
                    });
                };

                // Default rightControlsHtml function
                this.getRightControlsContent = function (card, cb) {
                    $timeout(function() {
                        cb($compile('<div></div>')($scope));
                    });
                };

                // Default centerControlsHtml function
                this.getCenterControlsContent = function (card, cb) {
                    $timeout(function() {
                        cb($compile('<div></div>')($scope));
                    });
                };

                this.onReload = function (card) {
                    console.log('card reloaded', card);
                };

                this.onResize = function (card) {
                    console.log('card resized', card);
                };

                this.onExpand = function (card)  {
                    console.log('card expanded', card);
                };

                this.scrollToCard = function () {
                    $scope.card.scrollToCard();
                };

                this.toggleCard = function () {
                    $scope.card.hidden ? $scope.card.showCard() : $scope.card.hideCard();
                };

This function will set up your defaults as well as create the card itself.

                this.setUpCard = function (cardOpts) {
                    if(!cardOpts.summaryViewType && !cardOpts.detailsViewType) {
                        cardOpts.summaryContentHtml = cardOpts.summaryContentHtml || this.getSummaryContent;
                        cardOpts.detailsContentHtml = cardOpts.detailsContentHtml || this.getDetailsContent;
                        cardOpts.onResize = cardOpts.onResize || this.onResize;
                        cardOpts.onReload = cardOpts.onReload || this.onReload;
                    }

                    cardOpts.showFooter = false;
                    cardOpts.leftControlsHtml = this.getLeftControlsContent;
                    cardOpts.rightControlsHtml = this.getRightControlsContent;
                    cardOpts.centerControlsHtml = this.getCenterControlsContent;

                    $scope.$on('deckster-card:scrollto-' + cardOpts.id, this.scrollToCard);
                    $scope.$on('deckster-card:toggle-' + cardOpts.id, this.toggleCard);

                    return cardOpts;
                };
            },

The link function will then set up the event listeners.

            link: function (scope, element, attrs, ctrls) {
                var deckCtrl = ctrls[0];
                var cardCtrl = ctrls[1];

                var cardOpts = $parse(attrs.cardOptions || {})(scope);

                scope.$watch('deckInitialized', function (initialized) {
                    if (initialized) {
                        deckCtrl.addCard(cardCtrl.setUpCard(cardOpts), function (card) {
                            scope.card = card;

                            // When the deck is resize resize this card as well
                            scope.$on('deckster:resize', function () {
                                // TODO code to resize cards
                            });

                            scope.$on('deckster:redraw', function () {
                                $timeout(function () {
                                    // TODO code to redraw cards
                                });
                            });
                        });
                    }
                });
            }
        };
    })

Deckster Popout

This directive should configure the Deckster popout functionality, if you wish you use that.

angular.module('app').directive('decksterPopout', ['$injector', '$compile', '$http', 'Deckster', function($injector, $compile, $http, Deckster) {
        return {
            restrict: 'E',
            link: function(scope, element) {
                var cardId, section;

                var $routeParams = $injector.get('$routeParams');
                cardId = $routeParams.id;
                section = $routeParams.section;


                var getSummaryTemplate = function(cardConfig, cb) {
                    // Not using the cardConfig here but you could use it to make request
                    $http.get('partials/testSummaryCard.html').success(function(html) {
                        if (cb) cb($compile(html)(scope));
                    });
                };

                var getDetailsTemplate = function(cardConfig, cb) {
                    // Not using the cardConfig here but you could use it to make request
                    $http.get('partials/testDetailsCard.html').success(function (html) {
                        if (cb) cb($compile(html)(scope));
                    });
                };

                // Get card config from server or angular constants using cardId
                var cardConfig =  {
                    title: 'Photos',
                    id: 'photoCard',
                    summaryContentHtml: getSummaryTemplate,
                    detailsContentHtml: getDetailsTemplate,
                    position: {
                        size_x: 1,
                        size_y: 1,
                        col: 1,
                        row: 1
                    }
                };

                Deckster.generatePopout(element, cardConfig, section);
            }
        };
    }]);

Clone this wiki locally