Kendo Mobile Gotchas, Tips, Tricks

After working on the Kendo UI Music Store sample web site for weeks, I switched to making a mobile version using Kendo Mobile and Icenium (which, by the way, is awesome!). There were a lot of things that didn’t seem to work in Kendo Mobile. Within 20 lines of code I was frustrated and could not get a working app. I eventually realized it was because Kendo Mobile just has some differences from Web that I didn’t yet understand.

This post is a random collection of little things in Kendo UI Mobile that threw me off or caused issues until I understood how they worked. Hopefully it can help the next person not step into the same pits.

View event ordering: Init, BeforeShow, Show

The views in Kendo Mobile have 3 events that you will probably use often: init, beforeShow and show. The init event is only called once per view and is not called until the first time the view is navigated to (not on app start).

Most people would think that the order these would be called in would be:

1. init
2. beforeShow
3. show

This is incorrect. beforeShow is actually the first to be triggered. Here is a sample:

<div data-role="view" data-init="onInit" data-before-show="onBeforeShow" data-show="onShow"></div>
function onInit(e) {
    console.log("init");
}

function onBeforeShow(e) {
    console.log("beforeShow");
}

function onShow(e) {
    console.log("show");
}

This results in this being printed to the console:


beforeShow
init
show

MVVM bind happens before init, beforeShow, and show

If we take the above example of event ordering and now add an MVVM view model, and in the HTML add data-model:

<div data-role="view" data-init="onInit" data-before-show="onBeforeShow" data-show="onShow" data-model="viewModel"></div>

The order of events is:


bind view model
beforeShow
init
show

So, what does this mean? Well, if you want to do anything in your init or beforeShow to set up your ViewModel, you can’t. However this does mean that from the init, show, and beforeShow events you can access the initialized MVVM bound widgets.

Layout elements are copied into the views that use them, so do not use IDs in layout elements.

If you specify two views that share a layout, for example

<div data-role="view" id="view1" data-layout="shared-layout"></div>

<div data-role="view" id="view2" data-layout="shared-layout"></div>

<div data-role="layout" data-id="shared-layout">
    <span id="foo">foo</span>
</div>

When the app is initialized, the layout is removed from the DOM and stored in a JavaScript variable.
When any view that uses a layout is shown, the elements from the layout are copied/cloned into the views, so the DOM will become:

<div data-role="view" id="view1" data-layout="shared-layout">
    <span id="foo">foo</span>
</div>

<div data-role="view" id="view2" data-layout="shared-layout">
    <span id="foo">foo</span>
</div>

You can see that this is now invalid HTML because we have 2 elements with the same ID. Because of this, you should never use an element id in your layouts.

Layout element copy does not happen until the view show event.

I think this is a poor design decision, but the layout elements are not copied/cloned into the view until after the init happens, just before the show event is fired.
This means that any jQuery selectors that you use in your view init or beforeShow (the first time it is called) event functions will not be able to target the elements that are from the layout, even though they become part of the view eventually.

MVVM binding happens before layout elements are copied. Use MVVM in layouts only if you are careful.

This means that if you try to use MVVM bindings in your layout, assuming that they will bind to the view model of the view, then your bindings will not work.

For example, this will not work:

<div data-role="view" data-model="myViewModel" data-layout="myLayout"></div>

<div data-role="layout" data-id="myLayout">
    <span data-bind="text: myProperty"></span>
</div>

<script type="text/html">
window.myViewModel = kendo.observable({
    myProperty: "hi!"
});
</script>

The span element will not be bound to the view model and will contain no text, not the word “hi!”.

You can overcome this by calling kendo.bind(element, viewModel) manually in the show event of every view that uses the layout, but you should only call kendo.bind() once on the layout, because the bindings will persist. This means having some kind of a flag to track whether or not the layout has already been bound to a viewModel.

However there is an issue here too; the layout elements are actually copied around between the views, including the MVVM bindings! So if you have multiple views that all share the same layout, you can not bind the layout elements to a different viewModel object for each view. In other words, if you are going to use MVVM bindings in your layout, make sure only 1 viewModel is bound to it. Depending on how you structure your app, this probably means that only 1 view can use the layout.

display: none; your views.

Your app is likely to have multiple views in the same HTML page. Each of these is just a <div&gy; tag. On app load, sometimes the user will be able to see a flash of all the text from all the views in the delay between the page being rendered and the JavaScript call to new kendo.mobile.Application(). You can prevent this by simply putting a style="display:none;" attribute on all your views (or do it in css). This way they will not be displayed when the app is first rendered.

View parameters can be passed on the query string.

Sometimes you need to pass parameters to your view. You can do this just like any other web site by including a query string to your navigation. For example:

<a href="#my-view?one=1&two=2">...</a>

These parameter values will now be available on your view once you have navigated there on a property named params.

function onShow(e) {
    console.log(e.view.params);
}

Here, e.view.params will be an object representing the data that was passed to the view:

{
    one: 1,
    two: 2
}

View parameters are not available in beforeShow.

I just demonstrated that the query string passed to a view is converted to an object and stored in the view’s .params property. However, if you try to look at view.params in the beforeShow method, you will find that it is always null. Kendo considers this event to happen before the page is really navigated to, so does not yet load the params.

Global vs ViewModel scope (data-source vs data-bind=”source:”)

This one threw me for a loop when I first started using Kendo Mobile. I was pretty good with the web version, and was accustomed to data-bind="source: myData" and data-bind="click: doSomething", but when I got to Kendo Mobile, I started seeing data-source="myData" and data-click="doSomething" in the examples. Why does Mobile have a whole other set of “data-” attributes for things that already exist?

Well, it turns out that one starts looking in the Global JS scope, and the other works with the view model defined by “data-model”.

If you are not using a view model to do data bindings in an MVVM style, then forget about data-model and data-bind.

So, to state this as simply as I can; the data-bind attribute searches from the object specified by the data-model attribute. The other data-* attributes search from the global scope.

Let’s look at this with an example:

var doSomething = function (e) { console.log("Global"); }

var viewModel = kendo.observable({
    doSomething: function (e) { console.log("ViewModel"); }
});
<div data-role="view" data-model="viewModel">
    <span data-role="button" data-click="doSomething">
        Clicking me will call doSomething() and print "Global"
    </span>
    <span data-role="button" data-bind="click: doSomething">
        Clicking me will call viewModel.doSomething() and print "ViewModel"
    </span>
</div>

The same holds true for data-source="globalScopeObject" and data-bind="source: viewModelProperty".

Save your sanity and put an ID on your body element.

If you plan on doing a lot of custom styling, you will quickly find that Kendo UI has some extensive, and fairly specific CSS selectors already in use. At times, it can be difficult to target the elements you want, and have a selector that is more specific than the ones that Kendo already specified. You can save your time and sanity by putting an ID on your body element: <body id="body">

This will allow you to quickly overcome those times when you just can’t seem to find the right selector or want to avoid the terrible <codeimportant! keyword by just putting the #body id as the root of your css selector.

For example, I simplified:

.km-root .km-android .km-navbar input[type="text"].search-text
{
    font-size: 1em !important;
    width: 100%;
    margin-right: -36px;
}

to simply:

#body .search-text
{
    font-size: 1em;
    width: 100%;
    margin-right: -36px;
}

The selector is clearer and I got rid of !important. Double-win! Of course, some CSS purists will hate this, but if the alternative is a 5-word selector and an !important, then you can probably convince anyone that this is a better solution.

Remember the Back Button.

It is easy to forget this until you start testing on a physical Android device. iOS devices and the simulator in Icenium do not have a back button, but Android devices do. In a PhoneGap / Kendo Mobile app it will function basically the same as the “back” button in your browser, just going back through the history of pages. You can get the current navigation history from the main pane of the app:

var app = new kendo.mobile.Application(document.body);
// ...do some navigating around between view, then...

console.log( app.pane.history );

This will print an array of the nav history of the app, with the last element being the current view.
This also means that you can manipulate this array however you want. You can add or remove views that you do or don’t want the back button to stop at as it is tapped.

The data-role="backbutton" widget is the same thing as the physical android back button.

Also, if you want to simulate the back button when developing in the simulator but don’t have a backbutton widget on the screen to click, you can run this line of code from the dev tools console:

$("body").data().kendoMobilePane.navigate("#:back");

Handle Errors.

In a normal website, you probably don’t care about the off chance that your JS throws an unhandled exception. The browser will just catch it, stop execution of your script, and move on. In a Kendo Mobile / PhoneGap app, where just about everything is done with JS, you really don’t want the JS to just stop executing. The app will usually just appear to “lock up” if it hits an error. I recommend adding an event handler to the window.onerror event and displaying the error to the user in some way. This way they at least know the app had an error and may need to be restarted.

View widgets do not exist until the view is navigated to the first time.

If you have several divs marked data-role="view" you should be aware that these divs are not turned in to view widgets until they have to be. This means that on app startup, if you immediately try to get all the views with the code:

var views = $(".km-view");

you will probably only receive 1 view; the initial one. It isn’t until you navigate to another view that Kendo will turn the div into a view widget, and the above code will start to find the other views.

Tired of Android being upside-down?

The Android styling in Kendo likes to keep the header on the bottom and the footer on the top.
Want to reverse the direction and make it the same layout as iOS? This bit of CSS will do the trick:

.km-android .km-view {
    -moz-box-direction: initial;
    -webkit-box-direction: initial;
    -ms-box-direction: initial;
    box-direction: initial;
}

Detect if App is Running in the Simulator

You can detect if your app is running in the simulator by checking: navigator.simulator

Make Simulator Reload to Main View

If you navigate to some page in your app, say #pageTwo, then edit a file and save it, the simulator will automatically reload. However, it will keep the URL the same, so it will reload to #pageTwo. If this view requires query string parameters, for example, if it is a view to edit an item, and you pass in the item ID, then reloading to this view will result in code errors.

You can force the app to go back to the default view by removing the “hash” from the current location when you are in the simulator:

    if(navigator.simulator && window.location.hash)
        window.location.hash = "";
    new kendo.mobile.Application(document.body, {});

More to come…

That is all I have for now. As I continue to learn about Kendo Mobile, I will hopefully keep adding to this list. Hopefully this gets you past some of the confusing points of Kendo Mobile. If you have any strange things you have run into, please leave a comment, and I will see if I can dig up an answer. If you have some tips or tricks of your own, please share!

Advertisements
Tagged with: ,
Posted in KendoUI, Programming
30 comments on “Kendo Mobile Gotchas, Tips, Tricks
  1. bundyo says:

    Better use visibility: hidden instead of display: none, as with the latter your elements don’t have sizes and may not initialize properly.

    • rally25rs says:

      In the case of Kendo Mobile, you can not use visibility:hidden to hide the initial views. It uses (I think) jQuery.show() and .hide() to show/hide the views. According to the jQuery docs, .show() and .hide() just toggle the “display” css propery, not visibility. So the views end up with style=”display:block; visibility:hidden;”. You would have to manually set the visibility back to normal on the first view “show” event.

  2. user1843640 says:

    Great article… I’ve run into many of the same issues and lost a fair amount of time trying to accomplish basic tasks. I have generally found the demos, docs and overall support for Kendo mobile lacking especially when compared to other telerik products – makes it difficult to be confident when working on a professional product.

    • I feel the same, especially when working with Android, click events sporadically don’t bind, the namespace support is broken, so you can’t even patch your way out of a problem with something that does work. It’s a half built product at best, Telerik would be better off getting a decent, solid product to market, rather than evangelising about how great their stuff is. A rock solid test suite would be a comforting, so you can platform test the whole kitchen sink before you waste weeks to find that critical feature X is borked.

  3. dtimson says:

    You are a champion. Thanks for the tips.

  4. Thanks for the tips! I’m using your Music Store Mobile app as the basis for building my own mobile app and it’s been awesome!

    Can you tell me why in the utils.redirect method you pop the last history element before performing the redirect? Is this just to ensure that a user can’t use the Android back button to get to the page after the redirect?

    Thanks!
    Nathan

    • rally25rs says:

      Yes, that is the exact reason. I am just removing the page that did the redirect from the history so that the android back button won’t go back to it. Glad the article helped you!

  5. Meesha says:

    Thanks for the article! I’ve already spent a lot of time trying to fix some errors and you gave me the solution. I wish some of such explanations would be available in the Kendo UI docs.

  6. RokK says:

    More useful stuff than on enitre kendoui website. /bow

  7. paulovictorv says:

    Awesome article, it helped me seeing that I completely forgot to add “data-layout” to the views.

    But I’m having issues with the NavBar widget. Sometimes it renders properly and sometimes it doesn’t render at all, meaning that it doesn’t get initialized by Kendo. If I try clearing the cache and doing a hard reload, it works sometimes but after the next refresh it breaks again.

    Have you came around such an issue while developing with KendoUI Mobile? This issue starting showing up when I updated to the 2013.2 version.

    Thanks and again, awesome blog post, way more useful than the regular docs.

    • rally25rs says:

      Sorry, I haven’t seen the same issue with the needing yet, but I just recently updated to project to Kendo UI 2013.2 so I will keep an eye out for it.

  8. […] via Kendo Mobile Gotchas, Tips, Tricks | Coding With Spike!. […]

  9. Thanks for the tips – I’ve been using your previous “how to Kendo Mobile w/require” as a base for the first app I’m writing with Icenium / Kendo Mobile and it’s great. A beginner’s question though about this specific line here on this post :

    “So, what does this mean? Well, if you want to do anything in your init or beforeShow to set up your ViewModel, you can’t. ”

    Great to know. So my noob’s question is : how can one initialise the ViewModel correctly ?

    It’s seems totally logical for me that in an App, we store locally some variables, and some views should reflect their values at any time, including at start time (or at least the first time the view is showed).

    If we take an (unsecure) login example, let’s say we have two input fields, username and password, data-bound to corresponding fields in the login-viewmodel.

    What we want is quite straightforward – when the view is first displayed, we want the username to be initialised with what I stored locally.

    I just couldn’t make it (but I must admit that I’m quite new to javascript also, I’m more a C++/Java developer than a Web developper).

    What I did was to load those parameters in the init function of an util.js, whose function is called before this one in app.js :

    _kendoApplication = new kendo.mobile.Application(document.body, { transition: “slide” });

    Of course, it can’t work “as is”, because the definition of the username and password data fields in the login viewModel sets the desired values with the values set by the definition of utils.js variables – default values.

    The problem is exactly the same with any other view that loads user defined parameters for its first view. So, the MVVM is great and all that, but how do we initialise things ? Is it even possible (if it’s not in a perfect theoretical way, I honestly don’t care as long as it works :D) ?

    “MVVM bind happens before init, beforeShow, and show”

    It suggests me to try intercepting the “bind” and setting the correct values then but… how ?

    • rally25rs says:

      You should be able to bind to a view model as usual, and in the show or beforeShow set your initial values. The UI should detect the change and react accordingly. Something like:


      var vm = kendo.observable({
      username: "",
      password: "",

      show: function (view) {
      this.set("username", utils.getDefaultUserName());
      }
      });

      Assuming your input box is bound correctly with data-bind="value: username"
      Just make sure you are calling .set() and not assigning the value with username = ...

      • Ok, thanks for the quick answer. I understand why it couldn’t work at all.

        I’m using require to structure things, as you suggested (and it’s really nice, by the way). I followed your post on that. Which means I used this structure to build my views :

        define([“kendo”], function (kendo) {
        return {
        init: function (initEvt) {},
        beforeShow: function (beforeShowEvt) {},
        show: function (showEvt) {},

        viewModel: kendo.observable({
        message: “This rocks!”
        })
        }
        });

        with the functions init, show etc. set outside the viewModel. Every view I made is so structured.

        I gave now a try at putting the functions in the viewModel and it seems to work, except for the one tiny crucial thing I wanted to do at first: initialize those variables defined in the model in the show function.

        When I try to use the getter or the setter function, it gives me an

        “Uncaught TypeError: Object [object Object] has no method ‘set’

        if used in the show, the init or the beforeShow. On another view, with my other setting (i.e. init, show etc. outside of the model), those getter/setter work perfectly fine inside a model, but in an independant function I use to react to a click.

        This is quite puzzling, and as usual the error message doesn’t talk much to me. Do I do something wrong ?

        Here’s one small example code of a typical view that produces the error by me :

        define([‘kendo’, ‘lang’, ‘const’, ‘utils’], function (kendo, lang, constants, utils) {
        return {
        yearsModel: kendo.observable({
        //Navigation
        title: lang.lblYears,
        info: lang.lblYearsInfo,

        //Model data, initialized to default or
        //stored values

        //Model functions
        init: function (initEvt) {
        console.log(“years init”);

        },
        beforeShow: function (beforeShowEvt) {
        console.log(“years BFS”);

        },

        show: function (showEvt) {
        console.log(“years show”);
        var cluNoClub = utils.currentCluNoClub = showEvt.view.params.cluNoClub;

        var model = this;
        model.set(“info”, “test”); // <== this is the faulty line

        utils.saveOption(constants.CURRENT_CLUNOCLUB, cluNoClub);
        $.getJSON(constants.BASEURL+"yearsevents"+constants.BEGIN_RESTKEY+constants.RESTKEY+"&clubId="+cluNoClub, function(data) {

        $("#years-data").kendoMobileListView({
        dataSource: data,
        template: $("#years-data-template").html(),
        style: "inset",
        dataAppendOnRefresh:true,
        });
        });
        },

        })
        }
        });

        Here I only try to put "something" into one of the two variables set, to no avail. Any idea ?

      • rally25rs says:

        The value of this in your show callback is not the view model. The view hide, show, etc. events are not MVVM bound, they are just called by Kendo as normal functions, so do not get their context set to the viewmodel. You would have to manually save off a reference to the viewmodel. The 2 possible ways to do that are:

        define(... {
          var viewModel = kendo.observable({
            info: "",
        
            show: function (showEvt) {
              viewModel.set("info", "test");
            }
          });
        
          return viewModel;
        });
        

        Or with the show outside the viewmodel. I usually do it this way to avoid the confusion you ran into, that hide and show are not called through the MVVM framework and so aren’t bound to the view model.

        define(... {
          var viewModel = kendo.observable({
            info: ""
          });
        
          return {
            viewModel: viewModel,
            show: function (showEvt) {
              viewModel.set("info", "test");
            }
          };
        });
        

        I hope that helps!

  10. It does – I’ll revert to the “outside of model” way.

    Thanks a lot !

  11. Simon Allen says:

    Thank you for your tips. As a noob, I wish I understood more about JS in general, I’d appreciate more. However I could succesfully use your tips on filtering navigation history.

    I have a question. It took me quite a bit to figure out that Kendo is all about filtering the data at the client end. I had to trash all my server side filtering, and master-detail organization because the data is read once views or tables at a time and then filtered. So for example, I can’t really have a top level table with three columns, and then ask for the twenty column detail of a single element. I need to get the twenty column table for all views, and then filter colums for the top level view, and rows for the detail view. I could not find a way of querying the server to request details of a row. The GET is only executed the first time. I understood this after using the Hyerarchical ListView example as a starting point for my app.
    Why does everybody using Kendo take this for granted?

  12. Shawn says:

    Is your music store Kendo Mobile site available for download to learn from?

  13. Tupac says:

    Hi rally. This is great, and was very useful. I’m doing an app with Kendo and Phonegap, and what I need to do is a list that if you swipe to one direction you navigate to another page that have other lists. but I couldn’t put the list in the Kendo “scrollview” so What I did was add event liseners for the swipe and, depending on direction, is the page that navigate to. I’m using translation “slide” but, when swipe through the left, the slide direction has to change, always the page brings from the right side… I want to change that and I can’t. Can you helpme somehow?

    • rally25rs says:

      You should be able to specify the slide direction, as indicated on this doc page:

      I’m not sure how you are doing your navigation, but if you call application.navigate() directly then the 2nd parameter would be the transition, so something like: application.navigate("#myView", "slide:right"); when your user has swiped to the left.

      I hope that helps!

  14. Harry Krriss says:

    Hi Rally, That’s a great article. I am working around how can i customize kendo loding message and show my own loader with some animated gif while doing Ajax requests?

    • rally25rs says:

      You can customize the loading message using the Application.loading configuration property, but if I remember correctly, it won’t let you change it after the application has been initialized. What I usually do is to set it to some HTML with an id, for example <div id="loading-message">Loading...<div> then I can change the message using jQuery: $("#loading-message").text("Please Wait...");. You can then put that code to change the loading message text into the DataSource `requestStart` event callback, if you wanted to customize the message for a specific DataSource.

      By default Kendo Mobile will show its loading indicator when any DataSource is loading, but if you wanted to manually control the loading indicator visibility, you can use the Application.showLoading() and Application.hideLoading() methods.

  15. Jisha says:

    I have developed a mobile application that includes lot of views. Sometimes the view will not load while calling app.navigate(). And some times the newly navigated view will come under the current view. The main thing is that the issue exist only in IOS. And in Android it is working perfectly. Can you please help me?

  16. Kurt says:

    This was really helpful. thanks!

  17. Josh says:

    This is a great overview of the “intricacies” of kendo. I had naturally assumed (without checking!) that init() would be the first function to load – phew, this explains a lot. Thanks very much Spike!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

CodingWithSpike is Jeff Valore. A professional software engineer, focused on JavaScript, Web Development, C# and the Microsoft stack. Jeff is currently a Software Engineer at Virtual Hold Technologies.


I am also a Pluralsight author. Check out my courses!

%d bloggers like this: