官术网_书友最值得收藏!

  • KnockoutJS Essentials
  • Jorge Ferrando
  • 656字
  • 2021-07-23 20:16:17

Handling templates with if and ifnot bindings

You have learned how to show and hide templates with the power of jQuery and Bootstrap. This is quite good because you can use this technique with any framework you want. The problem with this type of code is that since jQuery is a DOM manipulation library, you need to reference elements to manipulate them. This means you need to know over which element you want to apply the action. Knockout gives us some bindings to hide and show elements depending on the values of our view-model. Let's update the show and hide methods and the templates.

Add both the control variables to your view-model and expose them in the return statement.

var visibleCatalog = ko.observable(true);
var visibleCart = ko.observable(false);

Now update the show and hide methods:

var showCartDetails = function () {
  if (cart().length > 0) {
    visibleCart(true);
  }
};

var hideCartDetails = function () {
  visibleCart(false);
};

var showOrder = function () {
  visibleCatalog(false);
};

var showCatalog = function () {
  visibleCatalog(true);
};

We can appreciate how the code becomes more readable and meaningful. Now, update the cart template, the catalog template, and the order template.

In index.html, consider this line:

<div class="row" id="catalogContainer">

Replace it with the following line:

<div class="row" data-bind="if: visibleCatalog">

Then consider the following line:

<div id="cartContainer" class="col-xs-6 well hidden" data-bind="template:{name:'cart'}"></div>

Replace it with this one:

<div class="col-xs-6" data-bind="if: visibleCart">
  <div class="well" data-bind="template:{name:'cart'}"></div>
</div>

It is important to know that the if binding and the template binding can't share the same data-bind attribute. This is why we go from one element to two nested elements in this template. In other words, this example is not allowed:

<div class="col-xs-6" data-bind="if:visibleCart, template:{name:'cart'}"></div>

Finally, consider this line:

<div class="row hidden" id="orderContainer" data-bind="template:{name:'order'}">

Replace it with this one:

<div class="row" data-bind="ifnot: visibleCatalog">
  <div data-bind="template:{name:'order'}"></div>
</div>

With the changes we have made, showing or hiding elements now depends on our data and not on our CSS. This is much better because now we can show and hide any element we want using the if and ifnot binding.

Let's review, roughly speaking, how our files are now:

We have our index.html file that has the main container, templates, and libraries:

<!DOCTYPE html>
<html>
<head>
  <title>KO Shopping Cart</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
  <link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>

<div class="container-fluid">
  <div class="row" data-bind="if: visibleCatalog">
    <div class="col-xs-12" data-bind="template:{name:'header'}"></div>
    <div class="col-xs-6" data-bind="template:{name:'catalog'}"></div>
    <div class="col-xs-6" data-bind="if: visibleCart">
      <div class="well" data-bind="template:{name:'cart'}"></div>
    </div>
  </div>
  <div class="row" data-bind="ifnot: visibleCatalog">
    <div data-bind="template:{name:'order'}"></div>
  </div>
  <div data-bind="template: {name:'add-to-catalog-modal'}"></div>
  <div data-bind="template: {name:'finish-order-modal'}"></div>
</div>

<!-- templates -->
<script type="text/html" id="header"> ... </script>
<script type="text/html" id="catalog"> ... </script>
<script type="text/html" id="add-to-catalog-modal"> ... </script>
<script type="text/html" id="cart-widget"> ... </script>
<script type="text/html" id="cart-item"> ... </script>
<script type="text/html" id="cart"> ... </script>
<script type="text/html" id="order"> ... </script>
<script type="text/html" id="finish-order-modal"> ... </script>
<!-- libraries -->
<script type="text/javascript" src="js/vendors/jquery.min.js"></script>
<script type="text/javascript" src="js/vendors/bootstrap.min.js"></script>
<script type="text/javascript" src="js/vendors/knockout.debug.js"></script>
<script type="text/javascript" src="js/models/product.js"></script>
<script type="text/javascript" src="js/models/cartProduct.js"></script>
<script type="text/javascript" src="js/viewmodel.js"></script>
</body>
</html>

We also have our viewmodel.js file:

var vm = (function () {
  "use strict";
  var visibleCatalog = ko.observable(true);
  var visibleCart = ko.observable(false);
  var catalog = ko.observableArray([...]);
  var cart = ko.observableArray([]);
  var newProduct = {...};
  var totalItems = ko.computed(function(){...});
  var grandTotal = ko.computed(function(){...});
  var searchTerm = ko.observable("");
  var filteredCatalog = ko.computed(function () {...});
  var addProduct = function (data) {...};
  var addToCart = function(data) {...};
  var removeFromCart = function (data) {...};
  var showCartDetails = function () {...};
  var hideCartDetails = function () {...};
  var showOrder = function () {...};
  var showCatalog = function () {...};
  var finishOrder = function() {...};
  return {
    searchTerm: searchTerm,
    catalog: filteredCatalog,
    cart: cart,
    newProduct: newProduct,
    totalItems:totalItems,
    grandTotal:grandTotal,
    addProduct: addProduct,
    addToCart: addToCart,
    removeFromCart:removeFromCart,
    visibleCatalog: visibleCatalog,
    visibleCart: visibleCart,
    showCartDetails: showCartDetails,
    hideCartDetails: hideCartDetails,
    showOrder: showOrder,
    showCatalog: showCatalog,
    finishOrder: finishOrder
  };
})();
ko.applyBindings(vm);

It is useful to debug to globalize the view-model. It is not good practice in production environments, but it is good when you are debugging your application.

Window.vm = vm;

Now you have easy access to your view-model from the browser debugger or from your IDE debugger.

In addition to the product model that we coded in the Chapter 1, Refreshing the UI Automatically with KnockoutJS, we have created a new model called CartProduct:

var CartProduct = function (product, units) {
  "use strict";
  var _product = product,
    _units = ko.observable(units);
  var subtotal = ko.computed(function(){...});
  var addUnit = function () {...};
  var removeUnit = function () {...};
  return {
    product: _product,
    units: _units,
    subtotal: subtotal,
    addUnit : addUnit,
    removeUnit: removeUnit
  };
};

You have learned how to manage templates with Knockout, but maybe you have noticed that having all templates in the index.html file is not the best approach. We are going to talk about two mechanisms. The first one is more home-made and the second one is an external library used by lots of Knockout developers, created by Jim Cowart, called Knockout.js-External-Template-Engine (https://github.com/ifandelse/Knockout.js-External-Template-Engine).

主站蜘蛛池模板: 阿鲁科尔沁旗| 凤庆县| 商南县| 鹤岗市| 晋城| 民权县| 二连浩特市| 江山市| 尤溪县| 宁陵县| 五华县| 北碚区| 呈贡县| 旬阳县| 陆丰市| 武宁县| 上虞市| 曲麻莱县| 衡水市| 仁布县| 鱼台县| 泰宁县| 张掖市| 荣成市| 临夏市| 灵山县| 绥棱县| 麻栗坡县| 金门县| 漳州市| 宜城市| 客服| 水城县| 利辛县| 博兴县| 泸定县| 托克托县| 蓬莱市| 京山县| 定西市| 洛川县|