2

AngularJS で作成した Web アプリがあります: http://asmor.com/anr

このアプリは、特定のゲームのデッキ構築を支援するためのものです。

カードをデッキに追加すると、それらは右上隅の固定位置の div に追加されます。その div には動的に設定された max-height があるため、ブラウザのウィンドウの下部や、ページの右下隅にある別の固定 div によって遮られることはありません。

最大の高さに達すると、デッキ内のカードのリストがスクロールします。下記参照:

ここに画像の説明を入力

ここでの問題は、赤または緑のボタンをクリックしてスクロールしているときにデッキにカードを追加または削除すると、Angular がデッキ リストを再描画し、スクロールを一番上にリセットすることです。

便宜上、デッキリストに使用しているコードは次のとおりです。

<div id="deck" class="shown-{{ deck.notEmpty }} faction{{ deck.identity.faction }}">
    <div class="deckStat cardTotal">
        <strong class="valid-{{ deck.enoughCards }}">Cards:</strong> {{ deck.cardCount }} / {{ deck.minCards }}
    </div>
    <div class="deckStat agendaPointsTotal shown-{{ deck.isCorp }}">
        <strong class="valid-{{ deck.enoughAgendaPoints }}">Agenda points:</strong> {{ deck.totalPoints }} / {{ deck.minPoints }}
    </div>
    <div class="deckStat influencePointsTotal">
        <strong class="valid-{{ deck.withinInfluenceLimit }}">Influence points:</strong> {{ deck.influenceTotal }} / {{ deck.influenceAvailable }}
    </div>

    <div class="deckIdentity" ng-mouseover="setPreviewLink(deck.identity)">
        <strong>Identity:</strong>
        {{ deck.identity.name }}
    </div>

    <div id="deckScrollContainer" style="max-height: {{ getMaxDeckHeight() }}px">
        <ul ng-repeat="(typeName, typeHash) in deck.cardsByType">
            <li class="deckTypeHeader">
                <strong>{{ typeName }}</strong>
                <span class="quantity">
                    ({{ typeHash.qty }})
                </span>
            </li>
            <li ng-repeat="(cardName, qty) in typeHash.cards" class="card faction{{ getCardFaction(cardName) }}" ng-mouseover="setPreviewLink(cardName)">
                <button class="btn btn-mini btn-success add qty{{qty}}" ng-click="addCard(cardName)"><i class="icon-plus"></i></button>
                <button class="btn btn-mini btn-danger remove qty{{qty}}" ng-click="removeCard(cardName)"><i class="icon-minus"></i></button>
                <span class="quantity">
                    {{ qty }}x
                </span>
                {{ cardName }}
                <span class="influence">
                    {{ getInfluenceString(cardName, qty) }}
                </span>
            </li>
        </ul>
    </div>
</div>

私が試したことの 1 つは、現在のスクロール位置を取得し、後でそれを置き換えるカードを追加または削除するときに関数を追加することでした。

$scope.addCard = function (c) {
    $scope.setDeckScroll();
    $scope.deck.add(c);
};
$scope.setDeckScroll = function () {
    $scope.deckScrollPosition = document.getElementById("deckScrollContainer").scrollTop;
};

と...

$scope.getCardFaction = function (card) {
    $scope._persist();
    card = getCardByName(card);
    return card.faction;
};
$scope._persist = function () {
    // Any weird stuff that needs to be done on redraw/refresh
    if ($scope.deckScrollPosition) {
        document.getElementById("deckScrollContainer").scrollTop = $scope.deckScrollPosition;
        $scope.deckScrollPosition = null;
    }
};

私が知る限り、Angular はページを再描画するときに何度も再描画しています。最後の再描画がいつになるかはわかりません。また、毎回やみくもに scrollPosition を設定することはできません。それと、再描画のために一番上までスクロールした場合。

私が検討したオプションの 1 つは、setTimeout を使用して $scope.deckScrollPosition をクリアすることです。

setTimeout を使用してスクロール位置変数をクリアすることで、機能させることができました(すべての再描画が変数にアクセスできるようにするため)

$scope._persist = function () {
    // Any weird stuff that needs to be done on redraw/refresh
    if ($scope.deckScrollPosition) {
        document.getElementById("deckScrollContainer").scrollTop = $scope.deckScrollPosition;
        setTimeout(function () {
            $scope.deckScrollPosition = null;
        }, 100);
    }
};

...しかし、これは本当にハッキーに思えます。もっと良い方法があるかもしれないと思っています。どのように私ができるかについてのアイデア...

  1. angular をデータの変更ごとに 1 回だけ再描画するようにします (たとえば、複数回再描画を強制するコードが不十分である可能性がありますか?)

  2. setTimeout というごまかしに頼らずに、どうにかして再描画に対処できますか?

4

2 に答える 2

2

ここで答えを見つけました:

http://www.benlesh.com/2013/02/angular-js-scrolling-to-element-by-id.html

少しカスタマイズした plunkr の例はこちら: http://plnkr.co/edit/CTVgvEoY7CnLX38o70i4?p=preview

基本的に、location.hash とそれにスクロールを設定します。セットアップはもう少し複雑なので、うまくいくことを願っています。

e: 実際には最後の項目にビューポートの焦点を合わせていることに気付きましたが、これは実際には望ましくありません。

于 2013-05-03T09:48:32.993 に答える
1

あなたを助けるはずのいくつかのこと。のアイテムにdeck.cardsByTypeID または一意のフィールドがある場合、AngularJS にアイテムを追跡し、新しい ID を持つアイテムのみを再描画するように指示できます。他のアイテムは更新され、リスト全体の再描画が停止します。

<li ng-repeat="(cardName, qty) in typeHash.cards track by id">

また、「最後の再描画がいつになるかわからない」とも言いました-アイテムがいつ描画されるかについてのディレクティブを追加できます。

app.directive( 'repeatLoad', function(){
    return {
        link: function( scope, element, attrs ) {
            scope.$eval( attrs['repeatLoad'] );
        }
    }
}); 

次に、コントローラーに関数を含むディレクティブを追加して呼び出します

<li 
  ng-repeat="(cardName, qty) in typeHash.cards track by id"
  repeat-load="loadedElement()"
>

そして、コントローラーでそれらを数え、最後のものがいつ追加されたかを確認できます

$scope.cnt_cards = 0;
$scope.loaded_cards = 0;

$scope.loadedElement = function() {
  $scope.loaded_cards++;

  if( $scope.cnt_cards == $scope.loaded_cards ) {
    console.log('All cards loaded');
  }
}
于 2013-11-17T21:44:04.317 に答える