【问题标题】:Accessing external observableArray (KnockoutJS)访问外部 observableArray (KnockoutJS)
【发布时间】:2013-07-10 14:07:32
【问题描述】:

目标

访问外部 observableArray 以更改其属性之一。

问题

我的应用程序中有两个列表。其中一个列表包含我添加到其中的商店商品——非常类似于购物车;另一个列表是可供购买的产品。每个产品都有一个“添加按钮”,当我点击它时,它就像魔法一样出现在“购物车”中。

要从购物车中删除此产品,我们必须单击添加我们产品的按钮,因为它的状态已更改为“删除”。或者,我们可以点击添加到列表中的项目旁边——那里有一个“删除按钮”。

到这里为止,一切正常。问题是:当我通过点击购物车的“x”删除产品时,产品的按钮没有恢复正常。换句话说,按钮不会回到“添加按钮”。

要查看说明性示例,just click here to go to jsFiddle

技术语言

我需要从SummaryViewModel 访问ProductViewModel 上的self.products = ko.observableArray(products);

代码

如果 jsFiddle 出现问题,代码如下。

HTML:

<ul class="summary">
    <!-- ko foreach: Summary.items -->
        <p data-bind="text: name"></p>
        <button class="btn btn-danger btn-mini remove-item">
            <i class="icon-remove">×</i>
        </button>
    <!-- /ko -->
</ul>

<h1>What would you to buy?</h1>

<ul class="products">
    <!-- ko foreach: Product.products -->
    <li>
        <h3 data-bind="text: name"></h3>
        <p data-bind="text: desc"></p>
        <!-- ko if:isAdded -->
        <button data-bind="if: isAdded" class="btn btn-small btn-success action remove">
            <i data-bind="click: $root.Summary.remove" class="icon-ok">Remove</i>
        </button>
        <!-- /ko -->
        <!-- ko ifnot:isAdded -->
        <form data-bind="submit: function() { $root.Summary.add($data); }">
            <button data-bind="ifnot: isAdded" class="btn btn-small action add">
                <i class="icon-plus">Add</i>
            </button>
        </form>
        <!-- /ko -->
    </li>
    <!-- /ko -->
</ul>

JavaScript:

function Product(id, name, desc) {
    var self = this;

    self.id = ko.observable(id);
    self.name = ko.observable(name);
    self.desc = ko.observable(desc);
    self.isAdded = ko.observable(false);
}

function Item(id, name) {
    var self = this;

    self.id = ko.observable(id);
    self.name = ko.observable(name);
}

function SummaryViewModel() {
    var self = this;
    self.items = ko.observableArray([]);

    self.add = function (item) {
        self.items.push(new Item(item.id(), item.name()));

        console.log(item);

        item.isAdded(true);
    };

    self.remove = function (item) {
        var i = self.items().filter(function(elem){
            return elem.id() === item.id();
        })[0];
        self.items.remove(i);
        item.isAdded(false);
    };
};

function ProductViewModel(products) {
    var self = this;

    self.products = ko.observableArray(products);
};

var products = [
    new Product(1, "GTA V", "by Rockstar"), 
    new Product(2, "Watch_Dogs", "by Ubisoft")
];

ViewModel = {
    Summary: new SummaryViewModel(),
    Product: new ProductViewModel(products)
}

ko.applyBindings(ViewModel);

【问题讨论】:

    标签: javascript knockout.js


    【解决方案1】:

    发生这种情况的原因是,当您将项目推送到摘要列表时,它不再具有 isAdded 或 desc 属性。您要么需要将这些添加到摘要模型中,要么直接传递项目,而不是创建新对象。

    JavaScript 抛出错误('has no property 'isAdded')是我知道的。运行时打开控制台查看错误。

    添加console.log(item);向我展示了它的特性。

    self.remove = function (item) {
        var i = self.items().filter(function(elem){
            return elem.id() === item.id();
        })[0];
        self.items.remove(i);
        console.log(item);
        item.isAdded(false);
    };
    

    等一下,我会更新你的小提琴。

    http://jsfiddle.net/Jr3Tk/3/

    这是一个有效的例子。我删除了一些冗余(例如在您已经拥有产品时对其进行筛选和过滤)

    【讨论】:

    • 我的应用程序遇到的最大问题之一已经为您解决了,就在现在。谢谢你,非常感谢你的解释,关于我的代码的清理。 +1!
    【解决方案2】:

    最好不要在两个不同的地方跟踪关于一个对象的相同信息。这总是会导致同步问题。

    在您的情况下,您跟踪对象是否被“添加”...

    1. 在对象本身(您的产品模型中的 isAdded 可观察到)和
    2. “添加” 对象的手动列表中(您的摘要模型中可观察到的items

    两个地方丢一个很有用。

    例如,您可以删除手动列表并只跟踪产品模型中的对象状态。

    然后,您可以使用计算出的 observable 返回所选产品的过滤视图 (ko.utils.arrayFilter),然后由 Knockout 完成所有其余工作。

    function Product(id, name, desc) {
        var self = this;
    
        self.id = ko.observable(id);
        self.name = ko.observable(name);
        self.desc = ko.observable(desc);
        self.isAdded = ko.observable(false);
    
        self.addRemoveText = ko.computed(function () {
            return self.isAdded() ? "Remove" : "Add";
        });
        self.addRemove = function () {
            self.isAdded(!self.isAdded());
        };
    }
    
    function SummaryViewModel(parent) {
        var self = this;
    
        self.items = ko.computed(function () {
            var products = ko.utils.unwrapObservable(parent.products);
            return ko.utils.arrayFilter(products, function (product) {
                return product.isAdded();
            });
        });
    }
    
    function ProductViewModel(parent) {
        var self = this;
    
        self.items = ko.observableArray(parent.products);
    }
    
    function ViewModel(products) {
        var self = this;
    
        self.products = ko.utils.arrayMap(products, function (init) {
            return new Product(init.id, init.name, init.desc);
        });
        self.Summary = new SummaryViewModel(self);
        self.Product = new ProductViewModel(self);
    }
    

    在这里现场观看:http://jsfiddle.net/Tomalak/Jr3Tk/4/

    还请注意,基本 HTML 变得更加简单。

    <ul class="summary" data-bind="with: Summary">
        <!-- ko foreach: items -->
        <li>
            <p data-bind="text: name"></p>
            <button class="btn btn-danger btn-mini remove-item" data-bind="click: addRemove">
                <i class="icon-remove">×</i>
            </button>
        </li>
        <!-- /ko -->
    </ul>
    
    <h1>What would you to buy?</h1>
    
    <ul class="products" data-bind="with: Product">
        <!-- ko foreach: items -->
        <li>
            <h3 data-bind="text: name"></h3>
            <p data-bind="text: desc"></p>
            <button class="btn btn-small btn-success action remove">
                <i data-bind="click: addRemove, text: addRemoveText" class="icon-ok">Remove</i>
            </button>
        </li>
        <!-- /ko -->
    </ul>
    

    【讨论】:

    • 你好,托马拉克。你的回答太棒了!我正在用你的知识改进我的代码。谢谢!
    • 哦,托马拉克。只需要考虑一件事——现在我在我的应用程序上使用你的代码,但我没有使用click 来绑定添加/删除按钮——我不能,因为我们必须在发送之前填写一个表单.当然,我省略了这些信息,因为在我看来是不相关的。顺便说一句,我已经修改了你的代码,现在一切都很好。非常感谢您的贡献!对我非常有用。
    【解决方案3】:
    ProductViewModel.products.subscribe(function(products){
        SummaryViewModel.products(products);
    });
    

    但首先你需要在SummaryViewModel中添加self.products = ko.observableArray([]);

    我理解正确吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-04
      相关资源
      最近更新 更多