AngularJS:如何通过字符串访问控制器值,构建通用表格指令
Hey there! Let's work through your problem of building a highly flexible, globally reusable table directive—specifically the issue of accessing controller data via a passed string, and figuring out the best implementation approach.
First: Solving the String Path Data Access Issue
The root problem you're hitting is how to resolve a string like "ingredients.data" to the actual cntrl.ingredients.data array in your controller. A clean way to handle this is to create a helper function that can traverse nested object properties from a string path.
Helper Function for Path Resolution
Add this utility function (you can put it in a shared service or directly in your directive for simplicity):
function getNestedProperty(obj, path) { return path.split('.').reduce((current, key) => { return current && current[key] !== undefined ? current[key] : undefined; }, obj); }
This function takes your controller scope/object and the string path, splits it by dots, and walks through the object to fetch the target data.
Implementing the Reusable Table Directive
Here's a complete example of the directive that uses this helper, supports dynamic data sources, and stays flexible for different table scenarios:
Directive Code
angular.module('yourApp').directive('flexibleTable', function() { return { restrict: 'E', scope: { dataPath: '@', // String path to your controller data (e.g., "ingredients.data") columns: '<' // Array of column configs (e.g., [{key: 'name', label: 'Ingredient Name'}, ...]) }, template: ` <table class="table"> <thead> <tr> <th ng-repeat="col in columns">{{ col.label }}</th> </tr> </thead> <tbody> <tr ng-repeat="row in tableData"> <td ng-repeat="col in columns"> <!-- Support custom templates for complex cells, or default to value display --> <span ng-if="!col.template">{{ getRowValue(row, col.key) }}</span> <div ng-if="col.template" ng-include="col.template"></div> </td> </tr> </tbody> </table> `, link: function(scope, elem, attrs, ctrl) { // Access the parent controller's scope to resolve the data path const parentScope = scope.$parent; // Resolve the initial data source scope.tableData = getNestedProperty(parentScope, scope.dataPath); // Watch for changes in the source data to keep the table updated scope.$watch(() => getNestedProperty(parentScope, scope.dataPath), function(newVal) { scope.tableData = newVal; }, true); // Deep watch to catch changes inside arrays/objects // Helper to fetch row values (supports nested keys like "nutrition.calories") scope.getRowValue = function(row, key) { return getNestedProperty(row, key); }; // Reuse the path resolution helper inside the directive function getNestedProperty(obj, path) { return path.split('.').reduce((current, key) => { return current && current[key] !== undefined ? current[key] : undefined; }, obj); } } }; });
Controller Setup Example
angular.module('yourApp').controller('IngredientController', function() { const cntrl = this; // Your dynamic data structure (changes per table use case) cntrl.ingredients = { data: [ { name: 'Flour', quantity: '2 cups', category: 'Dry' }, { name: 'Eggs', quantity: '3', category: 'Refrigerated' } ] }; // Column config tailored to this table's data cntrl.tableColumns = [ { key: 'name', label: 'Ingredient Name' }, { key: 'quantity', label: 'Quantity' }, { key: 'category', label: 'Category' } ]; });
Calling the Directive in HTML
<div ng-controller="IngredientController as cntrl"> <flexible-table data-path="cntrl.ingredients.data" columns="cntrl.tableColumns"> </flexible-table> </div>
Is This the Optimal Implementation?
This approach hits all the marks for a flexible, reusable table:
- Dynamic Data Source: Uses string paths to pull data from any part of your controller, no hardcoding required.
- Configurable Columns: Lets you define columns per use case, so the table adapts to any data structure.
- Reactivity: Watches the source data for changes, so the table updates automatically when your data changes.
- Extensible: Supports custom column templates for buttons, formatting, or other custom UI elements.
To make it even more robust, you could:
- Move the
getNestedPropertyfunction into a shared utility service for app-wide reuse. - Add optional features like sorting, filtering, or pagination via additional scope bindings.
- Use transclusion to add custom content (like action bars) above/below the table.
内容的提问来源于stack exchange,提问作者txomin

