2013-10-31 25 views
3

根據建議給予here,以及給出的信息here關於如何爲forEach做一個自定義bindingHandler,我決定嘗試寫我自己的自定義綁定forEach和砌體。自定義綁定爲砌體如何將適當調整容器

因爲元素是在動態添加的,所以不會重新繪製和移動元素以填充空間。因此,在添加每個項目之後呈現或調用元素之後,需要移動此功能。

這裏是我的bindingHandler

ko.bindingHandlers.masonry = { 
init: function (element, valueAccessor, allBindingsAccessor) { 
    var $element = $(element), 
     originalContent = $element.html(); 
    $element.data("original-content", originalContent); 
    //var msnry = new Masonry($element); 
    return { controlsDescendantBindings: true } 

}, 
update: function (element, valueAccessor, allBindingsAccessor) { 
    var value = ko.utils.unwrapObservable(valueAccessor()), 

    //get the list of items 
    items = value.items(), 

    //get a jQuery reference to the element 
    $element = $(element), 

    //get the current content of the element 
    elementContent = $element.data("original-content"); 

    $element.html(""); 

    var container = $element[0]; 
    var msnry = new Masonry(container); 

    for (var index = 0; index < items.length; index++) { 
     (function() { 
      //get the list of items 
      var item = ko.utils.unwrapObservable(items[index]), 
       $childElement = $(elementContent); 

      ko.applyBindings(item, $childElement[0]); 

      //add the child to the parent   
      $element.append($childElement); 
      msnry.appended($childElement[0]); 

     })(); 

     msnry.layout(); 
     msnry.bindResize(); 
    } 
} 

}; 

和執行處理程序的HTML。

<div id="criteriaContainer" data-bind="masonry: { items: SearchItems.Items }"> 
    <div class="searchCriterion control-group"> 
     <label class="control-label" data-bind="text: Description"></label> 
     <div class="controls"> 
      <input type="hidden" data-bind="value: Value, select2: { minimumInputLength: 3, queryUri: SearchUri(), placeholder: Placeholder(), allowClear: true }" style="width: 450px"> 
     </div> 
     <p data-bind="text: Value"></p> 
    </div> 
</div> 

當這顯示在頁面上它所有疊加如果通過追加方式呈現的右上彼此頂部的元素。

你可以在我的bindingHandler中看到我調用了bindResize以及layout(),這兩者似乎都沒有任何作用。

下面是它在UI中的外觀截圖。 Masonry example with Knockout

回答

2

我做了定製綁定是基於別人的定製同位素結合:https://github.com/aknuds1/knockout-isotope/blob/master/lib/knockout-isotope.js

注意:自定義的作者同位素使用修改版本淘汰賽的綁定。下面的綁定使用標準挖空庫(我正在使用v3.3.0)。

讓自定義綁定工作的技巧是使用afterAdd回調跟蹤添加的元素,以便將它們附加到砌體對象。

"use strict"; 

(function() { 
    var $container, haveInitialized, newNodes = [], itemClass, masonryOptions; 

    function afterAdd(node, index, item) { 
     if (node.nodeType !== 1) { 
      return; // This isn't an element node, nevermind 
     } 
     newNodes.push(node); 
    } 

    ko.bindingHandlers.masonry = { 
     defaultItemClass: 'grid-item', 
     // Wrap value accessor with options to the template binding, 
     // which implements the foreach logic 
     makeTemplateValueAccessor: function (valueAccessor) { 
      return function() { 
       var modelValue = valueAccessor(), 
        options, 
        unwrappedValue = ko.utils.peekObservable(modelValue); // Unwrap without setting a dependency here 

       options = { 
        afterAdd: afterAdd 
       }; 

       // If unwrappedValue.data is the array, preserve all relevant 
       // options and unwrap value so we get updates 
       ko.utils.unwrapObservable(modelValue); 
       ko.utils.extend(options, { 
        'foreach': unwrappedValue.data, 
        'as': unwrappedValue.as, 
        'includeDestroyed': unwrappedValue.includeDestroyed, 
        'templateEngine': ko.nativeTemplateEngine.instance 
       }); 
       return options; 
      }; 
     }, 
     'init': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
      console.log({ msg: 'Initializing binding' }); 

      itemClass = ko.bindingHandlers.masonry.defaultItemClass; 
      masonryOptions = {}; 
      haveInitialized = false; 
      $container = $(element); 

      var parameters = ko.utils.unwrapObservable(valueAccessor()); 
      if (parameters && typeof parameters == 'object' && !('length' in parameters)) { 
       if (parameters.masonryOptions) { 
        var clientOptions; 
        if (typeof parameters.masonryOptions === 'function') { 
         clientOptions = parameters.masonryOptions(); 
         if (typeof clientOptions !== 'object') { 
          throw new Error('masonryOptions callback must return object'); 
         } 
        } else if (typeof parameters.masonryOptions !== 'object') { 
         throw new Error('masonryOptions must be an object or function'); 
        } else { 
         clientOptions = parameters.masonryOptions; 
        } 
        ko.utils.extend(masonryOptions, clientOptions); 
       } 
       if (parameters.itemClass) { 
        itemClass = parameters.itemClass; 
       } 
      } 

      // Initialize template engine, moving child template element to an 
      // "anonymous template" associated with the element 
      ko.bindingHandlers.template.init(
       element, 
       ko.bindingHandlers.masonry.makeTemplateValueAccessor(valueAccessor) 
      ); 

      return { controlsDescendantBindings: true }; 
     }, 
     'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
      ko.bindingHandlers.template.update(element, 
        ko.bindingHandlers.masonry.makeTemplateValueAccessor(valueAccessor), 
        allBindingsAccessor, viewModel, bindingContext); 

      // Make this function depend on the view model, so it gets called for updates 
      var data = ko.bindingHandlers.masonry.makeTemplateValueAccessor(
         valueAccessor)().foreach; 
      ko.utils.unwrapObservable(data); 

      if (!haveInitialized) { 
       masonryOptions.itemSelector = '.' + itemClass; 
       console.log({msg: 'Binding update called for 1st time, initializing Masonry', options: masonryOptions}); 
       $container.masonry(masonryOptions); 
      } 
      else { 
       console.log({ msg: 'Binding update called again, appending to Masonry', elements: newNodes }); 
       var newElements = $(newNodes); 
       $container.masonry('appended', newElements); 
       $container.masonry('layout'); 
       newNodes.splice(0, newNodes.length); // reset back to empty 
      } 

      // Update gets called upon initial rendering as well 
      haveInitialized = true; 
      return { controlsDescendantBindings: true }; 
     } 
    }; 
})(); 

這裏是使用結合的一個例子:

<div class="grid" data-bind="masonry: {data: blogEntries, masonryOptions: { itemClass: 'grid-item', columnWidth: 320, gutter: 10}}"> 
    <div class="grid-item"> 
     <div data-bind="css: {'idea-blog': isIdea }"> 
      <img data-bind="attr: { src: imageUrl }"> 
      <h2 data-bind="text: title"></h2> 
      <p data-bind="text: description"></p> 
      <div class="button-keep-reading"> 
       <a data-bind="attr: { src: articleUrl }"><span data-bind="text: linkText"></span> &gt;</a> 
      </div> 
     </div> 
    </div> 
</div> 

要知道,你要確保你使用的是在你的磚石瓦片被加載任何圖片綁定,因爲數據之前砌體在其他方面存在佈置問題。

相關問題