I'm developing a dashboard system that uses multiple kendo charts inside of cards or tiles. Columns in the dashboard are a fixed size and the cards may span multiple columns. The dashboard should reflow the card layout when the size of the window changes, and when a card is too wide for the dashboard, its column span should be reduced and the chart rescaled to fit the new dimensions of it's container. Here's the wrinkle. Each chart's html and javascript are stored in separate html files. When the dashboard is rendered, the html containing the angularjs template and the supporting javascript is inserted into the containing div and then compiled using the $compile service. So far I have had no trouble referencing scope contents inside the compiled html, but when I try to set up a name reference for the chart so that I can redraw it once the layout has changed, that reference does not exist on my scope. I can make this work in a simple example, but not in the dashboard structure I've set up Here are some excerpts from my code.
Dashboard template (with some Razor markup mixed in):
<div class="@divClass" ng-controller="@controllerName" tsv-resize-dash>
<div ng-repeat="card in currentLayout.cards"
class="{{cardClass}} card"
ng-style="{top: card.top, left: card.left, width: card.width, height: card.height}">
<i class="fa fa-info info" ng-class="card.infoOpacity == 1.0 ? 'pressed' : ''" ng-show="card.template.length > 0" ng-click="card.toggleInfo()"></i>
<div class="title" ng-bind="card.title"></div>
<div class="content">
<div class="card-body" ng-style="{opacity: card.contentOpacity}" dynamic-html="card.template"></div>
<div class="infoContent" ng-style="{opacity: card.infoOpacity}" ng-bind="card.cardInfo"></div>
</div>
</div>
</div>
Directive that inserts and compiles the template html:
.directive('dynamicHtml', function ($compile) {
return {
restrict: 'A',
link: function ($scope, $element, $attrs) {
$scope.$watch($attrs.dynamicHtml, function (html) {
$element.html(html);
$compile($element.contents())($scope);
});
}
};
})
Directive for resizing the dashboard:
.directive('tsvResizeDash', ['$window', function ($window) {
return {
link: function ($scope, $element, $attrs) {
angular.element($window).bind('resize', function () {
var nColumns = Math.max(Math.floor($element.width() / $scope.currentLayout.CARD_WIDTH), 1)
console.log(nColumns);
if ($scope.currentLayout.currentNumberOfColumns != nColumns) {
console.log('resize!');
$scope.currentLayout.currentNumberOfColumns = nColumns;
$scope.currentLayout.reflow();
$($scope.currentLayout.cards).each(function () {
if (this.redraw)
this.redraw($scope);
});
$scope.$apply();
}
});
}
}
}]);
Template example (The html is inserted and compiled; The javascript is simply rendered to the page.):
<script type="text/html">
<div kendo-chart="myChart"
k-theme="'metro'"
k-series-defaults="{
type: 'area',
area: {
line: { style: 'smooth' }
},
missingValues: 'interpolate'
}"
k-data-source="card.dataSource"
k-series="card.model.series"
k-legend="{
position: 'bottom',
labels: {
font: '11px Droid Sans'
}
}"
k-category-axis="{ baseUnit: 'fit' }"
ng-style="{
width: card.width - card.padding.horizontal,
height: card.height - card.padding.vertical
}">
</div>
</script>
<script type="text/javascript" cardtype="AreaChart">
//AreaChart extends the Card object which all cards share.
var AreaChart = Card.extend({
load: function () {
//call the load() of the super class using a promise to ensure the correct order of operations for the async methods
return this._super().done(function () {
this.dataSource = new kendo.data.DataSource({
//model is a member of the super class
data: this.model.data
});
console.log(this.model);
});
},
redraw: function ($scope) {
//undefined
console.log($scope.myChart);
}
})
</script>
I would expect that when I get to the redraw function just above, that I would have a reference to 'myChart' inside $scope, but that isn't the case. I've also tried referencing it inside the controller, and it does not exist at that point, either.
So my question is: Am I doing something wrong, here, or have I found some sort of bug?
I realize that's quite a bit to digest. Let me know if further explanation is needed.
Thanks!
Dashboard template (with some Razor markup mixed in):
<div class="@divClass" ng-controller="@controllerName" tsv-resize-dash>
<div ng-repeat="card in currentLayout.cards"
class="{{cardClass}} card"
ng-style="{top: card.top, left: card.left, width: card.width, height: card.height}">
<i class="fa fa-info info" ng-class="card.infoOpacity == 1.0 ? 'pressed' : ''" ng-show="card.template.length > 0" ng-click="card.toggleInfo()"></i>
<div class="title" ng-bind="card.title"></div>
<div class="content">
<div class="card-body" ng-style="{opacity: card.contentOpacity}" dynamic-html="card.template"></div>
<div class="infoContent" ng-style="{opacity: card.infoOpacity}" ng-bind="card.cardInfo"></div>
</div>
</div>
</div>
Directive that inserts and compiles the template html:
.directive('dynamicHtml', function ($compile) {
return {
restrict: 'A',
link: function ($scope, $element, $attrs) {
$scope.$watch($attrs.dynamicHtml, function (html) {
$element.html(html);
$compile($element.contents())($scope);
});
}
};
})
Directive for resizing the dashboard:
.directive('tsvResizeDash', ['$window', function ($window) {
return {
link: function ($scope, $element, $attrs) {
angular.element($window).bind('resize', function () {
var nColumns = Math.max(Math.floor($element.width() / $scope.currentLayout.CARD_WIDTH), 1)
console.log(nColumns);
if ($scope.currentLayout.currentNumberOfColumns != nColumns) {
console.log('resize!');
$scope.currentLayout.currentNumberOfColumns = nColumns;
$scope.currentLayout.reflow();
$($scope.currentLayout.cards).each(function () {
if (this.redraw)
this.redraw($scope);
});
$scope.$apply();
}
});
}
}
}]);
Template example (The html is inserted and compiled; The javascript is simply rendered to the page.):
<script type="text/html">
<div kendo-chart="myChart"
k-theme="'metro'"
k-series-defaults="{
type: 'area',
area: {
line: { style: 'smooth' }
},
missingValues: 'interpolate'
}"
k-data-source="card.dataSource"
k-series="card.model.series"
k-legend="{
position: 'bottom',
labels: {
font: '11px Droid Sans'
}
}"
k-category-axis="{ baseUnit: 'fit' }"
ng-style="{
width: card.width - card.padding.horizontal,
height: card.height - card.padding.vertical
}">
</div>
</script>
<script type="text/javascript" cardtype="AreaChart">
//AreaChart extends the Card object which all cards share.
var AreaChart = Card.extend({
load: function () {
//call the load() of the super class using a promise to ensure the correct order of operations for the async methods
return this._super().done(function () {
this.dataSource = new kendo.data.DataSource({
//model is a member of the super class
data: this.model.data
});
console.log(this.model);
});
},
redraw: function ($scope) {
//undefined
console.log($scope.myChart);
}
})
</script>
I would expect that when I get to the redraw function just above, that I would have a reference to 'myChart' inside $scope, but that isn't the case. I've also tried referencing it inside the controller, and it does not exist at that point, either.
So my question is: Am I doing something wrong, here, or have I found some sort of bug?
I realize that's quite a bit to digest. Let me know if further explanation is needed.
Thanks!