您可以在下一个视频中看到我的意思:https://www.dropbox.com/s/zu0skpfo2zkrex3/ios_safari_gestures.mp4?dl=0
在这个例子中,当我继续浏览器底部的按钮时,一切正常,页面显示正确。然而,当我从右向左滑动时,浏览器执行一些不需要的动画,导致一种闪烁。
接下来是视频示例的配置:
state: 'statistics-consumptions',
config: {
resolve: {
breadCrumbData: ['$stateParams', 'EquipmentMainService', function($stateParams, EquipmentMainService) {
return EquipmentMainService.getBreadcrumbData($stateParams.equipmentId);
}]
},
url: '/statistics-consumptions/{equipmentId}',
templateUrl: 'app/statistics/consumption/consumptions-test.html'
}
如果我删除resolve
条件,则闪烁消失。为什么会这样?它是UI路由器问题吗?
有趣的是,有些情况发生了这种情况。例如,前面的示例是真实案例的非常简化的版本。接下来是真实案例的配置。
状态:
state: 'statistics-consumptions',
config: {
resolve: {
breadCrumbData: ['$stateParams', 'EquipmentMainService', function($stateParams, EquipmentMainService) {
return EquipmentMainService.getBreadcrumbData($stateParams.equipmentId);
}],
initialData: ['$stateParams', 'StatisticsConsumptionsService', function($stateParams, StatisticsConsumptionsService) {
return StatisticsConsumptionsService.getInitialResources($stateParams.equipmentId);
}]
},
url: '/statistics-consumptions/{equipmentId}',
templateUrl: 'app/statistics/consumption/consumptions.html',
controller: 'StatisticsConsumptionsController',
controllerAs: 'vmStatisticsConsumptionsView',
title: 'statistics.consumptions.tab.title',
ncyBreadcrumb: {
label: 'breadcrumb.equipment.statistics.consumptions',
parent: 'statistics-general-view({equipmentId: bcEquipmentId})'
}
}
控制器。实际上,重要的部分是activate
函数,它从解析对象接收数据并初始化视图的数据:
(function() {
'use strict';
angular
.module('app.statistics')
.controller('StatisticsConsumptionsController', StatisticsConsumptionsController);
StatisticsConsumptionsController.$inject = ['$rootScope', '$scope', '$stateParams', '$state', 'StatisticsConsumptionsService', 'toastr', 'ngDialog',
'EQUIPMENT_TYPE_ORION', 'UtilsService', 'breadCrumbData', 'initialData', 'EQUIPMENT_TYPE_UNILAV'];
/**
* Controller to manage all actions related to the search of consumptions
*/
function StatisticsConsumptionsController($rootScope, $scope, $stateParams, $state, StatisticsConsumptionsService, toastr, ngDialog,
EQUIPMENT_TYPE_ORION, UtilsService, breadCrumbData, initialData, EQUIPMENT_TYPE_UNILAV) {
var vm = this;
vm.auxCurrentPage = 0;
vm.consumptions = null; // for the table
vm.deleteEquipmentData = deleteEquipmentData;
vm.deleteEquipmentDataConfirmDialog = deleteEquipmentDataConfirmDialog;
vm.dateFormat = 'yyyy/MM/dd';
vm.deleteWebData = deleteWebData;
vm.deleteWebDataConfirmDialog = deleteWebDataConfirmDialog;
vm.endDatePopupOpened = false;
vm.exportData = exportData;
vm.exportDataConfirmDialog = exportDataConfirmDialog;
vm.equipmentFormulas = [];
vm.formulaSelected = {};
vm.isOrionEquipment = false;
vm.openEndDatePopup = openEndDatePopup;
vm.openStartDatePopup = openStartDatePopup;
vm.pageSizeSelectorOptions = [];
vm.pumps = [];
vm.pumpSelected = {};
vm.receiveConsumptionsDialog = receiveConsumptionsDialog;
vm.search = search;
vm.searchObject = {equipmentId: null, startDate: null, endDate: null, washer: null, formula: null, pump: null, page: 0, size: 0};
vm.security = {};
vm.startDatePopupOpened = false;
vm.totalItems = 0;
vm.updateAfterWasherSelection = updateAfterWasherSelection;
vm.washers = [];
vm.washerFormulas = [];
vm.washerSelected = {};
vm.webId = '';
activate();
/**
* Initialization actions
*/
function activate() {
// Breadcrumb
$scope.bcEquipmentId = breadCrumbData.data.equipmentId;
$scope.bcEquipmentName = breadCrumbData.data.equipmentName;
$scope.bcInstallationName = breadCrumbData.data.installationName;
$scope.bcEquipmentInstallationId = breadCrumbData.data.installationId;
vm.washers = initialData.data[0];
vm.equipmentFormulas = initialData.data[1];
vm.pumps = initialData.data[2];
vm.searchObject.equipmentId = $stateParams.equipmentId;
vm.searchObject.startDate = initialData.data[3];
vm.searchObject.endDate = initialData.data[4];
vm.pageSizeSelectorOptions = initialData.data[5];
vm.isOrionEquipment = initialData.data[6] === EQUIPMENT_TYPE_ORION;
// Combo boxes
vm.washerSelected = vm.washers[0];
vm.pumpSelected = vm.pumps[0];
// We filter the formulas for the washer selected
vm.washerFormulas = [];
if (vm.isOrionEquipment) {
vm.washerFormulas = initialData.data[7];
} else {
for (var i = 0; i < vm.equipmentFormulas.length; i++) {
if (vm.equipmentFormulas[i].washerNumber === vm.washerSelected.number ||
initialData.data[6] === EQUIPMENT_TYPE_UNILAV) {
vm.washerFormulas.push(vm.equipmentFormulas[i]);
}
}
}
// Security
vm.security = initialData.data[8];
vm.webId = initialData.data[9];
$rootScope.cmcBaseForm.equipmentId = $stateParams.equipmentId;
// Default page size
vm.searchObject.size = vm.pageSizeSelectorOptions[0];
// Formulas combo box
vm.formulaSelected = vm.washerFormulas[0];
vm.auxCurrentPage = initialData.data[10].number + 1; // The front end page (Bootstrap pagination element) is one position ahead respect server pagination
vm.searchObject.size = initialData.data[10].size;
vm.totalItems = initialData.data[10].totalElements;
vm.consumptions = initialData.data[10].content;
}
/**
* Deletes the list of consumption records
*/
function deleteEquipmentData() {
$rootScope.cmcBaseForm.commActionId = $rootScope.CMC_ACTION_IDENTIFIER.CMC_ACTION_DELETE_CONSUMPTIONS;
$scope.cc.startCommProcess($rootScope.cmcBaseForm);
}
/**
* Confirm dialog to delete the list of consumptions of the equipment
*/
function deleteEquipmentDataConfirmDialog() {
$scope.confirmTitle = 'consumption.list.delete.equipment.data.confirm.title';
ngDialog.openConfirm({
template: 'app/core/template/confirm.action.template.html',
scope: $scope
}).then(
function() {
deleteEquipmentData();
}, function() {}
);
}
/**
* Deletes a bunch of consumption records from the server
*/
function deleteWebData() {
vm.searchObject.washer = vm.washerSelected.number;
vm.searchObject.formula = vm.formulaSelected.number;
vm.searchObject.pump = vm.pumpSelected.number;
StatisticsConsumptionsService.deleteWebData(vm.searchObject)
.then(
function() {
vm.auxCurrentPage = 1;
vm.totalItems = 0;
vm.consumptions = [];
toastr.success('consumption.list.web.data.deleted');
}
);
}
/**
* Opens the confirm dialog to delete the current consumptions
*/
function deleteWebDataConfirmDialog(form) {
if (!form.$valid) {
return;
}
$scope.confirmTitle = 'consumption.list.delete.web.data.confirm.title';
ngDialog.openConfirm({
template: 'app/core/template/confirm.action.template.html',
scope: $scope
}).then(
function() {
deleteWebData();
}, function() {}
);
}
/**
* Export a bunch of consumption records
*/
function exportData() {
vm.searchObject.washer = vm.washerSelected.number;
vm.searchObject.formula = vm.formulaSelected.number;
vm.searchObject.pump = vm.pumpSelected.number;
StatisticsConsumptionsService.exportData(vm.searchObject)
.then(
function(response) {
UtilsService.downloadFile(response.data.fileName);
}
);
}
/**
* Opens the confirm dialog to export the current records shown
*/
function exportDataConfirmDialog(form) {
if (!form.$valid) {
return;
}
if (vm.totalItems <= 0) {
toastr.warning('consumption.list.export.no.data.to.export');
return;
}
$scope.confirmTitle = 'consumption.list.export.data.confirm.title';
ngDialog.openConfirm({
template: 'app/core/template/confirm.action.template.html',
scope: $scope,
resolve: {
breadCrumbData: ['$stateParams', 'EquipmentMainService', function($stateParams, EquipmentMainService) {
return EquipmentMainService.getBreadcrumbData($stateParams.equipmentId);
}]
},
}).then(
function() {
exportData();
}, function() {}
);
}
/**
* Opens the dialog to select the consumptions to receive
*/
function receiveConsumptionsDialog() {
$scope.equipmentId = $stateParams.equipmentId;
ngDialog.open({
name: 'receiveConsumptionsDialog',
template: 'app/statistics/consumption/consumption.list.receive.data.dialog.html',
className: 'ngdialog-theme-default',
controller: 'ReceiveConsumptionsController',
controllerAs: 'vmReceiveConsumptions',
scope: $scope,
resolve: {
initialData: ['$stateParams', 'StatisticsConsumptionsService', function($stateParams, StatisticsConsumptionsService) {
return StatisticsConsumptionsService.getConsumptionDataToReceive($stateParams.equipmentId);
}]
}
});
}
/**
* Performs the search according to the filters selected
*/
function search(form) {
if (form != null && !form.$valid) {
return;
}
// The front end page (Bootstrap pagination element) is one position ahead respect server pagination
if (vm.auxCurrentPage !== 0) {
vm.searchObject.page = vm.auxCurrentPage - 1;
} else {
vm.searchObject.page = vm.auxCurrentPage;
}
vm.searchObject.washer = vm.washerSelected.number;
vm.searchObject.formula = vm.formulaSelected.number;
vm.searchObject.pump = vm.pumpSelected.number;
StatisticsConsumptionsService.searchConsumptions(vm.searchObject)
.then(
function(response) {
vm.auxCurrentPage = response.data.number + 1; // The front end page (Bootstrap pagination element) is one position ahead respect server pagination
vm.searchObject.size = response.data.size;
vm.totalItems = response.data.totalElements;
vm.consumptions = response.data.content;
}
);
}
/**
* Opens the popup to select the start date
*/
function openStartDatePopup() {
vm.startDatePopupOpened = true;
}
/**
* Opens the popup to select the end date
*/
function openEndDatePopup() {
vm.endDatePopupOpened = true;
}
/**
* Performs the required actions when one washer is selected
*/
function updateAfterWasherSelection() {
vm.washerFormulas = [];
if (vm.washerSelected != null && vm.washerSelected.number > 0) {
vm.washerFormulas.push(vm.equipmentFormulas[0]);
}
for (var i = 0; i < vm.equipmentFormulas.length; i++) {
if (vm.equipmentFormulas[i].washerNumber === vm.washerSelected.number) {
vm.washerFormulas.push(vm.equipmentFormulas[i]);
}
}
vm.formulaSelected = vm.washerFormulas[0];
}
}
})();
视图:
<form name="consumptionSearchForm" ng-submit="vmStatisticsConsumptionsView.search(consumptionSearchForm)" novalidate>
<!-- Buttons -->
<div class="main-actions-bar">
<div class="hidden-xs">
<button class="btn btn-primary" type="submit" translate>consumption.list.search.button</button>
<button class="btn btn-danger" ng-disabled="!vmStatisticsConsumptionsView.security.admin && vmStatisticsConsumptionsView.security.statistics != CLIENT_PERMISSIONS.TOTAL" type="button" ng-click="vmStatisticsConsumptionsView.deleteWebDataConfirmDialog(consumptionSearchForm)" translate>consumption.list.delete.web.data.button</button>
<button class="btn btn-danger" ng-disabled="!vmStatisticsConsumptionsView.security.admin && vmStatisticsConsumptionsView.security.statistics != CLIENT_PERMISSIONS.TOTAL" type="button" ng-click="vmStatisticsConsumptionsView.deleteEquipmentDataConfirmDialog()" translate>consumption.list.delete.equipment.data.button</button>
<button class="btn btn-success" ng-disabled="!vmStatisticsConsumptionsView.security.admin && vmStatisticsConsumptionsView.security.statistics == CLIENT_PERMISSIONS.VIEW" type="button" ng-click="vmStatisticsConsumptionsView.receiveConsumptionsDialog()" translate>consumption.list.receive.consumptions.button</button>
<button class="btn btn-info" type="button" ng-click="vmStatisticsConsumptionsView.exportDataConfirmDialog(consumptionSearchForm)" translate>consumption.list.export.data.button</button>
</div>
<div class="dropdown visible-xs-block">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{{'generic.group.actions' | translate}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a ng-click="vmStatisticsConsumptionsView.search(consumptionSearchForm)" translate>consumption.list.search.button</a></li>
<li><a disable-link="!vmStatisticsConsumptionsView.security.admin && vmStatisticsConsumptionsView.security.statistics != CLIENT_PERMISSIONS.TOTAL" ng-click="vmStatisticsConsumptionsView.deleteWebDataConfirmDialog(consumptionSearchForm)" translate>consumption.list.delete.web.data.button</a></li>
<li><a disable-link="!vmStatisticsConsumptionsView.security.admin && vmStatisticsConsumptionsView.security.statistics != CLIENT_PERMISSIONS.TOTAL" ng-click="vmStatisticsConsumptionsView.deleteEquipmentDataConfirmDialog()" translate>consumption.list.delete.equipment.data.button</a></li>
<li><a disable-link="!vmStatisticsConsumptionsView.security.admin && vmStatisticsConsumptionsView.security.statistics == CLIENT_PERMISSIONS.VIEW" ng-click="vmStatisticsConsumptionsView.receiveConsumptionsDialog()" translate>consumption.list.receive.consumptions.button</a></li>
<li><a ng-click="vmStatisticsConsumptionsView.exportDataConfirmDialog(consumptionSearchForm)" translate>consumption.list.export.data.button</a></li>
</ul>
</div>
</div>
<div class="container-fluid">
<h3 translate>generic.search.title</h3>
<!-- Filters -->
<div class="row">
<!-- Start date -->
<div class="col-lg-3 col-sm-4 col-xs-12">
<div class="form-group">
<label for="startDatePicker" translate>generic.start.date</label>
<div class="input-group" ng-class="{'has-error': consumptionSearchForm.startDatePicker.$invalid && (consumptionSearchForm.startDatePicker.$dirty || consumptionSearchForm.$submitted)}">
<input type="text"
class="form-control"
id="startDatePicker"
name="startDatePicker"
uib-datepicker-popup="{{vmStatisticsConsumptionsView.dateFormat}}"
ng-model="vmStatisticsConsumptionsView.searchObject.startDate"
is-open="vmStatisticsConsumptionsView.startDatePopupOpened"
ng-required="true"
current-text="{{'date.picker.today' | translate}}"
clear-text="{{'date.picker.reset' | translate}}"
close-text="{{'date.picker.close' | translate}}"
alt-input-formats="['dd/MM/yyyy']"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="vmStatisticsConsumptionsView.openStartDatePopup()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
<div ng-if="consumptionSearchForm.$submitted || consumptionSearchForm.startDatePicker.$dirty" ng-messages="consumptionSearchForm.startDatePicker.$error" class="msg-has-error">
<div ng-message="required" translate>generic.required.field</div>
<div ng-message="date" translate>generic.date.field</div>
</div>
</div>
</div>
<!-- End date -->
<div class="col-lg-3 col-sm-4 col-xs-12">
<div class="form-group">
<label for="startDatePicker" translate>generic.final.date</label>
<div class="input-group" ng-class="{'has-error': consumptionSearchForm.endDatePicker.$invalid && (consumptionSearchForm.endDatePicker.$dirty || consumptionSearchForm.$submitted)}">
<input type="text"
class="form-control"
id="endDatePicker"
name="endDatePicker"
uib-datepicker-popup="{{vmStatisticsConsumptionsView.dateFormat}}"
ng-model="vmStatisticsConsumptionsView.searchObject.endDate"
is-open="vmStatisticsConsumptionsView.endDatePopupOpened"
ng-required="true"
current-text="{{'date.picker.today' | translate}}"
clear-text="{{'date.picker.reset' | translate}}"
close-text="{{'date.picker.close' | translate}}"
alt-input-formats="['dd/MM/yyyy']"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="vmStatisticsConsumptionsView.openEndDatePopup()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</div>
<div ng-if="consumptionSearchForm.$submitted || consumptionSearchForm.endDatePicker.$dirty" ng-messages="consumptionSearchForm.endDatePicker.$error" class="msg-has-error">
<div ng-message="required" translate>generic.required.field</div>
<div ng-message="date" translate>generic.date.field</div>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Washer selector -->
<div ng-if="!vmStatisticsConsumptionsView.isOrionEquipment" class="col-lg-3 col-sm-4 col-xs-12">
<div class="form-group">
<label for="washerSelect" translate>consumption.list.washer.selector</label>
<select name="washerSelect"
id="washerSelect"
class="form-control"
ng-model="vmStatisticsConsumptionsView.washerSelected"
ng-change="vmStatisticsConsumptionsView.updateAfterWasherSelection()"
ng-options="washerSelected.description for washerSelected in vmStatisticsConsumptionsView.washers">
</select>
</div>
</div>
<!-- Formula selector -->
<div class="col-lg-3 col-sm-4 col-xs-12">
<div class="form-group">
<label for="formulaSelect" translate>consumption.list.formula.selector</label>
<select name="formulaSelect"
id="formulaSelect"
class="form-control"
ng-model="vmStatisticsConsumptionsView.formulaSelected"
ng-options="formulaSelected.description for formulaSelected in vmStatisticsConsumptionsView.washerFormulas">
</select>
</div>
</div>
<!-- Pump selector -->
<div class="col-lg-3 col-sm-4 col-xs-12">
<div class="form-group">
<label for="pumpSelect" translate>consumption.list.pump.selector</label>
<select name="pumpSelect"
id="pumpSelect"
class="form-control"
ng-model="vmStatisticsConsumptionsView.pumpSelected"
ng-options="pumpSelected.description for pumpSelected in vmStatisticsConsumptionsView.pumps">
</select>
</div>
</div>
</div>
<!-- Table of consumptions -->
<h3 translate>consumption.list.table.title</h3>
<!-- Page size selector -->
<div class="form-group form-inline">
<label for="pageSizeSelect" translate>consumption.list.page.size.selector</label>
<select name="pageSizeSelect"
id="pageSizeSelect"
class="form-control"
ng-model="vmStatisticsConsumptionsView.searchObject.size"
ng-change="vmStatisticsConsumptionsView.search(consumptionSearchForm)"
ng-options="size for size in vmStatisticsConsumptionsView.pageSizeSelectorOptions">
</select>
</div>
<!-- Table with consumption results -->
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th translate>consumption.list.table.head.date</th>
<th translate>consumption.list.table.head.washer</th>
<th translate>consumption.list.table.head.formula</th>
<th translate>consumption.list.table.head.pump</th>
<th translate>consumption.list.table.head.executions</th>
<th translate>consumption.list.table.head.volume</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="consumption in vmStatisticsConsumptionsView.consumptions">
<td>{{consumption.date}}</td>
<td>{{consumption.washer}}</td>
<td>{{consumption.formula}}</td>
<td>{{consumption.pump}}</td>
<td>{{consumption.executions}}</td>
<td>{{consumption.volume}}</td>
</tr>
</tbody>
</table>
</div>
<!-- Number of results -->
<div>
<span>{{('generic.number.of.records' | translate) + ': ' + vmStatisticsConsumptionsView.totalItems}}</span>
</div>
<!-- Pagination -->
<!-- Big screen -->
<uib-pagination total-items="vmStatisticsConsumptionsView.totalItems"
ng-model="vmStatisticsConsumptionsView.auxCurrentPage"
ng-change="vmStatisticsConsumptionsView.search(consumptionSearchForm)"
class="pagination-sm hidden-xs"
items-per-page="vmStatisticsConsumptionsView.searchObject.size"
max-size="10"
force-ellipses="true"
previous-text="{{'pagination.previous.button' | translate}}"
next-text="{{'pagination.next.button' | translate}}">
</uib-pagination>
<!-- Small screen -->
<uib-pagination total-items="vmStatisticsConsumptionsView.totalItems"
ng-model="vmStatisticsConsumptionsView.auxCurrentPage"
ng-change="vmStatisticsConsumptionsView.search(consumptionSearchForm)"
class="pagination-sm visible-xs-block"
items-per-page="vmStatisticsConsumptionsView.searchObject.size"
max-size="3"
previous-text="{{'pagination.previous.button' | translate}}"
next-text="{{'pagination.next.button' | translate}}">
</uib-pagination>
</div>
</form>
嗯,这个完整的例子在使用滑动手势向前推进时没有引起任何闪烁,但现在出现了让我发疯的原因。看起来控制器或视图的任何微小变化都会使闪烁效果再次出现。
例如,只需删除激活功能的最后一行:vm.consumptions = initialData.data[10].content;
该行只是初始化视图表中显示的数据。因此,如果此数据在开头为空,为什么会导致这种闪烁效应呢?
更奇怪的是。现在让控制器不受影响,转到视图并简单地删除那里显示的表:
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th translate>consumption.list.table.head.date</th>
<th translate>consumption.list.table.head.washer</th>
<th translate>consumption.list.table.head.formula</th>
<th translate>consumption.list.table.head.pump</th>
<th translate>consumption.list.table.head.executions</th>
<th translate>consumption.list.table.head.volume</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="consumption in vmStatisticsConsumptionsView.consumptions">
<td>{{consumption.date}}</td>
<td>{{consumption.washer}}</td>
<td>{{consumption.formula}}</td>
<td>{{consumption.pump}}</td>
<td>{{consumption.executions}}</td>
<td>{{consumption.volume}}</td>
</tr>
</tbody>
</table>
</div>
闪烁又出现了!但不仅是表,保留表并删除其中一个组合框:
<!-- Formula selector -->
<div class="col-lg-3 col-sm-4 col-xs-12">
<div class="form-group">
<label for="formulaSelect" translate>consumption.list.formula.selector</label>
<select name="formulaSelect"
id="formulaSelect"
class="form-control"
ng-model="vmStatisticsConsumptionsView.formulaSelected"
ng-options="formulaSelected.description for formulaSelected in vmStatisticsConsumptionsView.washerFormulas">
</select>
</div>
</div>
闪烁的又一次。但不是全部,因为如果我用日期过滤器删除第一行,那么闪烁就不会出现(???):
你知道吗?我很感激任何评论,因为我完全迷失了,我想在iOS中摆脱这种影响。谢谢。