Angular:下一节显示在上一节之前隐藏 - 使用 ng-show
Angular: Next section shown before previous section hidden - using ng-show
我正在 angular based on this example 中实施可重复使用的 分步向导指令 。它工作得很好,但就像在示例中一样,我正在使用 ng-show 隐藏除当前步骤之外的所有步骤。每当我更改同时显示当前和下一步的步骤时,这会导致快速闪烁。 我想不通的是如何消除闪烁并确保在任何时候只显示一个步骤。
我试过的:
我自己解决这个问题的最初尝试是将 show/hide-mechanic 更改为使用 ng-switch 但它不能很好地工作,因为 ng-switch-when 指令只接受字符串(所以我不能自动填充它一个索引)。此外,ng-switch-when 通过转换工作意味着我将在单个元素上有 2 个转换指令,这实际上没有意义。
我目前实现的向导指令如下所示:
// Wizard
// ======
//
// This component implements a wizard-directive and a dependent step-directive
// which together can be used to define a step-by-step wizard with arbitrary
// html in each step.
//
// Ex:
// ```html
// <wizard>
// <step>
// <h1>Step one</h1>
// </step>
// <step>
// <h1>Step two</h1>
// </step>
// </wizard>
// ```
//
angular.module('wizard', [])
// Wizard Directive
// ----------------
//
// The main directive which defines the wizard element. A wizard can contain
// arbitrary html, but will only display a single step-element at a time. The
// directive also defines a couple of ways to navigate the wizard - through
// buttons and bottom "tabs".
//
.directive('wizard', function($rootScope) {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: $rootScope.templateBasePath + '/components/wizard/wizard.html',
controller: function($scope, $element) {
// Initialize the array of steps. This will be filled by any child
// steps added to the wizard.
var steps = $scope.steps = [];
// Search through the wizard to find what step is currently visible.
function getCurrentStepIndex() {
var index;
angular.forEach(steps, function(step, i) {
if (step.selected) {
index = i;
// End early when the selected step is found.
return;
}
});
return index;
}
// Make the imagePath available to the template.
$scope.imagePath = $rootScope.imagePath;
// Move to the next step in the wizard.
$scope.next = function () {
var index = getCurrentStepIndex();
if (index < steps.length - 1) {
steps[index].selected = false;
steps[index+1].selected = true;
}
};
// Move to the previous step of the wizard.
$scope.previous = function () {
var index = getCurrentStepIndex();
if (index > 0) {
steps[index].selected = false;
steps[index-1].selected = true;
}
};
// Select a given step in the wizard.
$scope.select = function(step) {
angular.forEach(steps, function(step) {
step.selected = false;
});
step.selected = true;
};
$scope.onFirstStep = function() {
return getCurrentStepIndex() === 0;
}
$scope.onLastStep = function() {
return getCurrentStepIndex() === steps.length - 1;
}
// Called by the step directive to add itself to the wizard.
this.addStep = function(step) {
// Select the first step when added.
if (steps.length === 0) {
$scope.select(step);
}
// Add the step to the step list.
steps.push(step);
};
}
};
})
// Step Directive
// --------------
//
// The Step Directive defines a section of code which constitues a distinct step
// in the overall goal of the wizard. The directive can only exist as a direct
// child of a wizard-tag.
//
.directive('step', function() {
return {
require: '^wizard', // require a wizard parent
restrict: 'E',
transclude: true,
scope: true,
template: '<div class="wizard__step ng-hide" ng-show="selected"></div>',
link: function(scope, element, attrs, wizardCtrl, transclude) {
// Add itself to the wizard's list of steps.
wizardCtrl.addStep(scope);
// Make the wizard scope available under "wizard" in the transcluded
// html scope.
scope.wizard = scope.$parent.$parent;
// Transclude the tag content in order to set the scope. This allows
// the content to access the wizard's next() and previous() functions.
var transDiv = angular.element(element).find('.wizard__step');
transclude(scope, function (clone) {
transDiv.append(clone);
});
}
};
});
相应的向导模板如下所示:
<div class="wizard" tabindex="1">
<div class="wizard__display">
<div class="wizard__previous" ng-click="previous()"><div class="guideBack" ng-hide="onFirstStep()"></div></div>
<div class="wizard__content" ng-transclude></div>
<div class="wizard__next" ng-click="next()"><div class="guideNext" ng-hide="onLastStep()"></div></div>
</div>
<ul class="nav wizard__tabs">
<li ng-repeat="step in steps" ng-click="select(step)" ng-class="{active:step.selected}"></li>
</ul>
</div>
我把所有的注意力都集中在让它与 ng-show 或 ng-switch 一起工作上,这给自己带来了不必要的困难。通过jqlite/jquery手动进行显示和隐藏,问题很快解决。经过一些重构后,向导代码如下所示:
// Wizard
// ======
//
// This component implements a wizard-directive and a dependent step-directive
// which together can be used to define a step-by-step wizard with arbitrary
// html in each step.
//
// Ex:
// ```html
// <wizard>
// <step>
// <h1>Step one</h1>
// </step>
// <step>
// <h1>Step two</h1>
// </step>
// </wizard>
// ```
//
angular.module('ssbbtip.wizard', [])
// Wizard Directive
// ----------------
//
// The main directive which defines the wizard element. A wizard can contain
// arbitrary html, but will only display a single step-element at a time. The
// directive also defines a couple of ways to navigate the wizard - through
// buttons, bottom breadcrumbs and keyboard arrow keys.
//
.directive('wizard', function($rootScope) {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: $rootScope.templateBasePath + '/components/wizard/wizard.html',
controller: function($scope, $element) {
// Initialize the array of steps. This will be filled by any child
// steps added to the wizard.
var steps = $scope.steps = [],
// currentStep is the shadow variable supporting the
// `$scope.currentStep` property.
currentStep = 0;
// This utility function will adjust a value to fit inside the
// specified range inclusively.
function clampToRange(min, max, value) {
// Make sure the max is at least as big as the min.
max = (min > max) ? min : max;
if (value < min) {
return min;
} else if (value > max) {
return max;
} else {
return value;
}
}
// This property specifies the currently visible step in the wizard.
Object.defineProperty($scope, 'currentStep', {
enumerable: true,
configurable: false,
get: function () { return currentStep; },
set: function (value) {
if (value && typeof(value) === 'number') {
currentStep = clampToRange(0, steps.length-1, value);
} else {
currentStep = 0;
}
}
});
// Make the imagePath available to the template.
$scope.imagePath = $rootScope.imagePath;
// Handle keyboard events on the wizard to allow navigation by
// keyboard arrows.
$scope.onKeydown = function (event) {
event.preventDefault();
console.log(event);
switch (event.which) {
case 37: // left arrow
case 38: // up arrow
$scope.previous();
break;
case 39: // right arrow
case 40: // down arrow
case 32: // space bar
$scope.next();
break;
}
};
// Move to the next step in the wizard.
$scope.next = function () {
$scope.currentStep = $scope.currentStep + 1;
};
// Move to the previous step of the wizard.
$scope.previous = function () {
$scope.currentStep = $scope.currentStep - 1;
};
$scope.onFirstStep = function() {
return $scope.currentStep === 0;
};
$scope.onLastStep = function() {
return $scope.currentStep === steps.length - 1;
};
// Called by the step directive to add itself to the wizard.
this.addStep = function (step) {
steps.push(step);
};
// This watches the `$scope.currentStep` property and updates the UI
// accordingly.
$scope.$watch(function () {
return $scope.currentStep;
}, function (newValue, oldValue) {
$element.find('step .wizard__step').eq(oldValue).addClass('ng-hide');
$element.find('step .wizard__step').eq(newValue).removeClass('ng-hide');
});
}
};
})
// Step Directive
// --------------
//
// The Step Directive defines a section of code which constitues a distinct step
// in the overall goal of the wizard. The directive can only exist as a direct
// child of a wizard-tag.
//
.directive('step', function() {
return {
require: '^wizard', // require a wizard parent
restrict: 'E',
transclude: true,
scope: true,
template: '<div class="wizard__step ng-hide"></div>',
link: function(scope, element, attrs, wizardCtrl, transclude) {
// Add itself to the wizard's list of steps.
wizardCtrl.addStep(scope);
// Make the wizard scope available under "wizard" in the transcluded
// html scope.
scope.wizard = scope.$parent.$parent;
// Transclude the tag content manually in order to set the scope.
// This allows the content to access the `wizard.next()` and
// `wizard.previous()` functions.
var transDiv = angular.element(element).find('.wizard__step');
transclude(scope, function (clone) {
transDiv.append(clone);
});
}
};
});
和模板:
<div class="wizard" tabindex="1" ng-keydown="onKeydown($event)">
<!-- tabindex 1 is needed to make the div selectable in order to capture keydown -->
<div class="wizard__display">
<div class="wizard__previous" ng-click="previous()"><div class="guideBack" ng-hide="onFirstStep()"></div></div>
<div class="wizard__content" ng-transclude></div>
<div class="wizard__next" ng-click="next()"><div class="guideNext" ng-hide="onLastStep()"></div></div>
</div>
<ul class="nav wizard__tabs">
<li ng-repeat="step in steps track by $index" ng-click="currentStep = $index" ng-class="{active: currentStep === $index}"></li>
</ul>
</div>
我正在 angular based on this example 中实施可重复使用的 分步向导指令 。它工作得很好,但就像在示例中一样,我正在使用 ng-show 隐藏除当前步骤之外的所有步骤。每当我更改同时显示当前和下一步的步骤时,这会导致快速闪烁。 我想不通的是如何消除闪烁并确保在任何时候只显示一个步骤。
我试过的: 我自己解决这个问题的最初尝试是将 show/hide-mechanic 更改为使用 ng-switch 但它不能很好地工作,因为 ng-switch-when 指令只接受字符串(所以我不能自动填充它一个索引)。此外,ng-switch-when 通过转换工作意味着我将在单个元素上有 2 个转换指令,这实际上没有意义。
我目前实现的向导指令如下所示:
// Wizard
// ======
//
// This component implements a wizard-directive and a dependent step-directive
// which together can be used to define a step-by-step wizard with arbitrary
// html in each step.
//
// Ex:
// ```html
// <wizard>
// <step>
// <h1>Step one</h1>
// </step>
// <step>
// <h1>Step two</h1>
// </step>
// </wizard>
// ```
//
angular.module('wizard', [])
// Wizard Directive
// ----------------
//
// The main directive which defines the wizard element. A wizard can contain
// arbitrary html, but will only display a single step-element at a time. The
// directive also defines a couple of ways to navigate the wizard - through
// buttons and bottom "tabs".
//
.directive('wizard', function($rootScope) {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: $rootScope.templateBasePath + '/components/wizard/wizard.html',
controller: function($scope, $element) {
// Initialize the array of steps. This will be filled by any child
// steps added to the wizard.
var steps = $scope.steps = [];
// Search through the wizard to find what step is currently visible.
function getCurrentStepIndex() {
var index;
angular.forEach(steps, function(step, i) {
if (step.selected) {
index = i;
// End early when the selected step is found.
return;
}
});
return index;
}
// Make the imagePath available to the template.
$scope.imagePath = $rootScope.imagePath;
// Move to the next step in the wizard.
$scope.next = function () {
var index = getCurrentStepIndex();
if (index < steps.length - 1) {
steps[index].selected = false;
steps[index+1].selected = true;
}
};
// Move to the previous step of the wizard.
$scope.previous = function () {
var index = getCurrentStepIndex();
if (index > 0) {
steps[index].selected = false;
steps[index-1].selected = true;
}
};
// Select a given step in the wizard.
$scope.select = function(step) {
angular.forEach(steps, function(step) {
step.selected = false;
});
step.selected = true;
};
$scope.onFirstStep = function() {
return getCurrentStepIndex() === 0;
}
$scope.onLastStep = function() {
return getCurrentStepIndex() === steps.length - 1;
}
// Called by the step directive to add itself to the wizard.
this.addStep = function(step) {
// Select the first step when added.
if (steps.length === 0) {
$scope.select(step);
}
// Add the step to the step list.
steps.push(step);
};
}
};
})
// Step Directive
// --------------
//
// The Step Directive defines a section of code which constitues a distinct step
// in the overall goal of the wizard. The directive can only exist as a direct
// child of a wizard-tag.
//
.directive('step', function() {
return {
require: '^wizard', // require a wizard parent
restrict: 'E',
transclude: true,
scope: true,
template: '<div class="wizard__step ng-hide" ng-show="selected"></div>',
link: function(scope, element, attrs, wizardCtrl, transclude) {
// Add itself to the wizard's list of steps.
wizardCtrl.addStep(scope);
// Make the wizard scope available under "wizard" in the transcluded
// html scope.
scope.wizard = scope.$parent.$parent;
// Transclude the tag content in order to set the scope. This allows
// the content to access the wizard's next() and previous() functions.
var transDiv = angular.element(element).find('.wizard__step');
transclude(scope, function (clone) {
transDiv.append(clone);
});
}
};
});
相应的向导模板如下所示:
<div class="wizard" tabindex="1">
<div class="wizard__display">
<div class="wizard__previous" ng-click="previous()"><div class="guideBack" ng-hide="onFirstStep()"></div></div>
<div class="wizard__content" ng-transclude></div>
<div class="wizard__next" ng-click="next()"><div class="guideNext" ng-hide="onLastStep()"></div></div>
</div>
<ul class="nav wizard__tabs">
<li ng-repeat="step in steps" ng-click="select(step)" ng-class="{active:step.selected}"></li>
</ul>
</div>
我把所有的注意力都集中在让它与 ng-show 或 ng-switch 一起工作上,这给自己带来了不必要的困难。通过jqlite/jquery手动进行显示和隐藏,问题很快解决。经过一些重构后,向导代码如下所示:
// Wizard
// ======
//
// This component implements a wizard-directive and a dependent step-directive
// which together can be used to define a step-by-step wizard with arbitrary
// html in each step.
//
// Ex:
// ```html
// <wizard>
// <step>
// <h1>Step one</h1>
// </step>
// <step>
// <h1>Step two</h1>
// </step>
// </wizard>
// ```
//
angular.module('ssbbtip.wizard', [])
// Wizard Directive
// ----------------
//
// The main directive which defines the wizard element. A wizard can contain
// arbitrary html, but will only display a single step-element at a time. The
// directive also defines a couple of ways to navigate the wizard - through
// buttons, bottom breadcrumbs and keyboard arrow keys.
//
.directive('wizard', function($rootScope) {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: $rootScope.templateBasePath + '/components/wizard/wizard.html',
controller: function($scope, $element) {
// Initialize the array of steps. This will be filled by any child
// steps added to the wizard.
var steps = $scope.steps = [],
// currentStep is the shadow variable supporting the
// `$scope.currentStep` property.
currentStep = 0;
// This utility function will adjust a value to fit inside the
// specified range inclusively.
function clampToRange(min, max, value) {
// Make sure the max is at least as big as the min.
max = (min > max) ? min : max;
if (value < min) {
return min;
} else if (value > max) {
return max;
} else {
return value;
}
}
// This property specifies the currently visible step in the wizard.
Object.defineProperty($scope, 'currentStep', {
enumerable: true,
configurable: false,
get: function () { return currentStep; },
set: function (value) {
if (value && typeof(value) === 'number') {
currentStep = clampToRange(0, steps.length-1, value);
} else {
currentStep = 0;
}
}
});
// Make the imagePath available to the template.
$scope.imagePath = $rootScope.imagePath;
// Handle keyboard events on the wizard to allow navigation by
// keyboard arrows.
$scope.onKeydown = function (event) {
event.preventDefault();
console.log(event);
switch (event.which) {
case 37: // left arrow
case 38: // up arrow
$scope.previous();
break;
case 39: // right arrow
case 40: // down arrow
case 32: // space bar
$scope.next();
break;
}
};
// Move to the next step in the wizard.
$scope.next = function () {
$scope.currentStep = $scope.currentStep + 1;
};
// Move to the previous step of the wizard.
$scope.previous = function () {
$scope.currentStep = $scope.currentStep - 1;
};
$scope.onFirstStep = function() {
return $scope.currentStep === 0;
};
$scope.onLastStep = function() {
return $scope.currentStep === steps.length - 1;
};
// Called by the step directive to add itself to the wizard.
this.addStep = function (step) {
steps.push(step);
};
// This watches the `$scope.currentStep` property and updates the UI
// accordingly.
$scope.$watch(function () {
return $scope.currentStep;
}, function (newValue, oldValue) {
$element.find('step .wizard__step').eq(oldValue).addClass('ng-hide');
$element.find('step .wizard__step').eq(newValue).removeClass('ng-hide');
});
}
};
})
// Step Directive
// --------------
//
// The Step Directive defines a section of code which constitues a distinct step
// in the overall goal of the wizard. The directive can only exist as a direct
// child of a wizard-tag.
//
.directive('step', function() {
return {
require: '^wizard', // require a wizard parent
restrict: 'E',
transclude: true,
scope: true,
template: '<div class="wizard__step ng-hide"></div>',
link: function(scope, element, attrs, wizardCtrl, transclude) {
// Add itself to the wizard's list of steps.
wizardCtrl.addStep(scope);
// Make the wizard scope available under "wizard" in the transcluded
// html scope.
scope.wizard = scope.$parent.$parent;
// Transclude the tag content manually in order to set the scope.
// This allows the content to access the `wizard.next()` and
// `wizard.previous()` functions.
var transDiv = angular.element(element).find('.wizard__step');
transclude(scope, function (clone) {
transDiv.append(clone);
});
}
};
});
和模板:
<div class="wizard" tabindex="1" ng-keydown="onKeydown($event)">
<!-- tabindex 1 is needed to make the div selectable in order to capture keydown -->
<div class="wizard__display">
<div class="wizard__previous" ng-click="previous()"><div class="guideBack" ng-hide="onFirstStep()"></div></div>
<div class="wizard__content" ng-transclude></div>
<div class="wizard__next" ng-click="next()"><div class="guideNext" ng-hide="onLastStep()"></div></div>
</div>
<ul class="nav wizard__tabs">
<li ng-repeat="step in steps track by $index" ng-click="currentStep = $index" ng-class="{active: currentStep === $index}"></li>
</ul>
</div>