2013-10-31 5 views
3

here에 대한 제안과 forEach에 대한 custom bindingHandler를 만드는 방법에 대해 here에 제공된 정보에 따라 필자는 사용자 정의 바인딩을 작성하려고 시도했습니다. forEach와 Masonry.벽돌을위한 사용자 정의 바인딩 사용 방법

요소가 즉석에서 추가되므로 공간을 채우기 위해 요소를 다시 그리거나 이동하는 일이 발생하지 않습니다. 따라서 각 항목을 추가 한 후에 요소를 렌더링하거나 호출 한 후에이 기능을 이동해야했습니다.

은 여기 내 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에서 보이는 모양의 스크린 샷입니다. https://github.com/aknuds1/knockout-isotope/blob/master/lib/knockout-isotope.js

참고 : Masonry example with Knockout

답변

2

는 내가 만든 사용자 정의 바인더는 다른 사람이 사용자 정의 동위 원소에 대한 바인딩 것을 기반으로 사용자의 저자는 바인딩 녹아웃의 수정 버전을 사용하고 동위 원소. 아래 바인딩은 표준 녹아웃 라이브러리 (v3.3.0 사용)를 사용합니다.

사용자 정의 바인딩을 작동시키는 트릭은 afterAdd 콜백을 사용하여 추가 된 요소를 추적하여 masonry 오브젝트에 추가 할 수 있도록하는 것입니다. 당신이 데이터 때문에 바인딩하기 전에

<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> 

당신이로드 당신의 벽돌 타일에 사용 된 모든 이미지를 보장 할 것이라는 점에 유의하십시오 여기

"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 }; 
     } 
    }; 
})(); 

는 사용 바인딩의 예입니다 벽돌은 그렇지 않으면 문제가 레이아웃 문제가있다.