Angular ng-show 不连续检查条件?
Angular ng-show does not check the condition continuously?
我有一个 ng-repeat 指令,我在其中显示所有对象(想法)。如果想法描述(字符串)比 x 长,我只想显示前 x 个字符和 "show all" link。用户可以点击这个link,整个文本就会显示出来。但是一次只能显示一个想法及其整个文本。
我现在有这个:
div(ng-show = "idea.description.length > maxIdeaDescLength && openLongIdea != idea._id")
i {{idea.description.substring(0, maxIdeaDescLength) }} ...
a(href='', ng-click='openLongIdea = idea._id') show all
div(ng-show = "idea.description.length <= maxIdeaDescLength || openLongIdea == idea._id")
i {{idea.description}}
这是我的控制器的一部分:
$scope.openLongIdea = 0;
所以当我点击全部显示时 link,ideaID 将被保存到变量 openLongIdea 中。由于我的 ng-show 条件,我希望仅当 idea-ID 与 openLongIdea-ID 匹配时才显示整个描述。但我仍然一次看到不止一个想法,它们的描述很长。
第一次展示想法时,我的逻辑是可行的。当我点击 show all link 时,将显示较长的文本。但是当我点击另一个想法的查看全部link时,它也会在旧想法旁边显示为一个整体,虽然我用新的想法ID覆盖了openLongIdea中的值。
这里有什么问题?
如评论中所述,它看起来像是绑定到基元的问题。在 JavaScript 中,基元(布尔值、数字、字符串)是不可变的,因此当您更改一个时,例如您的数字 maxIdeaDescLength
,先前的实例将被丢弃并使用一个新实例。这会破坏 Angular 的双向绑定,maxIdeaDescLength
的任何其他用法都不会更新为新值。
您可以通过在 $scope
上的对象上使 openLongIdea
成为 属性 来解决这个问题,例如$scope.data.openLongIdea
。在这种情况下,即使 maxIdeaDescLength
更改,$scope
也会引用 data
,因此可以访问 maxIdeaDescLength
的更新值。
但是,请考虑切换到 controllerAs 视图语法。来自 John Papa's style guide,以及其他参数:
It promotes the use of binding to a "dotted" object in the View (e.g.
customer.name instead of name), which is more contextual, easier to
read, and avoids any reference issues that may occur without
"dotting".
更新控制器:
var app = angular.module("app", []);
app.controller('ctrl', function () {
var vm = this;
vm.maxIdeaDescLength = 10;
vm.ideas = [
{_id : 0, description :'abcd efgh ijkl'},
{_id : 1, description :'qwer tyui opzx'}
];
});
示例视图:
<div ng-app="app" ng-controller="ctrl as vm">
<div ng-repeat="idea in vm.ideas">
<div ng-show = "idea.description.length > vm.maxIdeaDescLength && vm.openLongIdea !== idea._id">
{{idea.description.substring(0, vm.maxIdeaDescLength) }}
<a href='' ng-click='vm.openLongIdea = idea._id'> show all </a>
</div>
<div ng-show = "idea.description.length <= vm.maxIdeaDescLength || vm.openLongIdea === idea._id">
{{idea.description}}
</div>
</div>
</div>
我会为此创建一个自定义过滤器指令,因为它将更具可读性。另一个答案中controllerAs
的推荐非常好我在演示中错过了这一点。
请查看下面或此 fiddle 中的演示。
Collapsing/Expanding在演示中可以通过点击段落来实现。
如果您切换每个想法,该指令可以在某一点上得到改进,如果当前 globalDisplay
不匹配,您可能需要单击 show/hide all
按钮两次。
对于'dot.rule'请看这个SO question。
angular.module('demoApp', [])
.constant('appConst', {
maxIdeaDescLength: 50,
expandedDefault: false
})
.filter('descLimit', descLimitFilter)
.controller('MainController', MainController);
function descLimitFilter($filter) {
return function(input, len, expanded, ellipsesChars) {
var ellipses = ellipsesChars || '...';
return expanded? input : $filter('limitTo')(input, len) + ellipses;
}
}
function MainController($scope, $http, appConst) {
$scope.maxIdeaDescLength = appConst.maxIdeaDescLength;
$scope.toggle = toggle;
$scope.toggleAll = toggleAll;
var globalDisplay = appConst.expandedDefault; // hide all by default;
activate();
function activate() {
$http.get('https://demo5147591.mockable.io/ideas')
.then(function(response) {
$scope.ideas = response.data;
//console.log(response.data);
setExpandedAll($scope.ideas, globalDisplay);
});
}
function toggle(item) {
item.expanded = !item.expanded;
}
function toggleAll(items) {
globalDisplay = !globalDisplay;
setExpandedAll(items, globalDisplay);
}
function setExpandedAll(items, state) {
angular.forEach(items, function(item) {
item.expanded = state;
});
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp" ng-controller="MainController">
<button ng-click="toggleAll(ideas)">show/hide descriptions</button>
<div>
<div ng-repeat="idea in ideas">
<h2>{{idea.title}}</h2>
<p ng-click="toggle(idea)">{{idea.description | descLimit: maxIdeaDescLength : idea.expanded}}</p>
</div>
</div>
</div>
我有一个 ng-repeat 指令,我在其中显示所有对象(想法)。如果想法描述(字符串)比 x 长,我只想显示前 x 个字符和 "show all" link。用户可以点击这个link,整个文本就会显示出来。但是一次只能显示一个想法及其整个文本。
我现在有这个:
div(ng-show = "idea.description.length > maxIdeaDescLength && openLongIdea != idea._id")
i {{idea.description.substring(0, maxIdeaDescLength) }} ...
a(href='', ng-click='openLongIdea = idea._id') show all
div(ng-show = "idea.description.length <= maxIdeaDescLength || openLongIdea == idea._id")
i {{idea.description}}
这是我的控制器的一部分:
$scope.openLongIdea = 0;
所以当我点击全部显示时 link,ideaID 将被保存到变量 openLongIdea 中。由于我的 ng-show 条件,我希望仅当 idea-ID 与 openLongIdea-ID 匹配时才显示整个描述。但我仍然一次看到不止一个想法,它们的描述很长。
第一次展示想法时,我的逻辑是可行的。当我点击 show all link 时,将显示较长的文本。但是当我点击另一个想法的查看全部link时,它也会在旧想法旁边显示为一个整体,虽然我用新的想法ID覆盖了openLongIdea中的值。
这里有什么问题?
如评论中所述,它看起来像是绑定到基元的问题。在 JavaScript 中,基元(布尔值、数字、字符串)是不可变的,因此当您更改一个时,例如您的数字 maxIdeaDescLength
,先前的实例将被丢弃并使用一个新实例。这会破坏 Angular 的双向绑定,maxIdeaDescLength
的任何其他用法都不会更新为新值。
您可以通过在 $scope
上的对象上使 openLongIdea
成为 属性 来解决这个问题,例如$scope.data.openLongIdea
。在这种情况下,即使 maxIdeaDescLength
更改,$scope
也会引用 data
,因此可以访问 maxIdeaDescLength
的更新值。
但是,请考虑切换到 controllerAs 视图语法。来自 John Papa's style guide,以及其他参数:
It promotes the use of binding to a "dotted" object in the View (e.g. customer.name instead of name), which is more contextual, easier to read, and avoids any reference issues that may occur without "dotting".
更新控制器:
var app = angular.module("app", []);
app.controller('ctrl', function () {
var vm = this;
vm.maxIdeaDescLength = 10;
vm.ideas = [
{_id : 0, description :'abcd efgh ijkl'},
{_id : 1, description :'qwer tyui opzx'}
];
});
示例视图:
<div ng-app="app" ng-controller="ctrl as vm">
<div ng-repeat="idea in vm.ideas">
<div ng-show = "idea.description.length > vm.maxIdeaDescLength && vm.openLongIdea !== idea._id">
{{idea.description.substring(0, vm.maxIdeaDescLength) }}
<a href='' ng-click='vm.openLongIdea = idea._id'> show all </a>
</div>
<div ng-show = "idea.description.length <= vm.maxIdeaDescLength || vm.openLongIdea === idea._id">
{{idea.description}}
</div>
</div>
</div>
我会为此创建一个自定义过滤器指令,因为它将更具可读性。另一个答案中controllerAs
的推荐非常好我在演示中错过了这一点。
请查看下面或此 fiddle 中的演示。
Collapsing/Expanding在演示中可以通过点击段落来实现。
如果您切换每个想法,该指令可以在某一点上得到改进,如果当前 globalDisplay
不匹配,您可能需要单击 show/hide all
按钮两次。
对于'dot.rule'请看这个SO question。
angular.module('demoApp', [])
.constant('appConst', {
maxIdeaDescLength: 50,
expandedDefault: false
})
.filter('descLimit', descLimitFilter)
.controller('MainController', MainController);
function descLimitFilter($filter) {
return function(input, len, expanded, ellipsesChars) {
var ellipses = ellipsesChars || '...';
return expanded? input : $filter('limitTo')(input, len) + ellipses;
}
}
function MainController($scope, $http, appConst) {
$scope.maxIdeaDescLength = appConst.maxIdeaDescLength;
$scope.toggle = toggle;
$scope.toggleAll = toggleAll;
var globalDisplay = appConst.expandedDefault; // hide all by default;
activate();
function activate() {
$http.get('https://demo5147591.mockable.io/ideas')
.then(function(response) {
$scope.ideas = response.data;
//console.log(response.data);
setExpandedAll($scope.ideas, globalDisplay);
});
}
function toggle(item) {
item.expanded = !item.expanded;
}
function toggleAll(items) {
globalDisplay = !globalDisplay;
setExpandedAll(items, globalDisplay);
}
function setExpandedAll(items, state) {
angular.forEach(items, function(item) {
item.expanded = state;
});
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp" ng-controller="MainController">
<button ng-click="toggleAll(ideas)">show/hide descriptions</button>
<div>
<div ng-repeat="idea in ideas">
<h2>{{idea.title}}</h2>
<p ng-click="toggle(idea)">{{idea.description | descLimit: maxIdeaDescLength : idea.expanded}}</p>
</div>
</div>
</div>