上周开始使用Angular,阅读/观看了许多教程,我正在尝试构建新闻源类型的应用程序。
这是瘦的:我有一个从服务器获取数据的服务。在新闻源本身,我有两个控制器:一个在其范围内拥有整个新闻源,另一个在每个新闻源文章中都有一个实例。如果用户单击单个帖子上的图标,则应调用已注入两个控制器的服务,然后广播主控制器选择的消息。然后,主控制器更新过滤器中的变量,根据用户的选择过滤新闻源内容。
问题在于:除了主控制器不更新HTML中的绑定变量外,一切正常。我已经阅读了关于ng-repeat和相关斗争中的双向绑定的每篇SO文章,但在我的情况下,绑定变量超出了ng-repeat,因此我发布了。
代码:
services.factory('filterService', function() {
var filterService = {};
filterService.filterKey = '';
filterService.getFilter = function() {
return filterService.filterKey;
};
filterService.setFilter = function(name) {
filterService.filterKey = name;
$rootScope.$broadcast('changeFilter');
};
return filterService;
});
app.controller('CommentCtrl', function($scope, $timeout, $http, filterService) {
$scope.setSearchParam = function(param) {
alert('clicked: ' + param)
filterService.setFilter(param);
}
app.controller('FeedCtrl', function($scope, articles, filterService, $timeout) {
$scope.articles = articles;
$scope.model = {
value: ''
};
$scope.$on('changeFilter', function() {
console.log(filterService.filterKey);
$scope.model.value = filterService.filterKey
}
});
});
<div class="articles">
<div class="articleStub" ng-repeat="article in articles|filter:model.value">
<div ng-controller="CommentCtrl">
<div class="{{article.sort}}">
<div class="leftBlock">
<a href="#" ng-click="setSearchParam(article.sort)">
<div class="typeIcon">
<i ng-class="{'icon-comments':article.question, 'icon-star':article.create, 'icon-ok-sign':article.notype}"></i>
</div>
</a>
注意:在app.config $ routeprovider函数中调用FeedCtrl控制器,无论它叫什么
编辑添加:警报和控制台检查两者都有效,所以我假设问题不在filterService或CommentCtrl中。
答案 0 :(得分:2)
我正在添加另一个答案,因为另一个仍然有效,但不是唯一的问题!
看了你的代码,你的问题有两个:
您有href="#"
这导致路由代码重新运行,并且它在同一页面上创建了一个新的控制器实例,但使用的是不同的范围。我发现这一点的方法是将调试行console.log("running controller init code for $scope.$id:" + $scope.$id);
添加到script.js
的{{1}}空白行下面。model.value
。您会注意到它在每次点击时都会运行,并且范围的$id
每次都不同。我不完全理解之后发生的事情,但是让两个相同的控制器照看页面的相同位置不是一件好事!
因此,考虑到这一点,我设置了href=""
。这有点破坏了按钮的渲染,但它确实解决了实例化多个控制器的问题。但是,这不能解决问题......另一个问题是什么?
angular.element.bind('click',....)正在“在角度世界之外”运行
这个有点复杂,但基本上对于角度数据绑定起作用,当scope
发生变化时,角度需要知道。大多数情况下,它通过角度函数(例如内部控制器,内部ng-*
指令等)自动处理,但在某些情况下,当从浏览器触发事件时(例如XHR,点击,触摸等) ,你必须告诉角度有些变化。您可以使用$scope.$apply()
执行此操作。有一些关于这个主题的好文章,所以我建议稍微阅读一下(先试试here)。
有两个解决方案 - 一个是使用ng-click
指令,它将click
事件包装在$scope.$apply
中(并且还有一个额外的优点,即你的标记更具语义性)或者另一种是自己做。为了尽量减少对代码的更改,我将click
代码包裹在scope.$apply
中:
element.bind('click', function() {
// tell angular that it needs to 'digest' the changes you're about to make.
scope.$apply(function(){
var param = scope.article.sort;
filterService.setFilter(param);
})
});
以下是您的代码的有效版本:http://plnkr.co/edit/X1AK0Bc4NZyChrJEknkN?p=preview
注意我还在列表中设置了一个过滤器。当没有设置过滤器时,您可以轻松地通过按钮清除隐藏的按钮:
<button ng-click="model.value=''" ng-show="model.value">Clear filter</button>
希望这会有所帮助:)
答案 1 :(得分:0)
我实际上认为问题不是你的model.value
没有得到更新 - 所有代码看起来都很好。
我认为问题在于你的filter
。
<div class="articleStub" ng-repeat="article in articles|filter:model.value">
此过滤器会将任何对象与包含model.value
的任何字段进行匹配。你真正想做的是以下几点:
<div class="articleStub"
ng-repeat="article in articles|filter:{sort: model.value}:true">
指定您只想匹配每篇文章的sort
属性。最终的true
参数意味着它也只允许严格匹配,因此ed
与edward
不匹配。
请注意,| filter:{sort: model.value}:true
是一个角度表达式,:
就像JavaScript逗号。如果你想象它在JavaScript中更像是:|('filter',{sort:model.value}, true)
其中|
是一个特殊的'在这里注入过滤器'功能..
修改强>:
我发现很难调试你的例子而没有在我面前的工作代码。如果你可以把它变成一个plunker我可以帮助更多,但与此同时,我认为你应该尝试通过使用不同的方法使你的代码更简单。
我创建了a plunker,它显示了按您点击的项目过滤列表的简便方法。我使用的代码非常少,所以希望它很容易理解?
我还建议您将Feed项目改为directive
。这些指令可以有自己的控制器,这样就可以防止你不得不重复ng-controller
。