使用knockout将数据拆分为两个bootstrap列

时间:2021-05-04 20:34:07

I use a Bootstrap carousel to display two columns of data in each carousel item. This is a simplified example of what i'm doing:

我使用Bootstrap轮播在每个轮播项目中显示两列数据。这是我正在做的简化示例:

<div class="carousel-inner col-sm-12" role="listbox" data-bind="foreach: { data: dataSlides, as: 'slide' }">
    <div class="item" data-bind="css: { active: $index() == 0 }">
        <div class="col-sm-6" data-bind="foreach: { data: dataLeft, as: 'data' }">
            <div data-bind="text: data"></div>
        </div>
        <div class="col-sm-6" data-bind="foreach: { data: dataRight, as: 'data' }">
            <div data-bind="text: data"></div>
        </div>
    </div>
</div>

and the data model i have, uses this structure:

和我拥有的数据模型,使用这种结构:

self.dataSlides = ko.observableArray(
[
    { dataLeft: ['data 1', 'data 2', 'data 3'], dataRight: ['data 4', 'data 5', 'data 6'] },
    { dataLeft: ['data 7', 'data 8', 'data 9'], dataRight: ['data 10', 'data 11', 'data 12'] }
]);

This works as expected but with one problem. The part where i display my data <div data-bind="text: data"></div> is quite complex in my real implementation and as you can see, i have to write it 2 times. Once for my left data and once for my right data.

这按预期工作但有一个问题。我显示我的数据

的部分在我的实际实现中相当复杂,正如您所看到的,我必须写2次。一次是我的左侧数据,一次是我的正确数据。

What i would like to do, is to put my data in just one data array (no dataLeft/dataRight) and display half of my data in the left column and the other half in the right column. This way i would not have to write the data display loop twice.

我想做的是将我的数据放在一个数据数组中(没有dataLeft / dataRight),并在左列显示我的一半数据,在右列显示另一半。这样我就不必两次写数据显示循环了。

To do this I need to somehow close and reopen the column tag when the foreach is through half of the data. I tried the following but this does not work because it breaks the foreach:

要做到这一点,当foreach通过一半的数据时,我需要以某种方式关闭并重新打开列标记。我尝试了以下但是这不起作用,因为它打破了foreach:

<!-- ko if: $index() == data().length / 2 -->
</div>
<div class="col-sm-6">
<!-- /ko -->

How can i do this?

我怎样才能做到这一点?

2 个解决方案

#1


1  

I think that modifying your data to simplify the view and data-binds isn't a bad idea. You just shouldn't do it manually.

我认为修改数据以简化视图和数据绑定并不是一个坏主意。你不应该手动完成它。

A good approach to these kind of problems is to work with two variables:

解决这些问题的一个好方法是使用两个变量:

  1. The (observable) data source: in your case, an array of slides
  2. (可观察的)数据源:在您的情况下,是一组幻灯片
  3. One or more data "representations": an automatic (computed) version of the data that filters, sorts or restructures it to match your view purposes.
  4. 一个或多个数据“表示”:数据的自动(计算)版本,用于过滤,排序或重构数据以符合您的查看目的。

So, for your case, I'd propose a ko.pureComputed variable that:

因此,对于您的情况,我建议使用ko.pureComputed变量:

  1. Watches the original data source and automatically computes,
  2. 观看原始数据源并自动计算,
  3. x rows with each y columns with each z slides.
  4. x行,每个y列,每个z幻灯片。

A sample implementation (not the "smartest" way to do it, I tried to make it easy to read... and don't mind the styling):

一个示例实现(不是“最聪明”的方式,我试图让它易于阅读......并且不介意样式):

var dataSlides = ko.observableArray(["slide 1","slide 2","slide 3","slide 4","slide 5","slide 6","slide 7","slide 8","slide 9","slide 10","slide 11","slide 12"]);

var dataTable = ko.pureComputed(function() {
  var rows = [];
  var slidesPerCol = 3;
  var colPerRow = 2;
  var slidesPerRow = slidesPerCol * colPerRow;
  
  dataSlides().forEach(function(slide, index) {
    var rowNr = Math.floor(index / slidesPerRow);
    var colNr = Math.floor((index % slidesPerRow) / slidesPerCol);
    
    rows[rowNr] = rows[rowNr] || getRow(colPerRow);
    rows[rowNr][colNr].push(slide);
  });
  
  return rows;
});

ko.applyBindings({ 
  dataTable: dataTable, 
  pushSlide: function(){
    dataSlides.push("slide " + dataSlides().length);
  }
});

// Util
function getRow(c, s) {
   return Array(c)
     .fill(null)
     .map(function() { return []; }); 
}
.table {
  border: 1px solid black;
}

.row {
  display: flex;
  justify-content: space-between;
}

.row:nth-child(odd) {
  background: rgba(0,0,0,0.1);
}

.col {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: stretch;
}

.col + .col { border-left: 1px solid black; }

.slide {
  flex: 1;
  text-align: center;
  padding: .5rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div data-bind="foreach: { data: dataTable, as: 'row' }" class="table">
  <div data-bind="foreach: { data: row, as: 'col' }" class="row">
    <div data-bind="foreach: { data: col, as: 'slide'}" class="col">
      <span data-bind="text: slide" class="slide"></span>
    </div>
  </div>
</div>
<button data-bind="click: pushSlide">add slide</button>

#2


0  

I think u should have a container for the left column and one for the right column, and foreach half the list in each ones.

我想你应该有一个左列的容器和一个右列的容器,并且每个容器的列表的一半。

Like this example: https://jsfiddle.net/Salmin/2ytouxvs/2/

像这个例子:https://jsfiddle.net/Salmin/2ytouxvs/2/

self.dataSlides = ko.observableArray(
  [
      ['data 1a', 'data 2a', 'data 3a', 'data 4a', 'data 5a', 'data 6a'],
      ['data 1b', 'data 2b', 'data 3b', 'data 4b', 'data 5b', 'data 6b']
  ]);


<div class="carousel-inner col-sm-12" role="listbox" data-bind="foreach: dataSlides, as: 'slide'">

  <div class="item-2">
    <div class="col-sm-6" data-bind="foreach: $data">
    <!-- ko if: $index()+1 < $data.length / 2 -->
      <div data-bind="text: $data"></div>
    <!-- /ko -->
    </div>
  </div>

  <div class="item-2">
    <div class="col-sm-6" data-bind="foreach: $data">
    <!-- ko if: $index()+1 > $data.length / 2 -->
      <div data-bind="text: $data"></div>
    <!-- /ko -->
    </div>
  </div>

</div>

#1


1  

I think that modifying your data to simplify the view and data-binds isn't a bad idea. You just shouldn't do it manually.

我认为修改数据以简化视图和数据绑定并不是一个坏主意。你不应该手动完成它。

A good approach to these kind of problems is to work with two variables:

解决这些问题的一个好方法是使用两个变量:

  1. The (observable) data source: in your case, an array of slides
  2. (可观察的)数据源:在您的情况下,是一组幻灯片
  3. One or more data "representations": an automatic (computed) version of the data that filters, sorts or restructures it to match your view purposes.
  4. 一个或多个数据“表示”:数据的自动(计算)版本,用于过滤,排序或重构数据以符合您的查看目的。

So, for your case, I'd propose a ko.pureComputed variable that:

因此,对于您的情况,我建议使用ko.pureComputed变量:

  1. Watches the original data source and automatically computes,
  2. 观看原始数据源并自动计算,
  3. x rows with each y columns with each z slides.
  4. x行,每个y列,每个z幻灯片。

A sample implementation (not the "smartest" way to do it, I tried to make it easy to read... and don't mind the styling):

一个示例实现(不是“最聪明”的方式,我试图让它易于阅读......并且不介意样式):

var dataSlides = ko.observableArray(["slide 1","slide 2","slide 3","slide 4","slide 5","slide 6","slide 7","slide 8","slide 9","slide 10","slide 11","slide 12"]);

var dataTable = ko.pureComputed(function() {
  var rows = [];
  var slidesPerCol = 3;
  var colPerRow = 2;
  var slidesPerRow = slidesPerCol * colPerRow;
  
  dataSlides().forEach(function(slide, index) {
    var rowNr = Math.floor(index / slidesPerRow);
    var colNr = Math.floor((index % slidesPerRow) / slidesPerCol);
    
    rows[rowNr] = rows[rowNr] || getRow(colPerRow);
    rows[rowNr][colNr].push(slide);
  });
  
  return rows;
});

ko.applyBindings({ 
  dataTable: dataTable, 
  pushSlide: function(){
    dataSlides.push("slide " + dataSlides().length);
  }
});

// Util
function getRow(c, s) {
   return Array(c)
     .fill(null)
     .map(function() { return []; }); 
}
.table {
  border: 1px solid black;
}

.row {
  display: flex;
  justify-content: space-between;
}

.row:nth-child(odd) {
  background: rgba(0,0,0,0.1);
}

.col {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: stretch;
}

.col + .col { border-left: 1px solid black; }

.slide {
  flex: 1;
  text-align: center;
  padding: .5rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div data-bind="foreach: { data: dataTable, as: 'row' }" class="table">
  <div data-bind="foreach: { data: row, as: 'col' }" class="row">
    <div data-bind="foreach: { data: col, as: 'slide'}" class="col">
      <span data-bind="text: slide" class="slide"></span>
    </div>
  </div>
</div>
<button data-bind="click: pushSlide">add slide</button>

#2


0  

I think u should have a container for the left column and one for the right column, and foreach half the list in each ones.

我想你应该有一个左列的容器和一个右列的容器,并且每个容器的列表的一半。

Like this example: https://jsfiddle.net/Salmin/2ytouxvs/2/

像这个例子:https://jsfiddle.net/Salmin/2ytouxvs/2/

self.dataSlides = ko.observableArray(
  [
      ['data 1a', 'data 2a', 'data 3a', 'data 4a', 'data 5a', 'data 6a'],
      ['data 1b', 'data 2b', 'data 3b', 'data 4b', 'data 5b', 'data 6b']
  ]);


<div class="carousel-inner col-sm-12" role="listbox" data-bind="foreach: dataSlides, as: 'slide'">

  <div class="item-2">
    <div class="col-sm-6" data-bind="foreach: $data">
    <!-- ko if: $index()+1 < $data.length / 2 -->
      <div data-bind="text: $data"></div>
    <!-- /ko -->
    </div>
  </div>

  <div class="item-2">
    <div class="col-sm-6" data-bind="foreach: $data">
    <!-- ko if: $index()+1 > $data.length / 2 -->
      <div data-bind="text: $data"></div>
    <!-- /ko -->
    </div>
  </div>

</div>