9

キーボードのみを使用してレコードのリストをナビゲートしようとしています。ページが読み込まれると、デフォルトの「フォーカス」は最初のレコードにあるはずです。ユーザーがキーボードの下矢印をクリックすると、次のレコードにフォーカスする必要があります。ユーザーが上矢印をクリックすると、前のレコードがフォーカスされます。ユーザーが [Enter] ボタンをクリックすると、そのレコードの詳細ページに移動する必要があります。

これが私がこれまでにPlunkrで持っていたものです。

これは AngularJS 1.1.5 (不安定) でサポートされているようで、本番環境では使用できません。私は現在1.0.7を使用しています。私はこのようなことをしたいと思っています-キーはドキュメントレベルで処理する必要があります。ユーザーが特定のキーを押すと、コードは許可されたキーの配列を検索する必要があります。一致するものが見つかった場合 (たとえば、下向きのキー コード)、次の要素にフォーカスを移動 (.highlight css を適用) する必要があります。Enter キーが押されると、css をハイライトするレコードを取得し、さらに処理するためにレコード ID を取得する必要があります。

ありがとうございました!

4

5 に答える 5

14

以下は、あなたが何を選択できるかの例です: http://plnkr.co/edit/XRGPYCk6auOxmylMe0Uu?p=preview

<body key-trap>
  <div ng-controller="testCtrl">
    <li ng-repeat="record in records">
      <div class="record"
           ng-class="{'record-highlight': record.navIndex == focu sIndex}">
        {{ record.name }}
      </div>
    </li>
  </div>
</body>

これは私が考えることができる最も簡単なアプローチです。イベントと メッセージを子スコープにキャッチするディレクティブkeyTrapをバインドします。要素ホルダー スコープはメッセージをキャッチし、単純に focusIndex をインクリメントまたはデクリメントするか、ヒットした場合は関数を起動します。bodykeydown$broadcastopenenter

編集

http://plnkr.co/edit/rwUDTtkQkaQ0dkIFflcy?p=preview

順序付き/フィルター済みリストをサポートするようになりました。

イベント処理部分は変更されていませんが、$indexどのアイテムがフォーカスされているかを追跡するために、フィルタリングされたリスト キャッシング手法も使用されています。

于 2013-07-01T06:58:14.737 に答える
5

これまでに提供されたすべてのソリューションには、共通の問題が 1 つあります。ディレクティブは再利用できません。コントローラーによって提供される親 $scope で作成された変数の知識が必要です。つまり、別のビューで同じディレクティブを使用したい場合は、以前のコントローラーで行ったすべてを再実装し、物事に同じ変数名を使用していることを確認する必要があります。ディレクティブは基本的に $scope 変数名をハードコーディングしているためです。それらの中で。同じ親スコープ内で同じディレクティブを 2 回使用することは絶対にできません。

これを回避するには、ディレクティブで分離スコープを使用します。これにより、親スコープから必要なアイテムを一般的にパラメーター化することで、親の $scope に関係なく、ディレクティブを再利用可能にすることができます。

私のソリューションでは、コントローラーが行う必要がある唯一のことは、テーブル内のどの行が現在選択されているかを追跡するためにディレクティブが使用する selectedIndex 変数を提供することです。この変数の責任をディレクティブに分離することもできましたが、コントローラーに変数を提供させることで、テーブルで現在選択されている行をディレクティブの外で操作できるようになります。たとえば、ディレクティブでのナビゲーションに矢印キーを使用しながら、コントローラに「クリックで行を選択」を実装できます。

指令:

angular
    .module('myApp')
    .directive('cdArrowTable', cdArrowTable);
    .directive('cdArrowRow', cdArrowRow);

function cdArrowTable() {
    return {
        restrict:'A',
        scope: {
            collection: '=cdArrowTable',
            selectedIndex: '=selectedIndex',
            onEnter: '&onEnter'
        },
        link: function(scope, element, attrs, ctrl) {
            // Ensure the selectedIndex doesn't fall outside the collection
            scope.$watch('collection.length', function(newValue, oldValue) {
                if (scope.selectedIndex > newValue - 1) {
                    scope.selectedIndex = newValue - 1;
                } else if (oldValue <= 0) {
                    scope.selectedIndex = 0;
                }
            });

            element.bind('keydown', function(e) {
                if (e.keyCode == 38) {  // Up Arrow
                    if (scope.selectedIndex == 0) {
                        return;
                    }
                    scope.selectedIndex--;
                    e.preventDefault();
                } else if (e.keyCode == 40) {  // Down Arrow
                    if (scope.selectedIndex == scope.collection.length - 1) {
                        return;
                    }
                    scope.selectedIndex++;
                    e.preventDefault();
                } else if (e.keyCode == 13) {  // Enter
                    if (scope.selectedIndex >= 0) {
                        scope.collection[scope.selectedIndex].wasHit = true;
                        scope.onEnter({row: scope.collection[scope.selectedIndex]});
                    }
                    e.preventDefault();
                }

                scope.$apply();
            });
        }
    };
}

function cdArrowRow($timeout) {
    return {
        restrict: 'A',
        scope: {
            row: '=cdArrowRow',
            selectedIndex: '=selectedIndex',
            rowIndex: '=rowIndex',
            selectedClass: '=selectedClass',
            enterClass: '=enterClass',
            enterDuration: '=enterDuration'  // milliseconds
        },
        link: function(scope, element, attrs, ctr) {
            // Apply provided CSS class to row for provided duration
            scope.$watch('row.wasHit', function(newValue) {
                if (newValue === true) {
                    element.addClass(scope.enterClass);
                    $timeout(function() { scope.row.wasHit = false;}, scope.enterDuration);
                } else {
                    element.removeClass(scope.enterClass);
                }
            });

            // Apply/remove provided CSS class to the row if it is the selected row.
            scope.$watch('selectedIndex', function(newValue, oldValue) {
                if (newValue === scope.rowIndex) {
                    element.addClass(scope.selectedClass);
                } else if (oldValue === scope.rowIndex) {
                    element.removeClass(scope.selectedClass);
                }
            });

            // Handles applying/removing selected CSS class when the collection data is filtered.
            scope.$watch('rowIndex', function(newValue, oldValue) {
                if (newValue === scope.selectedIndex) {
                    element.addClass(scope.selectedClass);
                } else if (oldValue === scope.selectedIndex) {
                    element.removeClass(scope.selectedClass);
                }
            });
        }
    }
}

このディレクティブは、矢印キーを使用してテーブルをナビゲートできるだけでなく、コールバック メソッドを Enter キーにバインドすることもできます。そのため、Enter キーが押されると、現在選択されている行が、ディレクティブ (onEnter) に登録されたコールバック メソッドへの引数として含まれます。

ちょっとしたおまけとして、CSS クラスと期間を cdArrowRow ディレクティブに渡すこともできます。これにより、選択した行で Enter キーが押されたときに、渡された CSS クラスが行要素に適用され、渡された後に削除されます。期間 (ミリ秒単位)。これにより、基本的に、Enter キーが押されたときに行を別の色で点滅させるようなことができます。

使用状況を表示:

<table cd-arrow-table="displayedCollection"
       selected-index="selectedIndex"
       on-enter="addToDB(row)">
    <thead>
        <tr>
            <th>First Name</th>
            <th>Last Name</th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="row in displayedCollection" 
            cd-arrow-row="row" 
            selected-index="selectedIndex" 
            row-index="$index" 
            selected-class="'mySelcetedClass'" 
            enter-class="'myEnterClass'" 
            enter-duration="150"
        >
            <td>{{row.firstName}}</td>
            <td>{{row.lastName}}</td>
        </tr>
    </tbody>
</table>

コントローラ:

angular
    .module('myApp')
    .controller('MyController', myController);

    function myController($scope) {
        $scope.selectedIndex = 0;
        $scope.displayedCollection = [
            {firstName:"John", lastName: "Smith"},
            {firstName:"Jane", lastName: "Doe"}
        ];
        $scope.addToDB;

        function addToDB(item) {
            // Do stuff with the row data
        }
    }
于 2015-10-23T18:47:19.630 に答える