Javascript 在AngularJS中跟踪栅格内的位置

Javascript 在AngularJS中跟踪栅格内的位置,javascript,angularjs,Javascript,Angularjs,我有一个问题: 我想在AngularJS的网格内跟踪一个位置,可以使用箭头键进行更改,例如10x10网格。我首先想到我会在指令中这样做,所以我有以下代码: angular.module('mymodule').directive('grid', function() { return { restrict : 'A', compile : function($target, $attrs) { var rows = $att

我有一个问题:

我想在AngularJS的网格内跟踪一个位置,可以使用箭头键进行更改,例如10x10网格。我首先想到我会在指令中这样做,所以我有以下代码:

angular.module('mymodule').directive('grid', function() {

    return {

        restrict : 'A',

        compile : function($target, $attrs) {

            var rows = $attrs.rows || 10;
            var cols = $attrs.cols || 10;

            for(var y = 0; y < rows; y++) {
                var row = angular.element('<div>');
                row.addClass('row');
                for(var x = 0; x < cols; x++) {
                    var col = angular.element('<div>');
                    col.addClass('item');
                    row.append(col);
                }
                $target.append(row);
            }

            return function($scope, $elem, $attrs) {
                // Maintain the position here
            }
        }

    }

});
angular.module('mymodule')。指令('grid',函数(){
返回{
限制:“A”,
编译:函数($target$attrs){
变量行=$attrs.rows | | 10;
var cols=$attrs.cols | 10;
对于(变量y=0;y
这将创建一个很好的网格,但是我怀疑实际将管理职位的代码放在哪里。我还希望能够保持两个网格之间的位置,所以我认为需要在存储网格的位置创建类似于位置管理器的东西


您对在我的模块中的何处执行此操作有何想法?

应该使用编译函数的情况非常罕见。您应该能够使用ng repeat完成同样的事情。例如:

<div ng-repeat="row in ctrl.range(rows)">
    <span 
            ng-repeat="col in ctrl.range(cols)" 
            ng-class="{selected: ctrl.curRow === row && ctrl.curCol === col}">
        cell
    </span>
</div>

细胞
有无数种方法可以做到这一点,但下面是一个可以让您开始的工作示例:

如果单击网格,则可以使用箭头键在其周围导航;DR
(但它甚至更长,而且没有大量的文档记录:D)


这是一个很长的解决方案,因此我不打算在这里复制所有代码。
我将提供代码大纲和方法的简短描述。
(完整的代码可以在上面的链接中找到(也作为一个模块捆绑在一起以便于重用)


概述

该方法利用了以下组成部分:

  • A
    网格
    指令

    在漂亮的网格中显示数据,并维护数据/元素的网格对象表示形式

  • A
    gridContainer
    指令

    顾名思义,此元素包含网格。它将网格组合在一起, 因此,容器中一次只能有一个网格处于活动状态(“活动”表示响应箭头键事件)。
    键事件监听器在该元素上注册(然后箭头按下被委托给活动网格)

  • A
    职位
    服务

    这用于创建新的位置对象,以保持和操纵栅格中活动单元的位置

  • A
    网格
    服务

    这用于创建能够跟踪其活动单元位置并提供实用功能的新栅格对象 对于四处移动、选择单元格、确定活动离开边界后“焦点”应放在何处等

  • A
    GridContainer
    service

    这用于“隔离”网格,即防止导航离开容器。
    它还允许在一个页面上有多个具有相同ID的网格。只要每一个都驻留在不同的容器中,所有这些都可以按预期工作

  • A
    方向
    常数

    基本上是对可能的相邻方向(北、南、东、西)的“枚举”

  • 一些CSS
    为了让我们的网格显示得漂亮和“网格化”

  • 下面是它的工作原理(或多或少):

  • 您定义了一个
    元素,该元素包含一个或多个
    元素。
    您可以指定所有必要的数据(ID、大小、数据、位置、“邻居”等)。
    每个
    元素都有一个备份它的
    GridContainer
    对象,每个
    元素都有一个备份它的
    Grid
    对象。
    (为
    grid
    指令提供了默认模板,但用户可以使用
    模板url
    属性指定不同的模板。)

  • 具有键盘焦点并按下箭头键后,它将调用活动网格上的相应方法 (例如,向上/向下/向左/向右移动)

  • 网格计算新位置并更改其内部位置表示。
    如果新位置超出自身边界,它将在相应方向上寻找邻居。
    如果没有在该方向注册的邻居,它将“环绕”(例如,如果您位于顶部并按“向上”,它将从底部开始)

  • 单击单元格时,父栅格将自动激活,其位置将更改为单击的单元格

  • 很简单,对吧


    视图

    这是示例4网格视图的外观:

    <grid-container>
        <grid grid-id="grid-1" data="data1"
              width="{{data1[0].length}}" height="{{data1.length}}" x="0" y="0"
              neighbor-e="grid-2" neighbor-s="grid-3">
        </grid>
        <grid grid-id="grid-2" data="data2"
              width="{{data2[0].length}}" height="{{data2.length}}"
              neighbor-w="grid-1" neighbor-s="grid-4">
        </grid>
        <grid grid-id="grid-3" data="data3"
              width="{{data3[0].length}}" height="{{data3.length}}"
              neighbor-n="grid-1" neighbor-e="grid-4">
        </grid>
        <grid grid-id="grid-4" data="data4"
              width="{{data4[0].length}}" height="{{data4.length}}"
              neighbor-n="grid-2" neighbor-w="grid-3">
        </grid>
    </grid-container>
    
    grid.tmpl.html

    <div tabindex="-1" ng-transclude=""></div>
    
    <div class="grid">
        <div class="row" ng-repeat="row in data" ng-init="rowIdx=$index">
            <div class="cell" ng-class="{active:isActive(colIdx,rowIdx)}"
                    ng-repeat="cell in row" ng-init="colIdx=$index">
                {{rowIdx+1}}.{{colIdx+1}}:<br />cell-{{cell.text}}
            </div>
        </div>
    </div>
    

    完整的代码和工作演示可以在中找到

    它被捆绑为一个模块(
    esGrid
    ),所有组件(控制器/指令/服务等)都是
    es。
    名称间隔(出于可重用性原因)。
    要在应用程序中使用它,请执行以下操作:

    1。在脚本中包含
    [模块:esGrid]
    下的IIFE。
    2。添加
    esGrid
    作为模块的依赖项。
    3.使用视图中的指令:

    4.像这样定义自己的模板:

    5.如果要引用CSS中的元素(或任何类似CSS的选择器),请不要忘记转义
    ,因为它是一个特殊字符:<
    var app = angular.module('myApp', []);
    
    app.controller('mainCtrl', function ($scope) {
        var data = [
            [{text:  1}, {text:  2}, {text:  3}, {text:  4}, {text:  5}],
            [{text:  6}, {text:  7}, {text:  8}, {text:  9}, {text: 10}],
            [{text: 11}, {text: 12}, {text: 13}, {text: 14}, {text: 15}],
            [{text: 16}, {text: 17}, {text: 18}, {text: 19}, {text: 20}],
            [{text: 21}, {text: 22}, {text: 23}, {text: 24}, {text: 25}]
        ];
    
        $scope.data1 = $scope.data2 = $scope.data3 = $scope.data4 = data;
    });
    
    app.directive('gridContainer', function (GridContainer) {
        return {
            restrict: 'E',
            templateUrl: 'partials/grid-container.tmpl.html',
            transclude: true,
            scope: {},
            controller: function gridContainerCtrl($element, $scope) {
                // Create a new GridContainer and maintain a reference to the currently active Grid,
                // so key-events change the position in that Grid
    
                // Delegate key-events to the active Grid
    
                // Deregister the event-listener upon removing the element
            }
        };
    });
    
        templateUrl: function (tElem, tAttrs) {
            var templateUrl = tAttrs.templateUrl;
    
            if (!templateUrl) {
                if (!$templateCache.get(defaultTmplKey)) {
                    $templateCache.put(defaultTmplKey, defaultTmplStr);
                }
                templateUrl = defaultTmplKey;
            }
    
            return templateUrl;
        },
    app.directive('grid', function ($templateCache, DIRECTION) {
        ...
    
        return {
            restrict: 'E',
            require: '^gridContainer',
            templateUrl: function (tElem, tAttrs) {
                // Return `tAttrs.templateUrl` if it is defined.
                // If not, put the default template into the `$templateCache`
                // and return the key.
            },
            scope: {
                data:   '=',
                gridId: '@',
                width:  '@',
                height: '@',
                x:      '@',
                y:      '@',
                neighborN: '@',
                neighborS: '@',
                neighborE: '@',
                neighborW: '@'
            },
            link: function gridPostLink(scope, elem, attrs, ctrl) {
                // Initialize a Grid
    
                // Utility function to check if a cell is the active cell
    
                // Upon clicking on a cell, set the current Grid as active
                // and change its position to the clicked cell
    
                // Deregister the event-listener upon removing the element
    
                // If the position is initialized [i.e. both !== -1],
                // set this Grid as the active grid
            }
        };
    });
    
    app.constant('DIRECTION', {...});
    
    app.factory('Grid', function (Position, DIRECTION) {
        ...
    
        function Grid(siblingGrids, id, rows, cols, x, y) {
            this._id           = ...;
            this._rows         = ...;
            this._cols         = ...;
            this._position     = new Position(...);
            this._neighbors    = ...;
            this._siblingGrids = ...;
    
            ...
        }
    
        Grid.prototype.getRows       = function () {...};
        Grid.prototype.getColumns    = function () {...};
    
        Grid.prototype.getX          = function () {...};
        Grid.prototype.getY          = function () {...};
        Grid.prototype.setXY         = function (x, y) {...};
    
        Grid.prototype.getNeighbor   = function (dir) {...};
        Grid.prototype.setNeighbor   = function (dir, neighborID) {...};
    
        Grid.prototype.indexToXY     = function (idx) {...};
        Grid.prototype.xyToIndex     = function (x, y) {...};
    
        Grid.prototype.moveUp        = function () {...};
        Grid.prototype.moveDown      = function () {...};
        Grid.prototype.moveLeft      = function () {...};
        Grid.prototype.moveRight     = function () {...};
    
        Grid.prototype._findNeighbor = function (direction) {...};
    
        return Grid;
    });
    
    app.factory('GridContainer', function (Grid) {
        function GridContainer() {
            this._grids = ...;
        }
    
        GridContainer.prototype.newGrid = function (id, rows, cols, x, y) {...};
    
        return GridContainer;
    });
    
    app.factory('Position', function () {
        function Position(x, y) {
            this._x = ...;
            this._y = ...;
        }
    
        Position.prototype.getX  = function () {...};
        Position.prototype.getY  = function () {...};
        Position.prototype.setXY = function (x, y) {...};
    
        return Position;
    });