AngularJS中没有嵌套DOM元素的嵌套范围?创建一个视图/活动" stack"

时间:2015-05-14 21:07:29

标签: angularjs angularjs-scope

我正在开发一个简单的CRUD应用程序来查看数据库中的最终编辑通用对象/行。每种类型的CRUD相关操作(浏览,查看,创建,编辑,删除,搜索...)都具有相应类型的视图。该视图的实例类似于"浏览表A"或"编辑表B"的第45行。

视图具有可以创建子视图的操作,例如用户可能正在浏览表格并单击"编辑"在一个特定的行上。这使视图成为堆栈,只有最顶层的视图实际显示给用户。触发新操作时,视图会被压入堆栈,并在该操作完成或用户取消时弹出。

现在我的代码看起来像这样:

app.js:

angular.module('MyApp', [])
.controller('AppController', function($scope) {
    var viewStack = [];

    $scope.currentView = function() {
        return viewStack[viewStack.length-1];
    };
    $scope.pushView = function(view) {
        viewStack.push(view);
    };
    $scope.popView= function() {
        viewStack.pop();
    };

    $scope.pushBrowseView = function(table) {
        var view = {
            type: "browse",
            table: table,
            rows: [],
            refresh: function() {
                 // Load data into view.rows via AJAX
            },
            // ...
        };
        view.refresh();
        $scope.pushView(view);
    };
    $scope.pushCreateView = function(table) {
        var view = {
            type: "create",
            table: table,
            newRow: {},
            // ...
        };
        $scope.pushView(view);
    };
    $scope.pushEditView = function(table, row) {
        var view = {
            type: "edit",
            table: table,
            row: row,
            // ...
        };
        $scope.pushView(view);
    };
    // More view types...
})
.controller('BrowseController', function($scope) {
    $scope.create = function() {
        $scope.pushCreateView($scope.currentView().table);
    };
    $scope.edit = function(row) {
        $scope.pushEditView($scope.currentView().table, row);
    };
})
.controller('CreateController', function($scope) {
    $scope.submit = function() {
         if(newRow.isValid()) {
            // POST to server
            window.alert('Row submitted');
            $scope.popView();
        } else {
            window.alert('Not valid');
    };
})
.controller('EditController', function($scope) {
    // Similar to CreateController...
})
// More controllers for other view types

page.html中:

<body ng-app="MyApp" ng-controller="AppController">

    <!-- Stuff -->
    <button ng-click="popView()">Back</button>
    <!-- More Stuff -->

    <div id="theview" ng-switch="currentView().type">

        <div ng-switch-when="browse" ng-controller="BrowseController">
            <button ng-click="create()">New Row</button>
            <table>
                <!-- header goes here -->
                <tr ng-repeat="row in currentView().rows">
                    <td><button ng-click="edit(row)">Edit</button></td>
                    <td ng-repeat="column in currentView().table.columns">
                        {{ row[column] }}
                    </td>
                </tr>
            </table>
        </div>

        <div ng-switch-when="create" ng-controller="CreateController">
            <form>
                <div ng-repeat="column in currentView().table.columns">
                    <label>{{ column }}</label>
                    <input
                        name="{{ column }}"
                        ng-model="currentView().newRow[column]">
                </div>
            </form>
            <button ng-click="submit()">Submit</button>
        </div>

        <div ng-switch-when="edit" ng-controller="EditController">
            <!-- Kinda like "create" -->
        </div>

        <!-- And the rest of the view types -->

    </div>
</body>

很明显,但这显然是它的主旨。问题是应用程序有一个根作用域,每个类型视图都有一个子作用域。由于堆栈上每种类型的视图可能同时存在多个实例,因此每个视图的状态需要完全存储在viewStack中的对象中,而不是存储在该视图类型中$scope {{ 1}}。

这根本不是正确的做事方式。每个视图实例都有一个范围是有意义的,每个范围都是堆栈下面视图范围的子代。这样我就能从顶视图中自然地通过其他事件播放事件。我也不必用currentView()为每个状态变量加前缀。但我能想到的唯一方法是使用递归指令,这可能会创建我不想要的深度嵌套的DOM元素。

这似乎是一种非常常见的设计模式。有没有更好的方法呢?

1 个答案:

答案 0 :(得分:0)

按照您所描述的内容,我认为您的每个观点都更适合directive with isolated scope,而不是将每个观点放在公共范围内的viewStack对象中。

在您的示例代码中,您将从AppController获取表信息,并将它们存储在$ scope中以供您使用。我无法看到这些视图如何进入范围,但假设它们来自某个Web服务,您可以移动存储并将该数据提取到指令本身。例如,

app.directive('exampleView', ['myViewService`, function(myViewService) { 
    return {
        restrict: 'E',
        scope: {
            'myTable': '=',
            'myRows' : '=',
            'onPopView': '&'
        },
        templateUrl: 'url/to/specific/view/template.html',
        link: function($scope,element, attributes) { 
            //Logic for processing values and saving back to server/whatever as required
            $scope.submit = function() {
                if(newRow.isValid()) {
                    // POST to server
                    window.alert('Row submitted');
                } else {
                    window.alert('Not valid');
                }
            };

            if($scope.onPopView) { 
                // Tell parent controller that view is discarded?
                $scope.onPopView();
            }
        }
    };
}]);

onPopView可能不适合您正在做的事情,但是将其用作创建父控制器挂钩的事件/回调的示例。这允许您在指令和父控制器之间分离职责。