Quantcast
Channel: Kendo UI – Falafel Software Blog
Viewing all articles
Browse latest Browse all 43

Manipulating a kendo view on show

$
0
0

When calling a kendo view there is a timing issue related to interacting with or manipulating a data-bound control and its child items in the DOM. In my case I needed to automatically scroll to the bottom of a MVVM template/source bound table each time the view was loaded – not the kendo grid control. The problem stemmed from a couple core issues:

  1. Most browsers will not properly determine the scrollHeight property of a hidden element.
  2. Kendo seems to render in the DOM the view (from the template) it is going to show but keeps i t’display:none’, even during the ‘show’ event while at the same time the view we are leaving is also still in the DOM but is visible.
  3. There is no  ‘view ready’ type event that fires after the current view is fully displayed in the DOM. As mentioned above the ‘show’ event and other events such as ‘transitionStart’ and ‘transitionEnd’ are executed with the view still hidden in the DOM.

The result in my case was that on initial load the scrollToBottom method would fire successfully since there was no transition of views going on (therefore the view showing was never hidden in the DOM). But on any subsequent visits to any page using that view it didn’t work.

My solution was to create a custom kendo widget that would be simple and lightweight and simple like the template/source MVVM bindings yet was aware of its datasource state and exposed events such as dataBound and dataBinding that I could hook into to see exactly when the elements were being rendered in their parent container.

var Repeater = kendo.ui.Widget.extend({
            init: function (element, options) {

                var that = this;

                kendo.ui.Widget.fn.init.call(that, element, options);
                that.template = kendo.template(that.options.template || $("#table-tmp").html());

                that._dataSource();
                that.trigger("dataBound");
                
                //just sets the element to the same height as its parent 
                var toheight = $(this.element).parent().height();
                $(this.element).height(toheight);
            },
            options: {
                name: "Repeater",
                autoBind: true,
                template: ""
            },
            refresh: function () {
                var that = this,
                    view = that.dataSource.view(),
                    html = kendo.render(that.template, view);
                that.trigger("dataBinding");
                //this is where the UI is built
                that.element.html(html);
                that.trigger("dataBound");
            },
            _dataSource: function () {

                var that = this;

                if (that.dataSource && that._refreshHandler) {
                    that.dataSource.unbind("change", that._refreshHandler);
                }
                else {
                    that._refreshHandler = $.proxy(that.refresh, that);
                }

                that.dataSource = kendo.data.DataSource.create(that.options.dataSource);
                that.dataSource.bind("change", that._refreshHandler);

                if (that.options.autoBind) {
                    that.dataSource.fetch();
                }
            },
            events: ["dataBinding", "dataBound"],
            items: function () {
                return this.element.children();
            },
            setDataSource: function (dataSource) {
                this.options.dataSource = dataSource;
                this._dataSource();
            }
});
//register the widget
kendo.ui.plugin(Repeater);

I could then use MVVM to still bind my new widget.

<table id="repeater" class="tableSection">
            <thead></thead>
            <tbody data-role="repeater" data-bind="source: Items, events: { dataBound: scrollBottom } "></tbody>
</table>

I listen to the ‘dataBound’ event which fires after the data items have been injected into the DOM as you can see above in the ‘refresh’ section of the custom widget. It was important to listen here because when a transition between views of the same type was being made the aforementioned events (show, etc) were executing while the DOM contains the markup for BOTH views. This occurs even if you are reusing the same view model. Kendo will generate the markup for the new view and inject it into the DOM before destroying the old one. This is a pain because you now have duplicated IDs on the page during the view’s show events and you cannot target elements via ID accurately at this point. The ‘dataBound’ event exposes the element making the request which is the one transitioning into view. We can also target the view-wrapper which kendo is keeping hidden and show it to get an accurate scrollHeight. Since the very next action that fires in the rendering of a view is to show this element anyways, it doesn’t break any animations or cause any undesired effects.

//...View definition...
model: kendo.observable(
                {
                    Items: [],
                    scrollBottom: function (e) {
                        //forces the view wrapper parent to visible so we can get an accurate scrollHeight
                        $(".view-wrp").parent().show();
                        var d = $(e.sender.element);
                        d.scrollTop(d.prop("scrollHeight"));
                    }
                })

With these changes I was able to effectively manipulate my table while the view was being shown without implementing any hacky timers.

 

 

The post Manipulating a kendo view on show appeared first on Falafel Software Blog.


Viewing all articles
Browse latest Browse all 43

Trending Articles