Van M. Tran
Van M. Tran

Reputation: 112

Knockout.js - how to cross-reference objects between two arrays

I have 2 models:

var ProductModel = function(productid, productname, producttypeid, productprice) {
  var self = this;
  self.ProductId = productid; 
  self.ProductName = productname;
  self.ProductTypeId = producttypeid;
  self.ProductPrice = productprice;
}

and

var ProductTypeModel= function(producttypeid, producttypename) {
  var self = this;
  self.ProductTypeId = producttypeid; 
  self.ProductTypeName = producttypename;
}

I use them in a viewmodel:

var ProductViewModel = function() {
  var self = this;
  self.ProductList = ko.observableArray([]);
  self.ProductTypeList = ko.observableArray([]);

  //...init 2 array and some misc methods
}

In html file:

...
<table>
  <thead>
   <tr>
    <th>Id</th>
    <th>Type</th>
    <th>Name</th>
    <th>Price</th>
   </tr>
  </thead>
  <tbody data-bind="template: { name: 'row', foreach: ProductList }">
  </tbody>
</table>
...
<script id="row" type="html/template">
  <tr>
    <td data-bind="html: ProductId"></td>
    <td data-bind="html: ProductName"></td>
    <td data-bind="html: ProductTypeId"></td> <!-- I stuck here!!! -->
    <td data-bind="html: ProductPrice"></td>
  </tr>
</script>
...

I want my table show ProductTypeName instead of ProductTypeId but I can't pass the ProductTypeId into any function to get ProductTypeName.

Upvotes: 3

Views: 347

Answers (2)

Jeff Mercado
Jeff Mercado

Reputation: 134881

You have two independent collections that are associated with each other. You could create a model that can manage that association through computed observables mapping the combined objects.

function ProductListModel (products, productTypes) {
    this.Products = ko.observableArray(
        ko.utils.arrayMap(products, function (product) {
            return new ProductModel(
                product.productId,
                product.productName,
                product.productTypeId,
                product.productPrice);
        })
    );
    this.ProductTypes = ko.observableArray(
        ko.utils.arrayMap(productTypes, function (productType) {
            return new ProductTypeModel(
                productType.productTypeId,
                productType.productTypeName);
        })
    );

    this.AssociatedProducts = ko.dependentObservable(function () {
        var products = this.Products(),
            productTypes = this.ProductTypes();
        return ko.utils.arrayMap(products, function (product) {
            var productType = ko.utils.arrayFirst(productTypes, function (productType) {
                return product.ProductTypeId === productType.ProductTypeId;
            });
            return {
                ProductId: product.ProductId,
                ProductName: product.ProductName,
                ProductType: productType.ProductTypeName,
                ProductPrice: product.ProductPrice,
            };
        });
    }, this);
}
function ViewModel(data) {
    var p = new ProductListModel(data.products, data.productTypes);
    this.Products = p.Products;
    this.ProductTypes = p.ProductTypes;
    this.AssociatedProducts = p.AssociatedProducts;
}
<tbody data-bind="template: { name: 'row', foreach: AssociatedProducts }">
</tbody>
...
<script id="row" type="html/template">
    <tr>
        <td data-bind="html: ProductId"></td>
        <td data-bind="html: ProductName"></td>
        <td data-bind="html: ProductType"></td>
        <td data-bind="html: ProductPrice"></td>
    </tr>
</script>

Fiddle

Upvotes: 1

Tomalak
Tomalak

Reputation: 338228

var ProductViewModel = function() {
  var self = this;
  self.ProductList = ko.observableArray([]);
  self.ProductTypeList = ko.observableArray([]);

  //...init 2 array and some misc methods

  self.getProductTypeName = function (productTypeId) {
    var typeId = ko.unwrap(productTypeId),
        found = ko.utils.arrayFirst(self.ProductTypeList(), function (productType) {
           return ko.unwrap(productType.ProductTypeId) === typeId;
         });

    return found ? ko.unwrap(found.ProductTypeName) : null;
  };
};

and

<script id="row" type="html/template">
  <tr>
    <td data-bind="html: ProductId"></td>
    <td data-bind="html: ProductName"></td>
    <td data-bind="html: $root.getProductTypeName(ProductTypeId)"></td>
    <td data-bind="html: ProductPrice"></td>
  </tr>
</script>

Upvotes: 3

Related Questions