Angularjs,需要最佳解决方案来将多个指令的动态模板形成和编译代码从控制器移动到自定义指令
Angularjs, need best solution to move dynamic template formation and compilation code of multiple directives from controller to custom directive
我需要在单击选项卡时在单个页面中显示多个 angularjs 指令。它可以是 c3 图表指令和 ng 网格指令的组合。我正在控制器中使用所有这些相关参数准备模型,然后形成模板,然后在控制器本身中进行编译,这非常好用。当我意识到在控制器中进行 DOM 操作不是一个好习惯时,我正在尝试在自定义指令中进行操作。
该指令应支持以下功能:
- 模板应该是 C3 图表指令的组合。
- 模板还可以包含 Angularjs ng Grid 指令以及 c3 图表指令。
- 将来我还想将 Good Map 指令与 C3 图表和 ng 网格指令一起使用。
- 自定义下拉菜单应该支持其中一些指令。
现在我在我的控制器中使用了以下代码,它运行良好。
var template = '<div class= "chartsDiv">';
var dashletteId = 0;
var dashletterName = "chart";
var chartName = "";
for (var dashVar = 0; dashVar < 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 += ' <div class="col"> <p class="graphtitle">' + dashletterName + ' </p> <span class="nullable"> <select ng-model="chartTypeSel" ng-options="eachChartType.name for eachChartType in chartTypeOptions" ng-change="transformChart(chartTypeSel, \'' + chartName + '\')"> </select> </span> <c3-simple id = "' + chartName + '" config="' + chartName + '"></c3-simple> </div>'
} else {
template += ' <div class="col"> <p class="graphtitle">' + dashletterName + ' </p> <c3-simple id = "' + chartName + '" config="' + chartName + '"></c3-simple> </div>';
}
}
template += ' </div>';
angular.element(document.querySelectorAll('.snap-content')).append($compile(template)($scope));
为了简单起见,我只提供了一些示例代码。基于 dashletteId,我有一些特定的要求,我正在根据 dashletteId 动态创建模板,所有这些代码对我来说都很好用。现在我的目标是将所有这些模板形成和编译代码从控制器移动到自定义指令,我正在为此寻找最佳解决方案,任何人都可以建议我一些指向最佳解决方案的指示。
对于特定的用户,当他点击任何标签时,需要形成什么样的模板来进行编译是预定义的。所以我可以在 ng-init 函数调用或选项卡的单击(即 select)函数调用期间获得它。
以下是我的ng网格模板形成的示例代码。
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 += ' <div class="col"> <p class="graphtitle">' + dashletterName + ' </p> <div class="gridStyle" ng-grid="' + config[dashletteId].gridOptions + '"></div>';
}
所以在单个页面中我需要显示四个指令,它可以是 3 个 c3 图表和 1 个 ng Grid table 指令,或者 2 个 C3 图表和 2 个 ng 网格 tables,等等基于用户所做的预定义选择。
以下是我的自定义指令的初步代码,在进一步研究之前,我想从其他人那里获取输入以获得更好的方法。在我的 link 函数中,我需要在单击选项卡或 ng-init 阶段等时从控制器动态获取模板。
app.directive('customCharts', ['$compile', function($compile) {
return {
restrict: 'EA',
scope: {
chartName: '='
},
link: function(scope, element) {
var template = ' <div class="col"> <p class="graphtitle">' + dashletterName + ' </p> <c3-simple id = "' + chartName + '" config="' + chartName + '"></c3-simple> </div>'
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);
}
}
}]);
如果我需要进一步说明我的问题,请告诉我。任何指示请提供一些示例代码。
我建议创建 一个或多个指令,每个指令向 DOM 添加一个组件 - 因此 1、2 和 3 应该是单独的指令。根据我的经验,如果您创建一个添加多个组件的指令,您最终可能会用一个笨重的控制器来换取一个笨重的指令。
至于 config 和 columnDefs 之类的东西,我会 在控制器中定义配置 ,或者在单独的 service/factory 中定义配置,如果你想保持你的控制器非常轻,然后将配置传递给指令范围。这将允许您在应用程序内或跨应用程序重复使用指令。
要监视控制器更改,请在指令 scope.$watch() 中添加适当的属性,然后在指令中调用适当的更新函数。我相信这就是您所说的 4 的意思。我的示例中的下拉菜单证明了这一点。
我在没有使用 $compile 的情况下取得了成功,但也许其他人可以为此争论不休。
警告:如果您使用的第三方库使用 Angular 不知道的异步功能,您可能需要将指令的更新行为放在 中scope.$apply(),否则 DOM 直到下一个 Angular 摘要周期才会更新。
如果您的 HTML 不止一两行,我建议您使用 templateUrl.
这是一个 toy example 使用 jQuery 的简单方法。
app.directive('chart', function() {
return {
restrict: 'EA',
scope: {config: '='},
templateUrl: 'chart.html',
link: function(scope, element) {
var el = element[0];
function update() {
$(el).html('<h4>' + scope.config.chartType + '</h4>');
}
scope.$watch('config', update, true);
}
}
});
我需要在单击选项卡时在单个页面中显示多个 angularjs 指令。它可以是 c3 图表指令和 ng 网格指令的组合。我正在控制器中使用所有这些相关参数准备模型,然后形成模板,然后在控制器本身中进行编译,这非常好用。当我意识到在控制器中进行 DOM 操作不是一个好习惯时,我正在尝试在自定义指令中进行操作。
该指令应支持以下功能:
- 模板应该是 C3 图表指令的组合。
- 模板还可以包含 Angularjs ng Grid 指令以及 c3 图表指令。
- 将来我还想将 Good Map 指令与 C3 图表和 ng 网格指令一起使用。
- 自定义下拉菜单应该支持其中一些指令。
现在我在我的控制器中使用了以下代码,它运行良好。
var template = '<div class= "chartsDiv">';
var dashletteId = 0;
var dashletterName = "chart";
var chartName = "";
for (var dashVar = 0; dashVar < 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 += ' <div class="col"> <p class="graphtitle">' + dashletterName + ' </p> <span class="nullable"> <select ng-model="chartTypeSel" ng-options="eachChartType.name for eachChartType in chartTypeOptions" ng-change="transformChart(chartTypeSel, \'' + chartName + '\')"> </select> </span> <c3-simple id = "' + chartName + '" config="' + chartName + '"></c3-simple> </div>'
} else {
template += ' <div class="col"> <p class="graphtitle">' + dashletterName + ' </p> <c3-simple id = "' + chartName + '" config="' + chartName + '"></c3-simple> </div>';
}
}
template += ' </div>';
angular.element(document.querySelectorAll('.snap-content')).append($compile(template)($scope));
为了简单起见,我只提供了一些示例代码。基于 dashletteId,我有一些特定的要求,我正在根据 dashletteId 动态创建模板,所有这些代码对我来说都很好用。现在我的目标是将所有这些模板形成和编译代码从控制器移动到自定义指令,我正在为此寻找最佳解决方案,任何人都可以建议我一些指向最佳解决方案的指示。
对于特定的用户,当他点击任何标签时,需要形成什么样的模板来进行编译是预定义的。所以我可以在 ng-init 函数调用或选项卡的单击(即 select)函数调用期间获得它。
以下是我的ng网格模板形成的示例代码。
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 += ' <div class="col"> <p class="graphtitle">' + dashletterName + ' </p> <div class="gridStyle" ng-grid="' + config[dashletteId].gridOptions + '"></div>';
}
所以在单个页面中我需要显示四个指令,它可以是 3 个 c3 图表和 1 个 ng Grid table 指令,或者 2 个 C3 图表和 2 个 ng 网格 tables,等等基于用户所做的预定义选择。
以下是我的自定义指令的初步代码,在进一步研究之前,我想从其他人那里获取输入以获得更好的方法。在我的 link 函数中,我需要在单击选项卡或 ng-init 阶段等时从控制器动态获取模板。
app.directive('customCharts', ['$compile', function($compile) {
return {
restrict: 'EA',
scope: {
chartName: '='
},
link: function(scope, element) {
var template = ' <div class="col"> <p class="graphtitle">' + dashletterName + ' </p> <c3-simple id = "' + chartName + '" config="' + chartName + '"></c3-simple> </div>'
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);
}
}
}]);
如果我需要进一步说明我的问题,请告诉我。任何指示请提供一些示例代码。
我建议创建 一个或多个指令,每个指令向 DOM 添加一个组件 - 因此 1、2 和 3 应该是单独的指令。根据我的经验,如果您创建一个添加多个组件的指令,您最终可能会用一个笨重的控制器来换取一个笨重的指令。
至于 config 和 columnDefs 之类的东西,我会 在控制器中定义配置 ,或者在单独的 service/factory 中定义配置,如果你想保持你的控制器非常轻,然后将配置传递给指令范围。这将允许您在应用程序内或跨应用程序重复使用指令。
要监视控制器更改,请在指令 scope.$watch() 中添加适当的属性,然后在指令中调用适当的更新函数。我相信这就是您所说的 4 的意思。我的示例中的下拉菜单证明了这一点。
我在没有使用 $compile 的情况下取得了成功,但也许其他人可以为此争论不休。
警告:如果您使用的第三方库使用 Angular 不知道的异步功能,您可能需要将指令的更新行为放在 中scope.$apply(),否则 DOM 直到下一个 Angular 摘要周期才会更新。
如果您的 HTML 不止一两行,我建议您使用 templateUrl.
这是一个 toy example 使用 jQuery 的简单方法。
app.directive('chart', function() {
return {
restrict: 'EA',
scope: {config: '='},
templateUrl: 'chart.html',
link: function(scope, element) {
var el = element[0];
function update() {
$(el).html('<h4>' + scope.config.chartType + '</h4>');
}
scope.$watch('config', update, true);
}
}
});