【问题标题】:Retain scroll position when a button is toggled切换按钮时保留滚动位置
【发布时间】:2017-03-13 22:16:45
【问题描述】:

我正在开发一个 angularJS 应用程序,该应用程序有一个页面,我使用 ng-repeat 显示大约 30 个项目。每个项目的前面都有一个切换按钮(启用/禁用)。使用我拥有的当前代码,我可以切换这些项目。但问题是如果我向下滚动并切换让我们说项目 25,然后它会自动滚动到页面顶部。如果我现在向下滚动,我可以看到切换确实发生了。
所以现在的要求是确保在点击切换按钮后滚动位置保持不变。

请看下面我的代码。

HTML

<div id="eventTypes" class="panel-body">
    <div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
        <div class="row">
            <div class="col-md-9">{{item.itemName)}}</div>
            <div class="col-md-3">
                <input id="toggleEnabled" 
                       type="button" 
                       ng-class="{'btn-primary': item.enabled}" 
                       value="{{item.enabled ? 'enabled' : 'disabled'}}" 
                       ng-click="toggleEnabled(item)">
            </div>
        </div>
    </div>
    <div ng-if="spinner" class="spinner">
        <div class="spinner-container container1">
            <div class="circle1"></div>
            <div class="circle2"></div>
            <div class="circle3"></div>
            <div class="circle4"></div>
            </div>
        <div class="spinner-container container2">
            <div class="circle1"></div>
            <div class="circle2"></div>
            <div class="circle3"></div>
            <div class="circle4"></div>
        </div>
        <div class="spinner-container container3">
            <div class="circle1"></div>
            <div class="circle2"></div>
            <div class="circle3"></div>
            <div class="circle4"></div>
        </div>
    </div>
</div>

JS

(function () {
    'use strict';
    angular.module('myApp').controller('itemsController', function ($scope, itemsService) {
        var serviceError = function (errorMsg) {
            console.log(errorMsg);
            $scope.turnOffSpinner();
        };

        $scope.items = [];
        $scope.item = {};
        $scope.spinner = true;

        $scope.toggleEnabled = function (item) {
            $scope.turnOnSpinner();
            itemsService.toggleEnabled(item)
                .then(function () {
                    $scope.loaditems();
                });
        };

        $scope.loaditems = function () {
            itemsService.getitems().then(function (response) {
                $scope.items = response.data;
            }, serviceError);
            $scope.turnOffSpinner();
        };

        $scope.turnOnSpinner = function () {
            $scope.spinner = true;
        };

        $scope.turnOffSpinner = function () {
            $scope.spinner = false;
        };

        $scope.loaditems();
    });
}());

现在的工作原理是,一旦我单击切换按钮,就会启用微调器。同时,控制器将调用itemService.toggleEnabled() 方法,该方法对服务器进行ajax 调用,以更改后端中项目的状态(启用为禁用,反之亦然)。成功更改状态以及 ajax 调用返回时,控制器中将调用 $scope.loadItems() 方法。然后,此方法将执行另一个 ajax 调用以获取项目(现在具有已切换项目的更新状态)。微调器被禁用,然后数据显示在 UI 上。 完成所有这些后,页面将滚动到顶部。当我想切换列表中的某个项目时,这很烦人。 当我单击相应项目的切换按钮而不向上滚动到顶部时,我希望页面出现在相同的位置。

我是 AngularJS 的新手,在这方面的任何帮助都会非常有帮助。

【问题讨论】:

  • 我想知道当您重新检索列表时,它是否会调用 $scope.loadItems(..) 如果它清除了缩短页面的列表,导致它在页面较短时向上滚动?
  • 我不这么认为。即使是这样,您认为我如何防止它向上滚动?

标签: javascript html angularjs


【解决方案1】:

看起来您的微调器方案是导致您出现问题的原因:

...
<div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
...
<div ng-if="spinner" class="spinner">
...

当你点击你的按钮时,当你 $scope.turnOnSpinner() 时,你正在从 DOM 中删除 ng-repeat 中的每一个元素。这就是为什么它似乎跳到了顶部。它并没有真正跳跃,只是没有足够的 DOM 元素来填满页面,使得页面太短以至于滚动条消失了(即使只有一秒钟)。然后当微调器完成时,您的 ng-repeat 再次用 DOM 元素填充页面,导致您的滚动位置丢失。

因此,基本上您要解决的问题是加载微调器实现不太理想的症状。

ng-if 是一种在 Angular 中隐藏东西的“残酷”方式。与ng-show/ng-hide 等“更软”的指令相比,它主要是为了隐藏更长时间的东西。解决问题的一种方法是在每个按钮上使用ng-disabled,以防止用户在微调器处于活动状态时与其交互,而不是硬删除每个元素:

之前:

<div ng-if="!spinner" ng-repeat="item in items" class="panel-body">
    <div class="row">
        <div class="col-md-9">{{item.itemName)}}</div>
        <div class="col-md-3">
            <input id="toggleEnabled" 
                   type="button" 
                   ng-class="{'btn-primary': item.enabled}" 
                   value="{{item.enabled ? 'enabled' : 'disabled'}}" 
                   ng-click="toggleEnabled(item)">
        </div>
    </div>
</div>

之后:

<div ng-repeat="item in items" class="panel-body">
    <div class="row">
        <div class="col-md-9">{{item.itemName)}}</div>
        <div class="col-md-3">
            <input id="toggleEnabled" 
                   ng-disabled="spinner"
                   type="button" 
                   ng-class="{'btn-primary': item.enabled}" 
                   value="{{item.enabled ? 'enabled' : 'disabled'}}" 
                   ng-click="toggleEnabled(item)">
        </div>
    </div>
</div>

另一个我非常喜欢并自己使用的解决方案是这个 Angular 模块:https://github.com/darthwade/angular-loading

您可以将它附加到页面中的任何元素,它会在其上放置一个加载微调器,并阻止您与它进行交互,直到您的 ajax 或其他操作完成。

如果您不喜欢其中任何一个,请尝试将您的 ng-repeat 放入一个容器中,以便在微调器启动时防止与您的元素发生交互:

<div class="container" ng-class="{'you-cant-touch-this': spinner}">
  <div ng-repeat="item in items" class="panel-body">
    <div class="row">
        <div class="col-md-9">{{item.itemName)}}</div>
        <div class="col-md-3">
            <input id="toggleEnabled" 
                   type="button" 
                   ng-class="{'btn-primary': item.enabled}" 
                   value="{{item.enabled ? 'enabled' : 'disabled'}}" 
                   ng-click="toggleEnabled(item)">
        </div>
    </div>
  </div>
</div>

现在您可以通过某种方式对其进行样式设置以防止交互,而无需从 DOM 中删除所有这些项目:

.you-cant-touch-this {
  pointer-events: none;
}

【讨论】:

  • 我尝试使用第一种方法,但是当我单击按钮时,没有任何反应。该按钮似乎不再可点击。
  • ng-disabled="!spinner"spinner 部分中删除!,使其看起来像这样:ng-disabled="spinner"。我已经更新了我的答案。
  • 这解决了它。非常感谢。我想知道您是否可以更详细地解释我以前的方法中出了什么问题。
猜你喜欢
  • 2015-05-26
  • 2019-04-13
  • 2011-10-22
  • 2019-01-25
  • 1970-01-01
  • 1970-01-01
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多