9

UPDATE: Here is a link to reproduce the problem

RELATED: This is another question of mine where similar problems are happening with Kendo UI Map, maybe it could help someone figure this one out! It has one failing and one working version.


I use Kendo UI's DataSource, DropDownList and Map in an Angular single-page application.

I want to use the same DataSource object for both the DropDownList and the Map. However, the Map behaves in a very unpredictable manner.

  1. When I put the DropDownList before the Map in the template, only the DropDownList gets populated. Inspecting the network traffic reveals that indeed only one request is being made. When I put the Map first, both of them get populated and two requests are made.
  2. When I don't use any promises in transport.read, but just call options.success immediately with a static value, everything works as expected. Two calls are being made.

I've been pulling my hair over this the entire work day, so any help is highly appreciated.

The data source service:

m.factory('ourDataSource', function(foo, bar, baz) {
    return new kendo.data.DataSource({
        transport: {
            read: function(options) {
                foo().then(function (result) {
                    return bar(result);
                }).then(function (result) {
                    return baz(result);
                }).then(function (result) {
                    options.success(result);
                }).catch(function (err) {
                    options.error(err);
                });
            }
        }
    });
});

The controller:

m.controller('ourController', ['ourDataSource', function(ourDataSource) {

    // set the data source of the dropdownlist
    this.ourDataSource = ourDataSource;

    // set up the map layers
    this.mapLayers = [{
        type: 'tile',
        urlTemplate: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/#= zoom #/#= y #/#= x #',
    }, {
        type: 'marker',
        dataSource: ourDataSource, // the same data source as before
        locationField: 'Position',
        titleField: 'Title'
    }];
}]);

The view:

<div ng-controller="ourController as ctrl">

    <select kendo-drop-down-list
            k-data-text-field="'Title'"
            k-data-value-field="'Title'"
            k-data-source="ctrl.ourDataSource"></select>

    <div kendo-map
         k-zoom="2"
         k-center="[1, 1]"
         k-layers="ctrl.mapLayers">
    </div>

</div>

What am I missing here?

Community
  • 1
  • 1
damd
  • 6,116
  • 7
  • 48
  • 77
  • But, you _don't want_ two read requests. It's a shared data source, so, why would you want it to request the same data twice? As for why it's not binding to the data, we'd probably need a public example that duplicates this behavior. It's hard to deduce as-is. – Brett Mar 03 '16 at 16:09
  • @Brett: Indeed, I don't want two requests, but even when I disable autoBind and call `fetch` manually, the result is the same. I felt like it would be easier to figure out if I reduced the test case to the simplest possible. I'll see if I can set up a public example. – damd Mar 03 '16 at 16:11
  • Could it be because `ourDataSource` is a factory that's returning a __new__ datasource object? Try returning it as a singleton. – Brett Mar 03 '16 at 16:18
  • @Brett: I'll try your suggestion. Meanwhile, I wrote an example displaying the problem in action, see the edited question! – damd Mar 03 '16 at 16:26
  • @Brett: When I think about it, `factory` already creates a singleton in AngularJS, unless I'm mistaken? – damd Mar 03 '16 at 16:29
  • Yea, I'm not sure either. It was a guess. Unfortunately, I can't get the dojo to run because work blocks it. I'll try from home later. – Brett Mar 03 '16 at 16:55
  • @Brett: Have you had a chance to check it out yet? :) – damd Mar 07 '16 at 08:26
  • Odd behavior. I'm at a loss to why this is occurring. – Brett Mar 08 '16 at 05:25
  • Just to confirm : From the another question that you had posted, If we apply the same fix to this, it is working fine I guess. [Dojo Link](http://dojo.telerik.com/efeVe/7). Am I right? – Keerthi Kumar P Mar 08 '16 at 13:44
  • @pathrik: Yes, I think so. The questions are slightly different though, even though the cause might be the same. For example, in this question, everything works if I put the map before the dropdown in the template, but not the other way around. The other question is specifically about why `k-layers` and `k-options` behave differently. – damd Mar 08 '16 at 13:51
  • Does this problem still occur with a different promise library other than `$q`, say `$http`? – Brett Mar 08 '16 at 16:33
  • @Brett: It happens when I use `$resource` as well, which I think uses `$http`. In fact, that's how I noticed the problem, I just happened to reproduce it using `$q`. I'm not sure, but I think `$http` probably uses `$q` itself! – damd Mar 09 '16 at 08:59

2 Answers2

0

I believe that this might be a bug in the Kendo UI Map widget, since the behavior occurring here isn't at all what one would expect. However, I do have a workaround solution. Rather than return the data source as a singleton object, return it as a function. This is probably not ideal, but it works.


angular.module('ourModule', ['kendo.directives'])
.factory('getDataSource', function ($q) {
  return function() {  // return a function that creates a new data source
    return new kendo.data.DataSource({
      transport: {
        read: function (options) {
          $q.when([
            {Position: [1, 1], Title: 'First place'},
            {Position: [10, 10], Title: 'Second place'}
          ]).then(function (result) {
            options.success(result);
          });
        }
      }
    });
  };
})
.controller('ourController', function (getDataSource) {
  this.ourDataSource = getDataSource();      
  this.mapLayers = [{
    type: 'tile',
    urlTemplate: '...removed for brevity...'
  }, {
    type: 'marker',
    dataSource: getDataSource(),
    locationField: 'Position',
    titleField: 'Title'
  }];
});
Brett
  • 4,268
  • 1
  • 13
  • 28
0

Factory mostly used to create instances on demand. See this example

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

 app.factory('dataSourceFactory', function($q) {

  function dataSourceFactory() {}

  dataSourceFactory.prototype = {
   contentTypes: function() {
    return new kendo.data.DataSource({
     transport: {
      read: function(options) {
       $q.when(
         [{
          Position: [1, 1],
          Title: 'First place'
         }, {
          Position: [10, 10],
          Title: 'Second place'
         }])
        .then(function(result) {
         options.success(result);
        });
      }
     }
    })
   }
  };

  return dataSourceFactory;
 });

 app.controller('ourController', ['$scope', 'dataSourceFactory',

  function($scope, dataSourceFactory) {

   var dataSourceFactory = new dataSourceFactory();

   $scope.mapLayers = [{
    type: 'tile',
    urlTemplate: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/#= zoom #/#= y #/#= x #',
   }, {
    type: 'marker',
    dataSource: dataSourceFactory.contentTypes(), // the same data source as before
    locationField: 'Position',
    titleField: 'Title'
   }];

   $scope.ourDataSource = dataSourceFactory.contentTypes();
  }
 ]);
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.common.min.css">
  <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.rtl.min.css">
  <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.default.min.css">
  <link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.3.930/styles/kendo.mobile.all.min.css">

  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  <script src="http://kendo.cdn.telerik.com/2015.3.930/js/angular.min.js"></script>
  <script src="http://kendo.cdn.telerik.com/2015.3.930/js/jszip.min.js"></script>
  <script src="http://kendo.cdn.telerik.com/2015.3.930/js/kendo.all.min.js"></script>

<div ng-app="ourModule">
  
  <div ng-controller="ourController">
      
    <kendo-drop-down-list k-data-source="ourDataSource"
                          k-data-text-field="'Title'"
                          k-data-value-field="'Title'">
    </kendo-drop-down-list>

    <kendo-map k-zoom="2"
               k-layers="mapLayers">
    </kendo-map>
    
  </div>
</div>

See this JSFiddle demo

111
  • 1,779
  • 1
  • 12
  • 15