Announcing Support For Kendo UI Mobile With AngularJS

angular-kendo-mobile-header

You asked for it. Then you asked for it again. Over and over we heard, “We want to be able to use Kendo UI Mobile with AngularJS”.

Ask and you shall receive. Kendo UI Mobile support has landed in Angular Kendo UI.

As of the Q1 March release this year, the mobile widgets have been decoupled from the Kendo UI Mobile application framework so that they can now be used anywhere and with any framework. Given our work on the Angular Kendo UI project and your loud and clear demand, we figured Angular was the best place to start.

Oh – and now that Kendo UI Core (which includes Kendo UI Mobile) is open source and the Angular integrations have always been open source, you can use them for the very low price of “totes free” with no strings attached.

Getting Started

If you’ve worked with Kendo UI Mobile before, you’re going to have change how you think just a tad when working with Angular. The Kendo UI Mobile Application object does a LOT of “behind the scenes” work for you such as adding meta tags, laying out visual elements and handling view state. Using the widgets outside of the Application object means that Angular will have to take over where Kendo UI leaves off. Well, Angular and your own mad coding skills.

You will need one of the mobile styles found in the Kendo UI styles/web folder, as well as jquery and kendo.ui.core. You will also need AngularJS and the Angular Kendo UI Integrations library. It’s only one file called angular-kendo.js.


<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
    <!-- Makes your prototype chrome-less once bookmarked to your phone's home screen -->
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <link href="app/components/kendo-ui-core/styles/web/kendo.bootstrap.mobile.min.css" rel="stylesheet" type="text/css">
  </head>

  <body>

    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.min.js"></script>
    <script src="app/components/kendo-ui-core/src/js/kendo.ui.core.js"></script>
    <script src="angular-kendo.js"></script>

  </body>

</html>

We use the stylesheets for Kendo UI Mobile from the Web folder because we are not using the Kendo UI Mobile application object. We are only using the widgets. Kendo UI provides specific mobile styles to match the mobile widgets to the current theme. In this case, I’ve gone with the Kendo UI Bootstrap theme.

You can experiment with the themes to see the different colors they provide.

  • Default
  • BlueOpal
  • Bootstrap
  • Silver
  • Uniform
  • Metro
  • Black
  • MetroBlack
  • HighContrast
  • Moonlight
  • Flat

Any of the Kendo UI Mobile widgets can be used. For a complete set of demos, checkout the Kendo UI site. These widgets have the same directives as Kendo UI Web, except everything is prefixed with kendo-mobile. For instance, if you wanted to create a NavBar, you would use the kendo-mobile-nav-bar directive.

Don’t forget to pass in the kendo.directives module to your application module.


<!DOCTYPE html>
<html ng-app="app">
  <head>
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
    <!-- Makes your prototype chrome-less once bookmarked to your phone's home screen -->
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <link href="app/components/kendo-ui-core/styles/web/kendo.bootstrap.mobile.min.css" rel="stylesheet" type="text/css">
  </head>

  <body>

    <!-- NavBar -->
    <header>
      <div kendo-mobile-nav-bar>
          <div kendo-mobile-view-title>CutePics</div>
      </div>
    </header>

    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.min.js"></script>
    <script src="app/components/kendo-ui-core/src/js/kendo.ui.core.js"></script>
    <script src="angular-kendo.js"></script>

    <script>

      (function() {

        var app = angular.module('App', [ 'kendo.directives' ]);

      }());

    </script>

  </body>

</html>

Notice the meta tags that I stuck in the head. These are required for sites running on mobile devices as apps. They handle things like viewport scaling, zooming and other things that you want to “just work” on mobile. Kendo UI Mobile would do this for you ordinarilly, but using it outside of the Application object means we need to add them ourselves.

Which would give us…

post

That’s a Kendo UI Mobile NavBar! It looks great, but it’s got some padding around the top, left and right sides. Actually, the body is the one with the padding which we can remove with a little CSS and tweak the font to something a littler prettier while we’re at it.


body, html {
  padding: 0;
  margin: 0;
  font-family: Tahoma, sans-serif;
}

navbar-no-padding

Much better! Now a Kendo UI NavBar is nice, but lets get to some more of the nitty gritty. Let’s add in a Kendo UI Mobile ListView. I’m also going to impement a multipage scenario so we can put Angular through its paces here.

I’m going to pull in a feed from Reddit (don’t worry! It’s just the ‘cute’ reddit; absolutely SFW.) and drop thumbnails in a ListView. Tapping on one of the items will show the image in a new view full screen.

View Demo

First, let’s create the home.html partial view.


<ul kendo-mobile-list-view k-data-source="reddit" 
    k-on-click="preview(kendoEvent)" 
    k-template="redditTemplate"></ul>  

Now the preview.html partial.


<img class="full" ng-src="{{ url }}" >

That gives us a ListView in the Home screen where we can put the initial feed from Reddit, and a second Preview screen where full screen images will be shown. We just need an `ng-view` in the index page to load our views.


<!DOCTYPE html>
<html ng-app="app">
  <head>
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
    <!-- Makes your prototype chrome-less once bookmarked to your phone's home screen -->
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <link href="app/components/kendo-ui-core/styles/web/kendo.blueopal.mobile.min.css" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="style.css">
  </head>

  <body>

    <header>
      <div kendo-mobile-nav-bar>
        <div kendo-mobile-view-title>CutePics</div>
        <button kendo-mobile-button data-align="left" k-on-click="goBack()" ng-show="app.back">Back</button>
      </div>
    </header>

    <div class="content" ng-view></div>

    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular-route.js"></script>
    <script src="app/components/kendo-ui-core/src/js/kendo.ui.core.js"></script>
    <script src="angular-kendo.js"></script>
    <script src="scripts.js"></script>

  </body>

</html>

We’re finally able to write some Angular code. We’re going to be needing a few things…

  1. A Router
  2. A Service – for sharing data between views
  3. A Home Controller
  4. A Preview Controller

(function () {

  var app = angular.module("myApp", ["ngRoute", "kendo.directives"]);

  app.controller('HomeCtrl', [ '$scope', function ($scope) {
    // home code
  }]);

  app.controller('PreviewCtrl', [ '$scope', function ($scope, $routeParams, appSvc) {
    // preview code
  }]);

  app.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/', {
        templateUrl: 'partials/home.html',
        controller: 'HomeCtrl'
      }).
      when('/preview', {
        templateUrl: 'partials/preview.html',
        controller: 'PreviewCtrl'
      })
  }]);

}());

That sets up our basic application structure. The Kendo UI Mobile ListView in the home.html file is referencing a few things on the scope in the Home controller that don’t exist.

  • k-data-source
  • k-template
  • k-on-click

The first attribute is a Kendo UI DataSource. Kendo UI Widgets are tightly coupled to the concept of a Kendo UI DataSource. DataSources can be used in Angular without any issue and should be used in place of $http when possible since widgets are not aware of changes everywhere, but only changes that occur inside of certain Kendo UI constructs like the DataSource.

The k-template defines the template we want to use for each item. Kendo UI has its own powerful templating system that Kendo UI widgets will expect to be using.

The k-on-click defines a method that should occur when one of the items in the list gets clicked on. In this case, we want to navigate to the “/preview” route manually.

We also need a service to hold a reference to the currently selected item. This way both the Home and Preview controllers can access the same data and the scopes will stay up to date. This is a lot, so lets start with the application service which will hold the currently selected item. By default it’s null.


// appSvc provides shared access between home and preview
app.service('appSvc', function () {
  this.selectedItem = null;
});

In the Home controller, we need to define the DataSource, the template and the click event.


app.controller('HomeCtrl', [ '$scope', '$location', 'appSvc', function 
$scope, $location, appSvc) {
  // the remote data source. it is read automatically when the listview
  // is initialized by the kendo-mobile-list-view directive
  $scope.reddit = new kendo.data.DataSource({
    transport: {
      read: 'http://www.reddit.com/r/cute.json',
      type: 'jsonp'
    },
    schema: {
      // doing a little filtering here to ensure we only look at images and not
      // videos or links to pages which contain images
      parse: function (data) {
        var data = $.map(data.data.children, function (item) {
          if (item.data.url.indexOf('.jpg') > -1) {
            return item;
          }
        })
        return data;
      }
    }
  }),
  // the template for each item in the dataset
  $scope.redditTemplate = "<a class='reddit-item'><img src='#: data.thumbnail #'><p>#: data.title #</p></a>";
  $scope.preview = function(e) {
    // set the selected item on the service so we can access it from preview partial
    appSvc.selectedItem = e.dataItem.data;

    // manually navigate to the preview partial
    $location.path("/preview")
  }
}]);

The preview controller only needs the application service injected in. The ng-source binding in the “preview.html” partial will update when the application service updates.


app.controller('PreviewCtrl', [ '$scope', '$routeParams', 'appSvc', function ($scope, $routeParams, appSvc) {
  $scope.url = appSvc.selectedItem.url;
}]);

A little CSS and we’re getting close to wrapping up.


body, html {
  padding: 0;
  margin: 0;
  font-family: Tahoma, sans-serif;
}
.reddit-item {
  height: 100%;
  overflow: scroll;
}
.reddit-item p {
  color: black;
}
.reddit-item img {
  float: left;
  margin-right: 20px;
}
.full {
  width: 100%;
  height: auto;
}

Now the ListView displays the data. Clicking an item in the list will get us to the Preview screen with a full image.

app1-app2

Wonderful! And what an adorable seal.

HOWEVER. We have a bit of a problem in that we can’t go back to the previous page. We need to add a “Back” button in the NavBar. That button only needs to be visible on the Preview page. Instead of putting a NavBar on both pages, lets create a LayoutController which will expose the Application Service to the main layout and allow us to toggle the visibility of the button.

Here is the complete HTML for the application layout.


<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
    <!-- Makes your prototype chrome-less once bookmarked to your phone's home screen -->
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <link href="app/components/kendo-ui-core/styles/web/kendo.blueopal.mobile.min.css" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="style.css">
  </head>

  <body ng-controller="LayoutCtrl">

    <header>
      <div kendo-mobile-nav-bar>
        <div kendo-mobile-view-title>CutePics</div>
        <button kendo-mobile-button data-align="left" k-on-click="goBack()" ng-show="app.back">Back</button>
      </div>
    </header>

    <div class="content" ng-view></div>

    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular-route.js"></script>
    <script src="app/components/kendo-ui-core/src/js/kendo.ui.core.js"></script>
    <script src="angular-kendo.js"></script>
    <script src="scripts.js"></script>

  </body>

</html>


…And the complete JavaScript for the entire application


(function () {

  var app = angular.module("App", ["ngRoute", "kendo.directives"]);

  // appSvc provides items
  app.service('appSvc', function () {
    this.selectedItem = null;
    this.back = false;
  });

  app.controller('LayoutCtrl', [ '$scope', '$location', 'appSvc', function ($scope, $location, appSvc) {

    // expose the service
    $scope.appSvc = appSvc;

    // handle back button click
    $scope.goBack = function () {
      appSvc.back = false;
      $location.path('/');
    }
  }]);

  app.controller('HomeCtrl', [ '$scope', '$location', 'appSvc', function ($scope, $location, appSvc) {
    // the remote data source. it is read automatically when the listview
    // is initialized by the kendo-mobile-list-view directive
    $scope.reddit = new kendo.data.DataSource({
      transport: {
        read: 'http://www.reddit.com/r/cute.json',
        type: 'jsonp'
      },
      schema: {
        // doing a little filtering here to ensure we only look at images and not
        // videos or links to pages which contain images
        parse: function (data) {
          var data = $.map(data.data.children, function (item) {
            if (item.data.url.indexOf('.jpg') > -1) {
              return item;
            }
          })
          return data;
        }
      }
    }),
    // the template for each item in the dataset
    $scope.redditTemplate = "<a class='reddit-item'><img src='#: data.thumbnail #'><p>#: data.title #</p></a>";
    $scope.preview = function(e) {
      // set the selected item on the service so we can access it from preview partial
      appSvc.selectedItem = e.dataItem.data;

      // manually navigate to the preview partial
      $location.path("/preview")

      // set the back button to visible
      appSvc.back = true;
    }
  }]);

  app.controller('PreviewCtrl', [ '$scope', '$routeParams', 'appSvc', function ($scope, $routeParams, appSvc) {
    // set the image url
    $scope.url = appSvc.selectedItem.url;
  }]);

  app.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/', {
        templateUrl: 'partials/home.html',
        controller: 'HomeCtrl'
      }).
      when('/preview', {
        templateUrl: 'partials/preview.html',
        controller: 'PreviewCtrl'
      })
  }]);

}());

ScreenFlow (8)

Fixed Headers

You may have noticed that the NavBar is not fixed, as in it scrolls off the screen as the list scrolls. This is another thing Kendo UI Mobile usually takes care of. It’s not hard to do with a little CSS wizardry and some fixed positioning. Remember, when in doubt about CSS, go look at how Bootstrap does it! Since fixing the header takes it out of the document layout, the body will now appear underneath it. We need to add some padding to the content area to account for the fixed header.


.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 100px;
  width: 100%;
  z-index: 1030;
}
.content {
  padding-top: 50px;
  height: 100%;
}

The z-index makes sure all the content scrolls UNDER the header while the header stays put.

View Demo

Go Forth And Be Angular

We are so pleased to release this initial implementation of the Kendo UI Mobile widgets for Angular. If you are working with us on this project (and many of you are), then you know that this is the single most active community project right now at Kendo UI. If you find a bug, have an idea for a feature or want to make an improvement, hit us up on GitHub. Make sure you include a working demonstration of your issue! I like to use Plunker with Angular Kendo UI, but we aren’t picky!

Grab a copy of Kendo UI Core (it’s free!) and get started building mobile apps with AngularJS. Like I’ve always said, a great JavaScript framework deserves a great UI. That’s why Angular and Kendo UI Mobile are the perfect match.

BuildFree_Blog_banner

Comments

  • Kerry Jenkins

    Thank you Telerik for providing AngularJS integration with your Kendo UI Mobile. This is really great!

    • burkeholland

      Thanks Kerry! Let us know if you find any issues or have any feedback. We’re working to make it better all the time. https://github.com/kendo-labs/angular-kendo

      • Kerry Jenkins

        Burke, can you point me to where I can find information on how to use angular directives such as ng-click in a Kendo UI template? I have been searching and finding some discussion but can’t find anything specifically stating how to do it.

  • dawesi

    another stack on a stack on a stack of micro frameworks… what a nightmare… looks ugly.

    A stack this high makes full stack frameworks like KendoUI and Sencha look skinny…

    • burkeholland

      Haha! It’s the leaning tower!

      In all seriousness, there is a lot of overlap between Angular and Kendo UI, but you can use them together. The integrations library is very small – its not even 1K lines of code and it actually generates itself. You can read more about that here.

  • CMichaelGraham

    I want to use ESRI JavaScript API, which uses Dojo. I also want to use TypeScript. I figured out how to get the Dojo AMD loader to lazy load my TypeScript view models. I can also load the views from external sources. All within the happy confines of the Kendo Mobile Application (and with heavy use of Kendo DataSource). The jQuery capabilities are great also in this set of technologies.

    My spider sense is that I should stick with the above and not try to add AngularJS into the mix. There are articles on lazy loading AngularJS modules with AMD, but it doesn’t look super easy.

    Having said all of that, I’m really hoping that we can port the entire platform over to AngularJS 2 when that drops. I’m hoping Kendo (or equivalent) will be right there with nice, powerful directives etc. The addition of Rob Eisenberg of Caliburn Micro and Durandal fame is VERY encouraging for AngularJS 2. http://eisenbergeffect.bluespire.com/angular-and-durandal-converge/

    • burkeholland

      The Kendo UI Mobile framework is definitely full stack. We provide Angular integration becomes some folks want to use Angular and they only want to use Kendo UI as the UI portion. You should be able to do that if you want to and this integration makes it possible.

      I agree that Angular 2.0 is very promising, and we are watching it closely!

  • Mathis Gardon

    Great news Burke !

    Thank you for delivering on that request. I remember that 9 months ago it was only a github issue, but you did listen to the feedback ! (I guess john papa asking for this did help, too ;-)

    Anyway, we are going to use it in my project so what you did will clearly be useful at least to us.

    keep up the good work !

  • http://www.sagarganatra.com/ Sagar Ganatra

    This is great!! Do you plan to add demos/docs here – http://kendo-labs.github.io/angular-kendo/#/

  • Luis Cabarique

    I would like to know if you have a release date of a fully supported integration between AngularJS and Kendo UI Mobile (including all mobile widgets)

  • Pingback: Announcing Support For Kendo UI Mobile With Ang...()

  • Pingback: Announcing Support For Kendo UI Mobile With Ang...()

  • Andrew

    Hi Burke,

    Im trying to use templates with the new kendo core, release 2014.2.716 and im getting this problem

    kendoMobileListView’s kTemplate attribute resolved to undefined. Maybe you meant to use a string literal like: ‘myTemplate’?

    can we use all html attributes in Kendo/Angular that in Kendo MVVM? Im starting to see it a little bit confusing.

  • Ries van Twisk

    Wen I use kendo with angular router my states are not saved. For example when a user sort’s a datagrid, re-order columns. Then when I go away from that page (with page, read angular router pages, not real HTML pages) and come back then the datagrid is loaded in it’s default state. Is it possible to let kendo save it’s states?

  • Ries van Twisk

    Wen I use kendo with angular router my states are not saved. For example when a user sort’s a datagrid, re-order columns. Then when I go away from that page (with page, read angular router pages, not real HTML pages) and come back then the datagrid is loaded in it’s default state. Is it possible to let kendo save it’s states?