KnockoutJS and CSS Grids

When I started using KnockoutJS I didn’t realize that I’d have to code differently to fit Twitter Bootstrap’s grid system.  Because with Knockout we are working with a Model-View-ViewModel architecture, you need to create your components with the grid in mind.  The best way to explain this would be  to show two contrasting examples.  On one hand I’ll show you how I would do this with php and on the other, I’ll show you how it would be best done with php.  I will be using Bootstrap’s grid system to demonstrate this example.

The Problem

Lets say that I would like to count to 10.  However, I only want 2 numbers per row.  So in other words row 1 would have 1 – 2, row 2 would have 3-4, row 3 would have 5-6 and so on.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$numbers = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

<?php foreach ( $numbers as $number ): ?>
 
  <?php if ( $number % 2 != 0 ): ?>
    <div class="row">
  <?php endif; ?>

      <div class="col-lg-6">$number</div>
 
  <?php if ( $number % 2 == 0 ): ?>
    </div>
  <?php endif; ?>
 
<?php endforeach; ?>

That is easy enough. However, if we were to try doing this in Knockout, we’d run into an error. To get this to work in Knockout is not quite as obvious, however it does create for cleaner views.

Let’s create our models and view models ViewModel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
$(document).ready(function(){

  //create a new object for each bootstrap row.
  function NumbersRow( num_cols ) {

      var self = this;
      self.num_cols = num_cols; //number of columns in each row.
      self.numbers = []; //the list of numbers that will be printed to each column

  }


  function NumbersViewModel() {

      var self = this;
      self.numbers =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; //our list of numbers.  We will need to break these out into rows and columns.
      self.numbersRows = ko.observableArray();
     
      //This function will go through our numbers array and break them town in to rows of two columns.
      self.GetNumbersRows = function() {
         
        var numbersRow = new NumbersRow(2); //create a new row with two columns

        $.each( self.numbers, function( i, number ) {

          //if our row already has two columns we will need to create a new one.
            if ( numbersRow.numbers.length == numbersRow.num_cols ) {
               
               numbersRow = new NumbersRow(2);

            }
             
            //add the number to the row.
            numbersRow.numbers.push( number );

            //if our row has two columns it can be pushed to the numbersRows array
            if ( numbersRow.numbers.length == numbersRow.num_cols ) {

               self.numbersRows.push( numbersRow );

            }
           
           
      });


    }
  }

  //create an instance of the numbers view model and apply bindings
    var numbersViewModel = new NumbersViewModel();

   numbersViewModel.GetNumbersRows();
 

   
    ko.applyBindings( numbersViewModel );


});

As you can see it appears like we had to write a little bit more code with Knockout. However, as you will see, the view will be much cleaner because we have separated out any logic into our NumbersViewModel.

For simplicity, I have removed the html, head and body tags.

1
2
3
4
5
6
7
8
9
<div class="numbers-container" data-bind="foreach: numbersRows">

    <div class="row" data-bind="foreach: numbers">

      <div class="col-lg-6" data-bind="text: $data"></div>
               
    </div>

</div>

While I still consider myself to be a novice with Knockout, I do see how this can make for more maintainable code. I do feel that programming to a view model takes some time to get used to, but I like that it really makes me think about how I am going to structure my code before I even start to write it.

Leave a Reply

Your email address will not be published. Required fields are marked *