Я написал пару директив, которые предназначены для увеличения одного из нескольких элементов до большего размера при щелчке элемента. При нажатии на увеличенный элемент следует вернуть его в нормальный размер. Когда элемент масштабируется, его схожие элементы (они все диаграммы d3) становятся невидимыми. Они восстанавливаются до видимости, когда расширенный элемент не затенен.
Вот как выглядит HTML:
<chart-container class="chart" chart-height="400" >
<h3 class="text-center">Vehicles by year</h3>
<div class="text-center" full-screen-toggle>
<bar-chart bar-data="{{vehiclesByYear}}"
layout="{{vehiclesByYearLayout}}"
bar-labels="{$dy: '-.5em', $anchor: 'middle'}"></bar-chart>
</div>
</chart-container>
<chart-container class="chart" chart-height="400">
<h3 class="text-center">Vehicle speed distribution</h3>
<div class="text-center" full-screen-toggle>
<pie-chart pie-data="{{aboveBelow}}"
layout="{{aboveBelowLayout}}"></pie-chart>
</div>
</chart-container>
Магия находится в контейнере-директиве элемента-элемента и директиве атрибута full-screen-toggle. Последний всегда украшает дочерний элемент внутри контейнера диаграмм. Здесь код для двух директив:
angular.module( 'MarkOlbert.fullScreenChart', [] ).directive( 'chartContainer', function () {
return {
restrict: 'E',
scope: {
chartHeight: '@chartHeight',
},
controller: ['$scope', '$element', '$rootScope', function ( $scope, $element, $rootScope ) {
var zoomState = 'normal';
var _self = this;
this.changing = false;
this.chartHeight = $scope.chartHeight;
function unZoom() {
$element.css('width', '50%' );
_self.changing = true;
$rootScope.$broadcast( 'chart:unzoom' );
_self.changing = false;
zoomState = 'normal';
}
function zoom() {
$element.css( 'width', '100%' );
_self.changing = true;
$rootScope.$broadcast( 'chart:zoom' );
_self.changing = false;
zoomState = 'full';
}
$element.on( 'click', function () {
switch ( zoomState ) {
case 'normal':
zoom();
break;
case 'full':
unZoom();
break;
}
} );
$rootScope.$on( 'chart:unzoom', function () {
if ( _self.changing ) return;
$element.css( 'display', 'block' );
} );
$rootScope.$on( 'chart:zoom', function () {
if ( _self.changing ) return;
$element.css( 'display', 'none' );
} );
}],
};
} );
angular.module( 'MarkOlbert.fullScreenToggle', [] ).directive( 'fullScreenToggle', function ($rootScope) {
return {
restrict: 'A',
require: '^^chartContainer',
link: function(scope,element,attrs, chartCtrl){
element.css('height', chartCtrl.chartHeight );
$rootScope.$on( 'chart:zoom', function () {
if ( !chartCtrl.changing ) return;
element.css( 'height', 1000 );
} );
$rootScope.$on( 'chart:unzoom', function () {
element.css( 'height', chartCtrl.chartHeight );
} );
},
};
} );
В директиве bar-chart у меня есть простые часы, которые должны реагировать на изменения высоты диаграммы:
scope.$watch( function () { return element.height(); }, function () {
if ( element.height() == 0 ) return;
}, true );
Сейчас он ничего не делает, потому что я пытаюсь понять, почему он не вызван, когда полноэкранный режим изменяет высоту.
Функциональность show/hide работает так, как было разработано. Все различные прослушиватели событий в контейнере диаграмм и полноэкранном переключении вызывают, когда они должны быть, и выполняются без ошибок.
Когда chart-container изменяет ширину своего элемента, страница обновляется, как ожидалось. Но когда полноэкранный режим изменяет высоту диаграммы, ничего не происходит. Часы $ watch в бар-диаграмме никогда не срабатывают.
.on()
- это метод jqLite/jQuery и не вызывает триггер цикла AngularJS.
Что происходит сейчас:
Но так как цикл дайджеста никогда не запускался, наблюдатель, который проверяет высоту элемента, никогда не будет выполняться, и никакие изменения не будут обнаружены.
Вы можете использовать $apply
:
$element.on('click', function() {
$scope.$apply(function() {
switch (zoomState) {
case 'normal':
zoom();
break;
case 'full':
unZoom();
break;
}
});
});