Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Best Practices

Burke Holland edited this page Feb 26, 2014 · 1 revision

Use ObservableArrays or DataSources Instead Of Arrays

Angular raises a developers expectations from the UI. Anything that we place on the scope we now expect to be bound to the UI and either one changing should update the other. By and large this is true when talking about Angular on it's own. The reason for this is that Angular is tracking all of these scope items and DOM bindings. For this reason, people often assume that if they bind an array to a Kendo UI widget in the scope, modifying that array will modify the widget. This is not the case and let me explain why.

When an array on the scope is set as the k-data-source for a Kendo UI widget, the directives pass this array to the Kendo UI widget as it is - just an array. Kendo UI doesn't watch every collection object for changes. It only watches special objects - specifically Observables, ObservableArrays and DataSources (which wraps observables). That means that when you are dealing with a Kendo UI widget from the scope, you will want to use either an ObservableArray or a Kendo UI DataSource instead of just a plain jane array.

Here is an example. This grid does not update when a new item is added to the collection because the collection is just a plain old JavaScript array.

View Example

Now we swap out the plain array for a Kendo UI ObservableArray. This array contains most of the native array methods, and Kendo UI watches this object constantly.

View Example

Don't forget about kendoEvent

Kendo UI widgets fire events. These events usually have an e parameter that comes along with them that is going to contain a bunch of information that you are probably going to be needing. In the Angular integrations for Kendo UI, you have to pass this parameter explicitly on the directive as kendoEvent.

<select kendo-drop-down-list k-on-change="change()"></select>

<script> function HomeCtrl($scope) { // logs 'undefined' $scope.change = function(e) { console.log(e); } } </script>

Instead, you will want to pass in the kendoEvent object with the event binding.

<select kendo-drop-down-list k-on-change="change(kendoEvent)"></select>

<script> function HomeCtrl($scope) { // logs the kendo event object $scope.change = function(e) { console.log(e); } } </script>

I often see folks wondering why there is no event object in their methods, and it's because they have left off the kendoEvent argument in their event binding.

Sometimes You Gotta $Apply

Since some widgets get super verbose in their configuration via attributes (particularlytrue with charts), we made it possible to provide the options configuration object via the scope using k-options.

This works great and allows you to keep you separation of UI and configuration concerns. However, any event bindings that you provide via configuration objects on the scope are not watched by Angular. This means that you need to call $apply when you are making changes to the scope inside of one of these events.

Here is an example where selecting the row and updating the $scope.firstName and $scope.lastName values does not update in the UI.

View Example

Since that change event binding is specified on the scope object, you need to $apply changes.

View Example

Note that this is only necessary when using the configuration object. If you set the event as an attribute on the HTML element, we go ahead and call apply for you in the directive.

Use ng-model Instead of Value For Bi-Directional Binding

The Kendo UI Directives are primarily concerned with the change event and value method of each widget. While you can set the value of a widget on initialization, in order to change it later you must use the value() method of the widget instance.

If you want bi-directional binding on your widgets, instead of the k-value attribute, use ng-model, which is tied to the widgets value under the covers and provides the desired two-way binding.

View Example

You can of course, always call the value method on any widget instance at any time in a controller. However, Angular Kendo UI is actually doing this exact same thing under the covers.

Remember That Strings Are 'Strings'

Angular's parsing engine requires that attributes that are strings be quoted as such, otherwise the value is looked for as a scope property. This can be confusing at first since Kendo UI does NOT require such quoting in it's declarative initialization.

I see people get bit by this one a lot when dealing with the AutoComplete, ComboBox, DropDownlist or any other widget that needs to know which field in the DataSource object contains the key, and which contains the text. Take for example a simple AutoComplete bound to a DataSource which has objects. Notice the subtle difference when using Kendo UI Declarative initialization vs the Angular Kendo UI integration.

<script>
// assuming we have data that looks like this...
app.people = new kendo.data.DataSource({
data: [ { text: "Alabama", value: "AL" },
{ text: "Alaska", value: "AK" },
{ text: "Arizona", value: "AZ" },
{ text: "Arkansas", value: "AR" } ]
});
</script>

<!-- the autocomplete declaration looks like this (kendo ui declarative) --> <input data-role="autocomplete" data-source="app.people" data-text-field="text" data-value-field="value" />

<!-- but the Angular integrations require quotes around the dataTextField and dataValueField attributes --> <input kendo-auto-complete k-data-source="people" k-data-text-field="'text'" k-data-value-field="'value'" />

That's a sneaky one isn't it! This is how Angular works though, and we decided it would actually be counter intuitive to try and auto-quote strings for you. It was so confusing for me though, that Pierre suggested we put in a message telling people they may have forgotten the quotes. That means that you will get an error message akin to the following if you use a value that we cannot find on the scope.

kendoAutoComplete's kDataTextField attribute resolved to undefined. Maybe you meant to use a string literal like: 'text'? 

Leverage Widget References

There will come some point in your application when you need to get a reference to a Kendo UI widget. If you weren't using Angular, you would just select the element with jQuery and pull the widget reference off it's data attribute.

<script>
// get the grid widget reference
$('#grid').data('kendoGrid');
// OR
$('#grid').getKendoGrid();
</script>

Of course, selecting items from an Angular controller with jQuery is expressly frowned upon and could result in the death of innocent puppies. That being the case, we decided to give you an alternate way of getting the widget reference. All you need to do is pass in a scope variable to the directive.

<div kendo-grid="grid" ...></div>

<script> function HomeCtrl($scope) {

$scope.refresh = function() { // scope.grid is the widget reference $scope.grid.refresh(); } } </script>

Respect The Scope Hierarchy

Often, you will find yourself embedding widgets inside of other widgets. A common practice is to put certain Kendo UI controls inside of a Kendo UI Window or widgets inside of a TabsTrip, Splitter and so on. When you do this, you will most likely run into scope hierarchy issues if you do not prefix your scope bindings with a .. Remember, the scope is not a model, it's just where your model lives. That means you can step all over yourself if you aren't careful. This will result in null widget references and all manor of weirdness that you may attribute back to Kendo UI, when this is really a scope issue.

The best way for me to explain this is really to let John Lindquist do it for me in this short video on The Dot. You can also read this comprehensive Gist on "Understanding Scopes".