我刚刚开始使用Angular,并且正试图绕过适当的指令使用。我正在编写一个自定义指令,它接受一个对象数组并将其解析为可变数量的垂直div。它基本上是一个网格系统,其中元素被排列成堆叠的垂直列而不是行。 div的数量随着屏幕的宽度而动态变化,需要在div类中进行动态更改,并在页面调整大小时重建div列中数组元素的顺序。
当我将模板的内容用作普通的静态HTML时,一切都加载得很好。当您使用输入字段等时,过滤器会动态更改数据集
当我使用我的指令时,初始页面加载看起来很好。但是,动态过滤被破坏 - 它不再绑定到输入字段。更重要的是,在页面调整大小时,HTML根本无法编译,在DOM中留下空白屏幕和未编译的指令标记。
我不太了解Angular,可以解决这个问题。如果我不得不猜测,由于范围问题,听起来好像没有在页面$ compile上正确绑定。
注意:我知道为模板做字符串连接是不好的做法,但我只想在开始搞乱嵌套指令之前让事情有效。
编辑:这里是我前端代码的Github回购链接:https://github.com/danheidel/education-video.net/tree/master/site
HTML
<body ng-controller="channelListController">
Creator: <input ng-model="query.creators">
Tags: <input ng-model="query.tags">
query: {{query}}
<div id="channel-view">
<channel-drawers channels="channels"></channel-drawers>
</div>
</body>
JS
.controller('channelListController', function ($scope, $http){
$http.get('api/v1/channels').success(function(data){
$scope.channels = data;
});
})
.directive('channelDrawers', function($window, $compile){
return{
restrict: 'E',
replace: true,
scope: {
channels: '='
},
controller: 'channelListController',
//templateUrl: 'drawer.html',
link: function(scope, element, attr){
scope.breakpoints = [
{width: 0, columns: 1},
{width: 510, columns: 2},
{width: 850, columns: 3},
{width: 1190, columns: 4},
{width: 1530, columns: 5}
];
angular.element($window).bind('resize', setWindowSize);
setWindowSize(); //call on init
function setWindowSize(){
scope.windowSize = $window.innerWidth;
console.log(scope.windowSize);
_.forEach(scope.breakpoints, function(point){
if(point.width <= scope.windowSize){
scope.columns = point.columns;
}
});
var tempHtml = '';
for(var rep=0;rep<scope.columns;rep++){
tempHtml +=
'<div class="cabinet' + scope.columns + '">' +
'<div class="drawer" ng-class="{' + ((rep%2 === 0)?'even: $even, odd: $odd':'even: $odd, odd:$even') + '}" ng-repeat="channel in channels | looseCreatorComparator : query.creators | looseTagComparator : query.tags | modulusFilter : ' + scope.columns + ' : ' + rep + '">' +
'<ng-include src="\'drawer.html\'"></ng-include>' +
'</div>' +
'</div>';
}
console.log(tempHtml);
element.html(tempHtml);
$compile(element.contents())(scope);
}
}
};
})
答案 0 :(得分:1)
当您想要操作模板时,应该实现$ compile函数。当您想要将模板绑定到范围和/或设置任何观察者时,应实现链接功能。如果您有动态HTML,并将其插入到DOM中,请向自己询问这些问题:
您要修改模板吗?如果是,则创建一个元素模板(angular.element(...))并将其附加到元素参数。
您是否修改了模板(步骤1)并且您的模板包含应该从其他模板绑定的绑定表达式,插值表达式和/或属性?如果是这样,您需要编译并链接从步骤1创建的元素。
以下是一个例子:
.directive('myDirective',function($compile) {
restrict: 'E',
scope: '=',
compile: function(element, attr) {
// manipulating template?
var e = angular.element('<div ng-model="person">{{person.name}}</div>');
element.append(e);
// the following is your linking function
return function(scope, element, attr) {
// template contains binding expressions? Yes
$compile(e)(scope);
};
}
});
要修复代码,请尝试将模板操作移动到编译函数,并在链接函数中调用$ compile(e)(范围)。
答案 1 :(得分:1)
首先,感谢pixelbits和pfooti的输入。它让我走上正轨。但是,我希望答案能够成为一个清白的答案,因为我们的讨论涉及的技术问题最终与实际答案相关。
基本上,这个问题框架很差。在做了更多阅读之后,很明显我正在使用Angular元素,而这些元素并非真正用于它们。
在这种情况下,我在我的指令中做了一堆模型操作,它确实应该在控制器中发生。我最终这样做,并将窗口调整大小处理程序代码移动到另一个组件。
现在,我甚至不需要指令来正确格式化我的数据。一些带有ng-class和ng-style风格的嵌套ng-repeats在2行HTML中完成了这项工作。
<div ng-repeat="modColumn in splitChannels"
ng-class="{'col-even' : !$even, 'col-odd' : !$odd}"
ng-style="{ width: 99 / windowAttr.columns + '%' }"
class="cabinet">
<div ng-repeat="channel in modColumn"
ng-class="{'row-even' : !$even, 'row-odd' : !$odd}"
ng-cloak
class="panel roundnborder">
<ng-include src="'panel.html'"></ng-include>
</div>
</div>
如果我能从一位Angular初学者那里得到一些建议,那就是:如果你的代码越来越复杂,或者你正在深入研究内部事物,请退后一步,重新思考你是如何使用角度组件。您可能正在执行应该在另一个组件类中执行的操作。正确的角度代码往往非常简洁,模块化和简单。
答案 2 :(得分:0)
您是否有理由使用模板进行此操作?
您是否可以将列数绑定到范围内的变量?我已经做了类似的事情,只是摆弄ng-if指令来隐藏现在不重要的东西,或者将一般布局内容附加到当前作用域(我通常将其全部填入属性中$ scope.view)
还有很多这样的东西已经在css3的媒体选择器中运行了,根本不需要弄乱DOM。如果没有更清楚地了解你想要完成的事情,我不确定这是否是超级必要的。不止一种皮肤猫等方法。
否则,@ pixelbits是正确的 - 如果你直接摆弄DOM树,那需要在编译中进行 - 进入DOM的值进入链接。