刷新后在Angular中保持滚动位置

时间:2013-05-03 08:23:37

标签: javascript angularjs scroll

我已经使用AngularJS撰写了一个网络应用程序:http://asmor.com/anr

此应用程序用于帮助为特定游戏构建套牌。

当您将牌添加到牌组时,它们会被添加到右上角的固定位置的div中。该div具有动态设置的最大高度,因此不会被浏览器窗口的底部或页面右下角的另一个固定div遮挡。

如果您达到最大高度,则卡座中的卡片列表会滚动。见下文:

enter image description here

现在的问题是,当您通过单击红色或绿色按钮滚动卡片时从卡座中添加或删除卡片时,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>

我尝试过的一件事是在添加或删除能够抓住当前滚动位置的卡片时添加功能,然后再更换它。

$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,因为如果有人自己滚动到顶部,我就不会能够分辨出它之间的区别,以及它是否因为重绘而滚动到顶部。

我考虑过的一个选项是使用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. 每次更改数据时只进行一次角度重绘(例如,我可能编写了一些不正确的内容,强迫它多次重绘?)

  2. 以某种方式处理重绘而不诉诸setTimeout chicanery?

2 个答案:

答案 0 :(得分: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:刚刚注意到它实际上将视口聚焦在最后一个项目上,这实际上是不可取的。

答案 1 :(得分:0)

有些事情可以帮到你。如果deck.cardsByType中的项目具有ID或某个唯一字段,您可以告诉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');
  }
}