Kendo-Knockout:窗口未正确关闭

时间:2012-12-24 11:02:31

标签: javascript knockout.js kendo-ui

我正在使用RPNiemeyer kendo-knockout库。我有一个网格。当用户点击一行网格时,会显示一个弹出窗口。当您关闭窗口并以相同的方式再次打开它时,应用程序冻结关闭动画。我尽可能地尝试用小提琴重现这个场景。 当您关闭弹出窗口并再次单击该行时,没有任何反应并重新加载浏览器。我坚信在我的申请中会发生类似的事情。

HTML:

<div data-viewId="languageList" >
    <div id="languageList" data-bind="with: viewModel">
        <div id="languageListGrid" data-bind="kendoGrid: { data: languageViewModels, columns: [ 
                { 
                    template: '<a href=\'\' data-bind=\'click: function() { onLanguageSelected(&quot;#=Language#&quot;) }\'>#=Language#</a>', 
                    field: 'Language', 
                    title: 'Language',
                    width: 50
                }

                ], 
            scrollable: false, sortable: true, pageable: false }" style="height: 380px">

        </div>
    </div>
</div>

<div data-viewid="languageDetails">
    <div id="languageDetails" data-bind="with: viewModel" class="hidden">
        <form id="languageDetailsForm" action="" style="font-family: Trebuchet MS, Verdana, Helvetica, Sans-Serif;">
        <div data-bind="kendoWindow: {isOpen: isOpen, title:'Language', width: 400, height: 200, modal: true }" >
            test
            <button id="cancelLanguage" class="k-button" data-bind="click: cancelLanguage">Cancel</button>
        </div>
       </form>
    </div>
</div>​

的javascript:

$(function () {

    var elementIsBoundNew = function (element) {
        return !!ko.dataFor(element);
    }

    var applyBindings = function (viewModel, elementId) {
        var element = $('div[data-viewId="' + elementId + '"]')[0];
        if (!elementIsBoundNew(element)) {
            var parentViewModel = { viewModel: viewModel };
            ko.applyBindings(parentViewModel, element);
        }
    };

    var FranchiseDetailsViewModel = function () {
        var 
            self = this,
            initialize = function () {
                self.languagesInfoViewModel(new LanguageListViewModel(self));
                applyBindings(self.languagesInfoViewModel, "languageList");
            };

        FranchiseDetailsViewModel.prototype.languagesInfoViewModel = ko.observable();
        initialize();
    };

    var LanguageListViewModel = function (franchise) {
        var 
            self = this,
            initialize = function () {
                var languageViewModel = new LanguageDetailsViewModel(franchise);
                self.languageViewModels.push(languageViewModel);
            };
        LanguageListViewModel.prototype.languageViewModels = ko.observableArray([]);
        LanguageListViewModel.prototype.selectedLanguageViewModel = ko.observable();

        LanguageListViewModel.prototype.onLanguageSelected = function (selectedLanguage) {
  // when you uncomment this line everyting works fine
  //var language = new LanguageDetailsViewModel();  
            self.selectedLanguageViewModel(self.languageViewModels()[0]);

            applyBindings(self.selectedLanguageViewModel, "languageDetails");

            self.selectedLanguageViewModel().openPopUp();
        };
        initialize();
    };

    var LanguageDetailsViewModel = function () {
        var 
            self = this,
            closePopUp = function () {
                self.isOpen(false);
            };

        self.Language = ko.observable("English");

        LanguageDetailsViewModel.prototype.isOpen = ko.observable(false);

        LanguageDetailsViewModel.prototype.openPopUp = function () {
            self.isOpen(true);
        };

        LanguageDetailsViewModel.prototype.cancelLanguage = function () {
            closePopUp();
        };

    };

    var initialize = new FranchiseDetailsViewModel();
});​

奇怪的是,如果我将这行代码添加到我的onLanguageSelected方法中,每个工作都很好:

var language = new LanguageDetailsViewModel();

小提琴:

http://jsfiddle.net/bZF9k/26/

非常感谢任何有关工作示例的帮助。谢谢!

根据RPNiemeyer的帖子更新:

我添加了这些代码行以使用此处Kendo-Knockout: Calling a method that changes viewmodel property from a template with data-binding inside a grid, breaks bindings的技术:

 ko.bindingHandlers.preventBinding = {
      init: function() {
          return { controlsDescendantBindings: true };
      }        
    };

    ko.bindingHandlers.kendoGrid.options.dataBound = function(data) {
      var body = this.element.find("tbody")[0];

      if (body) {
         ko.applyBindings(ko.dataFor(body), body);   
      }
    };

这正是我的应用程序中发生的事情。当我打开弹出窗口时,关闭它然后再次打开它第二次没有正确关闭。请看我更新的小提琴:

http://jsfiddle.net/bZF9k/29/

我错过了什么?再次感谢您的反馈!

2 个答案:

答案 0 :(得分:1)

看起来窗户关闭后没有正确清理。这通常不是问题(并且是可取的),但如果网格被重新渲染,则初始化新的kendoWindow,但不知道那里已经存在窗口。

可能可以在knockout-kendo代码中处理这个问题。窗口的destroy方法已被调用,因此我需要研究为什么它实际上没有删除窗口元素。

现在的解决方法是为窗口关闭时配置全局处理程序,如:

  ko.bindingHandlers.kendoWindow.options.close = function() {
      $('.k-window, .k-overlay').remove();
  };

此处示例:http://jsfiddle.net/rniemeyer/dcYRM/

答案 1 :(得分:1)

这是淘汰赛中的一个错误。

不调用窗口的destroy方法。这是因为knockout-kendo检测到从dom中删除小部件时需要销毁它。但是,kendoWindow会将元素移动到dom的末尾。当knockout.js在更新期间清除或删除元素时,它不会删除元素,因为元素已被移动。

这可以通过修改knockout-kendo来修复,这样就可以留下创建kendoWindow的元素所在的代理元素,以及destroy小部件kendoWindow代理元素被处置。对knockout-kendo 0.7.0的以下更改实现了这一点:

--- knockout-kendo-0.7.0.js 
+++ knockout-kendo-0.7.0.js
@@ -62,14 +62,19 @@
                   return { controlsDescendantBindings: true };
               }
         };

         //build the core logic for the init function
         binding.setup = function(element, options, context) {
-            var widget, $element = $(element);
+            var widget, $element = $(element), $disposeProxy;

+            // Create proxy in original location to capture when the element would have been disposed
+            if (widgetConfig.destroyByProxy) {
+                $disposeProxy = $('<div style="display: none" />').insertAfter($element);
+            }
+            
             //step 2: setup templates
             self.setupTemplates(widgetConfig.templates, options, element, context);

             //step 3: initialize widget
             widget = self.getWidget(widgetConfig, options, $element);

@@ -78,12 +83,17 @@

             //step 5: set up computed observables to update the widget when observable model values change
             self.watchValues(widget, options, widgetConfig, element);

             //step 6: handle disposal, if there is a destroy method on the widget
             if(widget.destroy) {
+                if ($disposeProxy) {
+                    ko.utils.domNodeDisposal.addDisposeCallback($disposeProxy[0], function() {
+                        widget.destroy();
+                    });
+                }
                 ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                     widget.destroy();
                 });
             }
         };

@@ -768,13 +778,16 @@
         }
     },
     watch: {
         content: CONTENT,
         title: TITLE,
         isOpen: [OPEN, CLOSE]
-    }
+    },
+    // The dom element that contains the window isn't going to be disposed when the template containing it is rendered again.
+    // This is because the window's dom element is placed at the end of the document structure by kendoWindow
+    destroyByProxy: true
 });

 createBinding({
     name: "kendoChart",
     watch: {
         data: function(value) {