2

我正在使用Angular在一個Node.js api服務器周圍創建SPA的業務應用程序。我決定使用狀態機的UI路由器原因以及嵌入URL的直觀方式,但是在指令中創建動態URL時,我偶然遇到了一些挑戰。在指令中動態創建的角度ui路由器的ui-sref?

我爲我的數據網格使用jQuery Datatables作爲指令,但使用'fnRender'生成的任何操作鏈接似乎都沒有將'ui-sref'編譯到它們各自的'href'鏈接。該指令代碼如下:

app.directive('datatable', function ($http) { 
    return { 
    restrict: 'A', 
    link: function ($scope, $elem, attrs) { 
     var responsiveHelper; 
     var breakpointDefinition = { 
      tablet: 1024, 
      phone : 480 
     }; 
     var options = { 
      bDeferRender: true, 
      sPaginationType: "full_numbers", 
      oLanguage: { 
       sEmptyTable: "No records returned", 
       sSearch: "<span>Search:</span> ", 
       sInfo: "Showing <span>_START_</span> to <span>_END_</span> of <span>_TOTAL_</span> entries", 
       sLengthMenu: "_MENU_ <span>entries per page</span>", 
       sProcessing: "Loading..." 
      }, 
      sDom: "lfrtip", 
      oColVis: { 
       buttonText: "Change columns <i class='icon-angle-down'></i>" 
      }, 
      oTableTools: { 
       sSwfPath: "js/plugins/datatable/swf/copy_csv_xls_pdf.swf" 
      }, 
      bAutoWidth  : false, 
      fnPreDrawCallback: function() { 
       if (!responsiveHelper) { 
        responsiveHelper = new ResponsiveDatatablesHelper($elem, breakpointDefinition); 
       } 
      }, 
      fnRowCallback : function (nRow, aData, iDisplayIndex, iDisplayIndexFull) { 
       responsiveHelper.createExpandIcon(nRow); 
      }, 
      fnDrawCallback : function (oSettings) { 
       responsiveHelper.respond(); 
      } 
     }; 
     if (typeof $scope.dtOptions !== 'undefined') { 
      angular.extend(options, $scope.dtOptions); 
     } 
     if (attrs['dtoptions'] === undefined) { 
      for (property in attrs) { 
       switch (property) { 
        case 'sajaxsource': 
         options['sAjaxSource'] = attrs[property]; 
        break; 
        case 'sajaxdataprop': 
         options['sAjaxDataProp'] = attrs[property]; 
        break; 
       } 
      } 
     } else { 
      angular.extend(options, $scope[attrs['dtoptions']]); 
     } 

     if (typeof options['sAjaxSource'] === 'undefined') { 
      throw "Ajax Source not defined! Use sajaxsource='/api/v1/*'"; 
     } 
     if (typeof options['fnServerData'] === 'undefined') { 
      options['fnServerData'] = function (sSource, aoData, resultCb) { 
       $http.get(sSource, aoData).then(function (result) { 
        resultCb(result.data); 
       }); 
      }; 
     } 
     options.aoColumnDefs = []; 
     $elem.find('thead th').each(function() { 
      var colattr = angular.element(this).data(); 
      if (colattr.mdata) { 
       if (colattr.mdata.indexOf("()") > 1) { 
        var fn = $scope[colattr.mdata.substring(0, colattr.mdata.length - 2)]; 
        if (typeof fn === 'function') { 
         options.aoColumnDefs.push({ 
          mData: fn, 
          sClass: colattr.sclass, 
          aTargets: [colattr.atargets] 
         });  
        } else { 
         throw "mData function does not exist in $scope."; 
        } 
       } else { 
        options.aoColumnDefs.push({ 
         mData: colattr.mdata, 
         sClass: colattr.sclass, 
         bVisible: colattr.bvisible, 
         aTargets: [colattr.atargets] 
        }); 
       } 
      } else { 
       if (colattr.fnrender.indexOf("()") > 1) { 
        var fn = $scope[colattr.fnrender.substring(0, colattr.fnrender.length - 2)]; 
        if (typeof fn === 'function') { 
         options.aoColumnDefs.push({ 
          fnRender: fn, 
          sClass: colattr.sclass, 
          aTargets: [colattr.atargets] 
         });  
        } else { 
         throw "fnRender function does not exist in $scope."; 
        } 
       } else { 
        options.aoColumnDefs.push({ 
         fnRender: function (oObj) { 
          return "<a tooltip class='btn' title='View' ui-sref=\""+colattr.tag+".show({slug:\'"+oObj.aData._id+"\'})\"><center class=\"icon-search\"></center></a>"; 
         }, 
         sClass: colattr.sclass, 
         bVisible: colattr.bvisible, 
         aTargets: [colattr.atargets] 
        }); 
       } 
      } 
     }); 
     $elem.dataTable(options); 
     $(".dataTables_length select").wrap("<div class='input-mini'></div>").chosen({disable_search_threshold: 9999999 }); 
    } 
    } 
}); 

它運行與出併發症,甚至會生成以下錨標記:

<a ui-sref="organisation.show({slug:'527a44c02aa9ce3a1c3fbc17'})"></a> 

不過,UI的路由器不把它編譯成各自的狀態URL。可能是什麼問題?有沒有我可能錯過的配置?

感謝

+0

這是不直接關係到你的問題,但我注意到,你沒有引用方括號中的'$ http'依賴於你的代碼,像這樣: 'app.directive('datatable',['$ http',function($ http){/ * logic * /}]);' 如果你沒有養成這樣做的習慣控制器,指令,服務等等,縮小你的腳本 - 根據我的經驗,ASP.NET minifying - 改變了'$ compile'變量,並且找出哪些依賴關係丟失將是一場噩夢。我很難學會,所以我想我會分享。 –

回答

3

是否使用你的數據表中的任何其他指令或表達式?我猜這些都行不通,因爲它看起來像Angular從來沒有機會編譯任何你正在生成的HTML。

這與uiSref無關並且與正確書寫指令有關。在最佳實踐方面,這個指令有方式太多的代碼。你應該把它分解成多個嵌套的指令和直接的HTML。我建議花一些時間學習關於跨越的知識,並使用指令控制器進行嵌套指令。

+0

此外,'$ http'調用應該在控制器中,結果應該是數據綁定到指令的。如果你真的需要將它封裝在一個地方,寫一個伴侶服務並注入。 –

+0

非常感謝您的回答,它似乎有助於如何更好地構建我的代碼。紅色的跨越和嵌套的指令應該有所幫助,但我仍然對服務有點謹慎,哪些功能會更好地寫成服務? – keepitdk

+1

與DOM操作無直接關係的任何非平凡的邏輯。 –

1

撇開最佳做法,我只是自己遇到並解決了這個問題。由於DataTables正在修改Angular事件循環之外的DOM,因此ui-sref屬性不會被編譯。這是一個簡單的解決方法:您需要在創建每行時調用$ compile。

這裏是我的(更簡單的)指令:

function datatable($compile) { 
    return { 
     restrict: "A", 
     link: (scope, elem, attrs) => { 


      // THE IMPORTANT BIT 
      scope.options.createdRow = function (row) { 
       $compile(row)(scope); 
      }; 


      var api = elem.DataTable(scope.options); 
      var handleUpdates = function (newData) { 
       var data = newData || null; 
       if (data) { 
        api.clear(); 
        api.rows.add(data).draw(); 
       } 
      }; 
      scope.$watch("options.data", handleUpdates, true); 
     }, 
     scope: { options: "=" } 
    }; 
}