0

I'm new to AngularJS and hoping someone can help me with this!

I'm developing a Widget Container which allow to add new predefined widgets to the container. Each widget has its own behavior so I need one controller per widget type. A widget could be a ChartBar or DataTable so its controller must know how to fill the widget with data, that's why I'm using a controller for each widget type.

What I did: I could add new widgets to container but only added one type of widget even when I clicked in the "Add Widget 2" and "Add Widget 3" buttons.

Please see the Plunkr for a demo.

Maybe my approach of how achieve this is totally wrong. In that case, a guidance will be very appreciated.

My HTML:

  <div class="col-lg-12">
    <button class="btn btn-xs btn-success" ng-click="addWidget(1)">Add Widget 1</button>
    <button class="btn btn-xs btn-success" ng-click="addWidget(2)">Add Widget 2</button>
    <button class="btn btn-xs btn-success" ng-click="addWidget(3)">Add Widget 3</button>
  </div>

  <div class="portlets-wrapper">
    <!-- START row-->
    <div class="row">
      <widget-container id="portlet-1" portlet="portlet" container="widgetList" class="col-lg-4"></widget-container>
    </div>
    <!-- END row-->
  </div>
</div>

My widget-container directive:

  angular.module('myApp').directive('widgetContainer', function ($rootScope, $compile) {
    var controller = 'Widget1Controller';
    var linker = function(scope, element, attrs) {

        scope.$watch('ctrl.container', function(newValue, oldValue) {

              if(newValue.length>0){
                var item = newValue[newValue.length-1];
                var widgetId = Math.floor((Math.random() * 100) + 1);

                if(item.type ==1) controller= 'Widget1Controller';
                if(item.type ==2) controller= 'Widget2Controller';
                if(item.type ==3) controller= 'Widget3Controller';


                element.html(element.html() + getTemplate(scope, item.type)).show();
                $compile(element.contents())(scope);


              }
        }, true);
    };
    var getTemplate = function(scope, type) {            
        var template1 = '<div id="panelPortlet" class="panel panel-default">\
                          <div class="panel-heading portlet-handler">{{ widgetName || "Another Widget"}} \
                             <paneltool tool-collapse="tool-collapse" tool-dismiss="tool-dismiss" tool-refresh="traditional" tool-settings="tool-settings"></paneltool>\
                          </div>\
                          <div collapse="panelPortlet" class="panel-wrapper">\
                             <div class="panel-body">\
                                Another Widget {{ getData() }}\
                             </div>\
                          </div>\
                       </div>';

        var template2 = '<div id="panelPortlet" class="panel panel-default">\
                          <div class="panel-heading portlet-handler">{{ widgetName || "Chart Bar"}} \
                             <paneltool tool-collapse="tool-collapse" tool-dismiss="tool-dismiss" tool-refresh="traditional" tool-settings="tool-settings"></paneltool>\
                          </div>\
                          <div collapse="panelPortlet" class="panel-wrapper">\
                             <div class="panel-body">\
                                ChartBar {{ getData() }}\
                             </div>\
                          </div>\
                       </div>';

        var template3 = '<div id="panelPortlet" class="panel panel-default">\
                          <div class="panel-heading portlet-handler">{{ widgetName || "Line Chart"}} \
                             <paneltool tool-collapse="tool-collapse" tool-dismiss="tool-dismiss" tool-refresh="traditional" tool-settings="tool-settings"></paneltool>\
                          </div>\
                          <div collapse="panelPortlet" class="panel-wrapper">\
                             <div class="panel-body">\
                                LineChart {{ getData() }}\
                             </div>\
                          </div>\
                       </div>';

        if(type==1) return template1;
        if(type==2) return template2;
        if(type==3) return template3;

    };
    return {
        restrict: "E",
        link: linker,
        scope: {
            container:'='
        }
        ,controller: controller
        ,bindToController: true
        ,controllerAs: 'ctrl'
    };
});

1 Answer 1

3

I think that a proper way would be to divide in different template files each html template. After this, I would create a widget-container directive and a widget directive, which requires widget-container directive to be used(require: '^widget-container').

The html could have something like this:

 <widget-container>
        <widget ng-repeat="widget in widgetList"></widget>
</widget-container>

widget-container directive should manage widgetList object, being part of its scope. Now in your widget directive you could define a template, which using ng-switch manages the multiple templates. It is important to keep your code clean and reusable, so if the behaviour of the widgets is quite different, you should consider defining different directives for each widget.

In that case could be something like this:

 <widget-container>
        <div ng-repeat="widget in widgetList">
                <div ng-switch="{{ widget.type }}">
                    <widget1 ng-switch-when="1" />
                    <widget2 ng-switch-when="2" />
                </div>
        </div>
</widget-container>

EDIT:

So here it is, the plunker with the example working nice. I really believe that it is a much cleaner code, always remember that angularjs helps you a lot to keep code reusable and highly maintainable.

http://plnkr.co/edit/uYRsGDUutMDKsbpmchNK

I have divided your script in 3 different directives, widgetA, widgetB and widgetC. The three of them have their own template (template1,template2 and template3).

Making use of javascript design patterns, I have also created constructors for each widget. And a function addWidget(widgetType) which defines which constructor should be called.

$scope.addWidget = function(widgetType) {
    var newWidget = null;
    switch (widgetType) {
        case 1:
            newWidget = new WidgetA();
            break;
        case 2:
            newWidget = new WidgetB();
            break;
        case 3:
            newWidget = new WidgetC();
            break;
     }
     $scope.widgetList.push(newWidget);
 };

function WidgetA() {
  this.type = 1;
  this.widgetName = 'I`m a widget 1';
  this.getData = function() {
    return [1, 1, 1, 1, 1, 2, 3, 4, 5];
  };
}

function WidgetB() {
  this.type = 2;
  this.widgetName = 'I`m a widget 2';
  this.getData = function() {
    return [1, 2, 2, 2, 2, 2, 3, 4, 5];
  };
}

function WidgetC() {
  this.type = 3;
  this.widgetName = 'I`m a widget 3';
  this.getData = function() {
    return [1, 2, 3, 3, 3, 3, 4, 5];
  };
}

widgetContainer directive wasn`t necessary in this case.

Take a look at it, Hope it helps!

Sign up to request clarification or add additional context in comments.

3 Comments

This is a nice approach!, now, using the ng-switch with multiples directives what should do the widget-container directive? Could you provide a more detailed example or plunkr?
Thanks man! It´s much more clear now. Using this approach I´m facing another issue. The array of data used is only an example, I need to bring data from an API so, when a new widget is added, it redraw all the widgets making an API call for each widget. I think that preventing widget redrawing in each widgetList push should fix it, but I don´t know how to do that. Thoughts?
If you want to prevent rendering of unnecessary items you should use the "track by" option. Here is a question about it: stackoverflow.com/questions/20509280/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.