Search code examples
angularjsangularjs-directiveangularjs-scopewidgetdashboard

How to feed data to a schema inside an AngularJS directive


I am trying to write a custom directive that will "create" a dashboard widget including:

  1. Title (the name of the widget)
  2. Schema (the way the data should be modelled or shown, i.e. it could be the number of users registered - an number, or it could be a string displaying the day of the week with most users etc...)
  3. Data (the data to feed to the schema, a number, a string, or whatever is fetched from the db or datasource)

Here is how far I got with my directive:

app.directive('widget', function () {
return {
    restrict: 'E',
    scope: {
        title: '@',
        schema: '&',
        data: '&'
    },
    template: '<div>' +
        '<span class="widget-title">{{title}}</span>' +
        '<div schema="schema()" data="data()"></div>' +
        '</div>'
}})

and my html:

<widget title="Number of users" options="numberWidget" data="userData">
</widget>

I am stuck on how to "bind" the data to the schema, and where I should define the different schemas (or widget models). Maybe my approach is wrong, so a little direction would be much appreciated. I can provide more info if needed.

EDIT: Here is a CodePen i made of how far I am and what I thought I could achieve - maybe it can be (or should be) done differently.


Solution

  • Ok, so I decided to do it slightly differently to what I though it worked. Here is my solution:

    Directive: The directive is restricted to an Element and has an isolated scope which takes some arguments, the data with '&', which evaluates an expression (the expression is defined in the controller as a function shown below) and the classes and title with '@', which evaluate a string (these strings are defined in the HTML shown below)

    app.directive('widget', ['$log', function ($log) {
        return {
            restrict: 'E',
            scope: {
                data: '&',
                valueClass: '@',
                keyClass: '@',
                widgetTitle: '@',
                titleClass: '@'
            },
            template:
              '<div><span class="{{titleClass}}">{{widgetTitle}}</span></div>' +
              '<div><span class="{{valueClass}}">{{data().value}}</span></div>' +
              '<div><span class="{{keyClass}}"> {{data().key}}</span></div>',
            link: function (scope, element, attrs) {
                scope.$watch('data', function (newValue, oldValue) {
                    if (newValue !== oldValue) {
                        scope.data = newValue;
                        $log.info('updating data');
                    }
                }, true);
            }
        };
    }]);
    

    HTML: The HTML then initializes the 'widget' element created from the directive like so:

    <widget title-class="widget-title" 
            widget-title="Number of Users" 
            data="userData" 
            value-class="number-widget" 
            key-class="description">
    </widget>
    

    The data attribute calls a $scope variable from the controller

    Controller: The controller simply fetches the data required in this example, it is a dummy object defined on $scope called $scope.userData:

    app.controller('dashboardCtrl', [
        '$scope', function ($scope) {
    
            $scope.userData = {
                key: 'Total Users',
                value: 100
            };
        }
    ]);
    

    CSS: The CSS obviously defines the classes used in the HTML element, this is down to preference of styling but just for my example I will show them:

    .widget-title {
        //whatever you need
    }
    
    .number-widget {
        //whatever you need
    }
    
    .description {
        // whatever you need
    }
    

    I hope this helps someone, any maybe there's a better way of doing but this suited my needs.