title: Angularjs, need best solution to move dynamic template formation and compilation code of multiple directives from controller to custom directive
body: <p>I need to display multiple angularjs directives in a single page on tab click.  It could be a combination c3 chart directives and ng grid directives. I am preparing the model with all these relevant parameters in the controller then forming the template and then compiling in the controller itself, which is perfectly working fine. As I realized doing DOM manipulation in controller is not a good practice, I am trying to do it in the custom directive. </p>
  
  <p>This directive should support the following features :</p>
  
  <ol>
  <li>The template should be combination of C3 chart directives.</li>
  <li>The template can also have Angularjs ng Grid directive also along with c3 chart directives.</li>
  <li>In future I also would like to use Good Map directive along with C3 chart and ng grid directives. </li>
  <li>And some of these directives should be supported with custom dropdown. </li>
  </ol>
  
  <p>For now I have used the following code in my controller which is perfectly working fine.</p>
  
  <pre><code>   var template = '&lt;div class= "chartsDiv"&gt;';
  
      var dashletteId = 0;
      var dashletterName = "chart";
      var chartName = "";
  
      for (var dashVar = 0; dashVar &lt; data.tabDetails.length; dashVar++) {
          dashletteId = data.tabDetails[dashVar].dashletteId; // Added
          dashletterName = data.tabDetails[dashVar].dashletteName;
          var axisType = data.tabDetails[dashVar].axisType;
          var dataType = data.tabDetails[dashVar].dataType;
  
          chartName = "chart" + eachTab.tabName.replace(/ +/g, "") + dashletteId ;
  
          $scope[chartName] = {};
  
  
          if (axisType == "timeseries") {
  
              var xticksClassiffication = data.tabDetails[dashVar].xticksClassification;
              var tickFormatFunction = {};               
              $scope[chartName] = {
  
                  data: {
                      x: 'x',
                      columns: data.tabDetails[dashVar].columns,
                      type: data.tabDetails[dashVar].dataType
                  },
                  axis: {
                      x: {
                          type: data.tabDetails[dashVar].axisType,                          
                          tick: {
                              format: data.tabDetails[dashVar].xtickformat
                                  // '%Y-%m-%d'
  
                          }
                      }
                  },
                  subchart: {
                      show: true
                  }
  
              };
  
          }
  
          if (dashletteId == 7) {
  
              template += ' &lt;div class="col"&gt; &lt;p class="graphtitle"&gt;' + dashletterName + '  &lt;/p&gt; &lt;span class="nullable"&gt;  &lt;select ng-model="chartTypeSel" ng-options="eachChartType.name for eachChartType in chartTypeOptions" ng-change="transformChart(chartTypeSel, \'' + chartName + '\')"&gt; &lt;/select&gt; &lt;/span&gt; &lt;c3-simple id = "' + chartName + '" config="' + chartName + '"&gt;&lt;/c3-simple&gt;  &lt;/div&gt;'
  
          } else {
  
              template += ' &lt;div class="col"&gt; &lt;p class="graphtitle"&gt;' + dashletterName + ' &lt;/p&gt; &lt;c3-simple id = "' + chartName + '" config="' + chartName + '"&gt;&lt;/c3-simple&gt; &lt;/div&gt;';
          }
  
  
      }
  
      template += ' &lt;/div&gt;';
      angular.element(document.querySelectorAll('.snap-content')).append($compile(template)($scope));
  </code></pre>
  
  <p>In order to make it simple I have provided only some sample code. Based on dashletteId,  I have some specific requirements for which I am creating template dynamically based on dashletteId, all this code is perfectly working fine for me. Now my aim is to move all this template formation and compilation code from controller to a custom directive and I am looking for best possible solution for this, can any suggest me some pointers towards best solution. </p>
  
  <p>For a specific user when he clicks any tab, what template has to be formed for compilation is predefined. So I can get that either during ng-init function call or tab's click (i.e, select) function call. </p>
  
  <p>The following is sample code for my ng grid template formation.</p>
  
  <pre><code>if (axisType == "table") {
  
          var config = {
              9: {
                  gridOptions: 'gridOptionsOne',
                  data: 'dataOne',
                  columnDefs: 'colDefsOne'
              },
              10: {
                  gridOptions: 'gridOptionsTwo',
                  data: 'dataTwo',
                  columnDefs: 'colDefsTwo'
              },
              11: {
                  gridOptions: 'gridOptionsThree',
                  data: 'dataThree',
                  columnDefs: 'colDefsThree'
              },
              18: {
                  gridOptions: 'gridOptionsFour',
                  data: 'dataFour',
                  columnDefs: 'colDefsFour'
              }
          };
  
          $scope.getColumnDefs = function(columns) {
              var columnDefs = [];
  
              columnDefs.push({
                  field: 'mode',
                  displayName: 'Mode',
                  enableCellEdit: true,
                  width: '10%'
              });
              columnDefs.push({
                  field: 'service',
                  displayName: 'Service',
                  enableCellEdit: true,
                  width: '10%'
              });
  
              angular.forEach(columns, function(value, key) {
                  columnDefs.push({
                      field: key,
                      displayName: value,
                      enableCellEdit: true,
                      width: '10%'
                  })
              });
  
              return columnDefs;
          };
  
          if (dataType == "nggridcomplex") {
  
              $scope.serverResponse = {
                  columns: data.tabDetails[dashVar].columns,
                  data: data.tabDetails[dashVar].data
  
              };
  
              $scope[config[dashletteId].columnDefs] = $scope.serverResponse.columns;
              $scope[config[dashletteId].data] = $scope.serverResponse.data;
  
          } else {
              if (dashletteId == 18) {
                  $scope.serverResponse = {
                      columns: data.tabDetails[dashVar].timespans[0], // This is for column headers.
                      data: data.tabDetails[dashVar].columns
                  };
  
              } else {
                  $scope.serverResponse = {
                      columns: data.tabDetails[dashVar].timespans[0], // This is for column headers.
                      data: data.tabDetails[dashVar].columns
                  };
              }
  
  
              $scope[config[dashletteId].columnDefs] = $scope.getColumnDefs($scope.serverResponse.columns);
              $scope[config[dashletteId].data] = $scope.serverResponse.data;
          }
  
          $scope[config[dashletteId].gridOptions] = {
              data: config[dashletteId].data,
              showGroupPanel: true,
              jqueryUIDraggable: false,
              columnDefs: config[dashletteId].columnDefs
          };
  
          template += ' &lt;div class="col"&gt; &lt;p class="graphtitle"&gt;' + dashletterName + ' &lt;/p&gt; &lt;div class="gridStyle" ng-grid="' + config[dashletteId].gridOptions + '"&gt;&lt;/div&gt;';
  
      }
  </code></pre>
  
  <p>So in a single page I need to show four directives, it could be 3 c3 charts and 1 ng Grid table directive, or 2 C3 charts and 2 ng Grids tables, etc based on the predefined choice made by the user.</p>
  
  <p>The following is preliminary code of my custom directive before working on further on this I thought of taking input from others for better approach. Here in my link function the template I need to get dynamically from controller upon tab click or ng-init phase, etc. </p>
  
  <pre><code>app.directive('customCharts', ['$compile', function($compile) {
      return {
          restrict: 'EA',
          scope: {
              chartName: '='
          },
          link: function(scope, element) {
              var template = ' &lt;div class="col"&gt; &lt;p class="graphtitle"&gt;' + dashletterName + ' &lt;/p&gt; &lt;c3-simple id = "' + chartName + '" config="' + chartName + '"&gt;&lt;/c3-simple&gt; &lt;/div&gt;'
              var parent = angular.element(document.querySelectorAll('.chartsDiv')) // DOM element where the compiled template can be appended
              var linkFn = $compile(template);
              var content = linkFn(scope);
              parent.append(content);
          }
  
      }
  
  
  }]);
  </code></pre>
  
  <p>Please let me know if I need to provide any further clarification to my question. Any directions please with some sample code.</p>