【问题标题】:Knockout.js - how to cross-reference objects between two arraysKnockout.js - 如何在两个数组之间交叉引用对象
【发布时间】:2015-04-21 14:17:37
【问题描述】:

我有 2 个模型:

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

和

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

我在视图模型中使用它们:

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

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

在 html 文件中:

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

我希望我的表格显示 ProductTypeName 而不是 ProductTypeId,但我无法将 ProductTypeId 传递给任何函数来获取 ProductTypeName。

【问题讨论】:

  • 您的问题不清楚有 3 种类型并且没有任何模型或控制器 :) 在您可以在 html 中使用 foreach 调用它们的步骤之后,您应该定义类型并在另一种类型中使用它们作为类型实例
  • @HamitYıldırım,我想得很清楚。我可以绑定我的视图模型并正确使用它。 ProductViewModel 可能有点像你所说的控制器。

标签: knockout.js


【解决方案1】:
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;
  };
};

和

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

【讨论】:

  • 唯一困扰我的是这个函数会为 ProductList 中的每个项目调用,并且会多次遍历 ProductTypeList。我更愿意将 ProductTypeList 映射到 { typeId: typeName, typeId: typeName ... } 之类的东西,以便更快地访问。为此,我将在我的主模型上使用计算。 getProductTypeName 将使用新计算的。
  • 是的,但只有之后这才成为页面的性能瓶颈。其他一切都是过早的优化。
  • 你就在这里。只是我的习惯,因为我们的项目后端总是发送相当多的数据。这一切都在前端处理。
  • 无论如何都要牢记这一点。当页面感觉缓慢时,这是我会查看的地方之一。
  • 感谢@Tomalak,我意识到我没有解开可观察对象的错误。我可以重写您的代码以获得相同的结果:$root.getProductTypeName(ProductTypeId()) 和getProductTypeName 函数,我可以以任何我想要的方式使用参数productTypeId。
【解决方案2】:

您有两个相互关联的独立集合。您可以创建一个模型,该模型可以通过映射组合对象的计算 observables 来管理该关联。

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

【讨论】:

  • 你的解决方案很好!现在,您可以使用ko.computed 代替ko.dependentObservable ref。但是我注意到当您的应用程序使用 ajax 加载数据时它是不安全的。在这种情况下,您必须先加载ProductTypes,然后再加载Products。
  • 我知道computed 和dependentObservable 之间的别名,我只是更喜欢后者。通常,首先加载哪些值并不重要。虽然这个特定的实现将取决于它们的加载顺序,但这很容易解决。
猜你喜欢
  • 1970-01-01
  • 2021-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多