ng-* Directives in Widget Markup
Prior to the Kendo UI Q2 2015 release, the widgets which were instantiated over existing markup partially supported ng-repeat
, ng-if
, ng-bind
, and other DOM manipulation directives in the markup.
However, subsequent changes to the markup, caused by those directives, were not handled correctly.
Description
The widgets which exhibited this behavior were the TabStrip, PanelBar, Menu, TreeView, and some of the hybrid mobile widgets. This behavior was accidental and was caused by the directives instantiating the widgets in a $timeout
(setTimeout
) wrap.
The timeout initialization caused several other issues as well. The widget instances were not accessible in a reliable manner. The instantiation of each widget required several additional $scope.digest
cycles to be executed. Performance was negatively affected and the widget initialization was visible to the end user in certain scenarios. The change from June 17 2015 removed the timeout implementation, effectively breaking the accidental ng-repeat
support in later releases.
Approaches
To handle the issues, utilize any of the following approaches:
Using the dataSource Option
The recommended approach to achieve dynamic content generation for the listed widgets is through the dataSource
configuration option.
The following example demonstrates a Kendo UI TreeView widget with DataSource
in AngularJS.
<div ng-app="app" ng-controller="MyCtrl">
<button ng-click="add()">Add new</button>
<div kendo-tree-view k-data-source="tree"></div>
</div>
<script>
angular.module("app", ["kendo.directives"]).controller("MyCtrl", function($scope) {
$scope.tree = new kendo.data.ObservableArray([
{ text: "Foo", items: [
{ text: "Foo 1" },
{ text: "Foo 2" } ] },
{ text: "Bar", items: [
{ text: "Bar 1" },
{ text: "Bar 2" } ] },
]);
$scope.add = function() {
$scope.tree.push({
text: "This works",
items: [ { text: "Sweet" } ]
});
};
});
</script>
The following example demonstrates a Kendo UI PanelBar widget with DataSource
in AngularJS.
<div ng-app="foo">
<div ng-controller="MyCtrl">
<ul kendo-panel-bar k-data-source="panelBarDataSource"></ul>
</div>
</div>
<script>
angular.module("foo", [ "kendo.directives" ]).controller("MyCtrl", function($scope) {
$scope.panelBarDataSource = [
{
text: "Item 1 (link)",
cssClass: "myClass", // Add custom CSS class to the item, optional, added 2012 Q3 SP1.
url: "https://www.telerik.com/kendo-ui/" // link URL if navigation is needed (optional)
},
{
text: "<b>Item 2</b>",
encoded: false, // Allows use of HTML for item text
content: "text" // content within an item
},
{
text: "Item 3",
// content URL to load within an item
contentUrl: "https://demos.telerik.com/kendo-ui/content/web/panelbar/ajax/ajaxContent1.html"
},
{
text: "Item 4",
// item image URL, optional
imageUrl: "https://demos.telerik.com/kendo-ui/content/shared/icons/sports/baseball.png",
expanded: true, // item is rendered expanded
items: [{ // Sub item collection.
text: "Sub Item 1"
},
{
text: "Sub Item 2"
}]
},
{
text: "Item 5"
}
]
});
</script>
The following example demonstrates a Kendo UI TabStrip widget with DataSource
in AngularJS.
<div ng-app="foo">
<div ng-controller="MyCtrl">
<div kendo-tab-strip k-data-source="tabStripDataSource" k-data-text-field="'text'" k-data-content-field="'content'"></div>
</div>
</div>
<script>
angular.module("foo", [ "kendo.directives" ]).controller("MyCtrl", function($scope) {
$scope.foo = "Hello from angular";
$scope.tabStripDataSource = [
{
text: "Item 1",
content: "Item 1 content - "
},
{
text: "Item 2",
content: "Item 2 content - "
}
]
});
</script>
The following example demonstrates a Kendo UI Menu widget with DataSource
in AngularJS.
<div ng-app="foo">
<div ng-controller="MyCtrl">
<div kendo-menu k-data-source="menuDataSource"></div>
</div>
</div>
<script>
angular.module("foo", [ "kendo.directives" ]).controller("MyCtrl", function($scope) {
$scope.foo = "Hello from angular";
$scope.menuDataSource = [{
text: "Item 1 ",
cssClass: "myClass",
url: "https://www.telerik.com/kendo-ui"
},
{
text: "<b>Item 2</b>",
encoded: false
},
{
text: "Item 3",
items: [{
text: "Sub Item 1"
},
{
text: "Sub Item 2"
}]
},
{
text: "Item 4",
spriteCssClass: "imageClass3"
}];
});
</script>
Using the k-ng-delay Option
The approach is not recommended and results in side effects, such as FOUC (flash of unstyled content) and decreased performance.
To avoid the generation of content with DataSource
, you can implement a possible workaround by using the k-ng-delay
configuration option.
The following example demonstrates a Kendo UI TabStrip widget in AngularJS with delayed initialization.
<div id="example" ng-app="KendoDemos">
<div class="demo-section k-content">
<div ng-controller="MyCtrl">
<div kendo-tab-strip k-ng-delay="tabStripDelay">
<!-- tab list -->
<ul>
<li ng-repeat="tab in tabs">Tab</li>
</ul>
<div ng-repeat="tabContent in tabContents">
TabContent
</div>
</div>
</div>
</div>
</div>
<script>
angular.module("KendoDemos", [ "kendo.directives" ])
.controller("MyCtrl", function($scope, $timeout){
$scope.tabs = [ "t1", "t2" ];
$scope.tabContents = [ "tc1", "tc2" ];
$timeout(function() {
$scope.tabStripDelay = true;
});
})
</script>
Using the ng-if Directive
The ng-if
directive, which is applicable to most ng-\*
directives, operates directly on the origin HTML that is bound to and not on the widget itself. When the Kendo UI widget has a more complex rendering, such as the NumericTextBox, then the directive shows or hides the original input
element and not the widget. This is expected due to the previously mentioned specifics of the ng-\*
directives.
The general solution is to use a specific k-ng-\ directive where available—for example,
k-ng-disabled
.
The following example demonstrates how to use a wrapping HTML element to hide/show Kendo UI Widget.
<div id="example" ng-app="KendoDemos">
<div class="demo-section k-content">
<div ng-controller="MyCtrl">
<div ng-if="show">
<input kendo-numeric-text-box />
</div>
</div>
</div>
</div>
<script>
angular.module("KendoDemos", [ "kendo.directives" ])
.controller("MyCtrl", function($scope, $timeout){
$scope.show = false;
})
</script>