使用KnockoutJS在所有图像加载(并执行某些操作)时捕获

时间:2013-03-16 03:39:28

标签: javascript knockout.js jquery-deferred

我目前有一个基本实现,可以在所有图像都已加载时捕获,但我对UI和viewModel之间的代码,可伸缩性和某种紧密耦合感到不满意。

我有一系列的promises,我已在viewModel和bindingHandler之外声明,以便每个人都可以访问它。 bindingHandler会将每个未解析的promise推送到数组中,init函数将等待,直到数组收到所有已解析的promise。一旦发生这种情况,就会调用一个函数来为所有图像设置一个统一的高度。

以下是用户界面:

<ul data-bind="foreach: movies">
    <li>
        <div class="image">
            <img data-bind="imageLoad: { src: posters.Detailed, alt: title }"/>
        </div>
    </li>
</ul>

另一方:

function ViewModel() {
    var self = this;
    self.movies = ko.observableArray([]);

    self.init = function(data) {
        self.isLoading(true);

        self.movies = ko.utils.arrayMap(data, function(item) {
            return new robot.ko.models.Movie(item);
        });

        $.when.apply($, promises).done(function () {
            robot.utils.setThumbnailHeight($('.thumbnails li'), function () {
                self.isLoading(false);
            });
        });
    };
}

ko.bindingHandlers.imageLoad = {
    init: function(element, valueAccessor) {
        var options = valueAccessor() || {};
        var promise = $.Deferred();

        var loadHandler = function() { return promise.resolve; };
        promises.push(promise);

        ko.applyBindingsToNode(element, {
            event: { load: loadHandler },
            attr: { src: options.src, alt: options.alt }
        });     
    }
}

在一个完美的世界中,我的viewModel只会将回调切换到我的bindingHandler,但它会是动态的,它不需要知道关于UI的任何内容,而bindingHandler将能够找出它需要哪个元素调整。我已经玩弄了某种deferredObservable的想法,但最后我只是不满意在viewModel中。我还想过在父元素上使用data-属性来知道实际设置高度的元素,但这似乎很草率。

我觉得我错过了什么。

所以,我问,有什么更好的方法来实现这一点,这将允许松耦合和可扩展性,这样我就不必专门告诉UI做什么(尽可能多)以及何时,来自我的viewModel?

1 个答案:

答案 0 :(得分:1)

在处理它的foreach范围内使用自定义绑定。例如:

<ul data-bind="foreach: movies, uniformHeight: movies">
    <li>
        <div class="image">
            <img data-bind="imageLoad: { src: posters.Detailed, alt: title }"/>
        </div>
    </li>
</ul>

基于数据的方法

根据ViewModel的形状做出假设 - 具体来说,假设promises的状态反映了加载图像的实际状态。 bindingHandler看起来像这样:

ko.bindingHandlers.uniformHeight = {
  init: function(element, valueAccessor) {
      //assume valueAccessor contains an array of promises
      $.when.apply($, valueAccessor()()).done(function () {
            robot.utils.setThumbnailHeight($(element).find('.thumbnails li'), function () {
                self.isLoading(false);
            });
      });
  }
};

这可能需要对你如何处理robot.ko.models.Movie中的逻辑进行更多的重组,但是根据你发布的内容,没有什么大不了的。

基于资源的方法

需要更多管道,但最终可能会更可靠。

您的外部uniformHeight绑定可以在bindingContext上注册并等待子imageLoad绑定到每个绑定事件,以通知图像已加载;当他们回来时可以积累高度并且可能会去抖。