Javascript – How to remove items from external observableArray with KnockoutJS

javascriptknockout.js

The goal

Remove items from external observableArray using KnockoutJS.

The problem

There is two observableArray in my application. One for available products to buy and other to products that I have added in the Summary by clicking in add button.

Until here, all works fine. But now I need to remove items from Summary and change the button state/style — and I do not know how to access an external observableArray to do this.

To understand my problem, check out this jsFiddle or see the markup in the next topic.

As you could see, when I click on add button, the product goes to Summary. When I click on remove — regardless if the button is from the summary or the product — I want to change the button state and remove the item from summary. Technically speaking, I want to remove the item from items' observableArray utilizing the products' observableArray.

My code

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) {
        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);

Best Answer

You can search for it.

You can query the cart for an item with the same id, and remove it

self.remove = function (item) {
    var inItems = self.items().filter(function(elem){
        return elem.id() === item.id(); // find the item with the same id
    })[0];
    self.items.remove(inItems);
    item.isAdded(false);
};

Unless you have hundreds of thousands of items, that should be perfectly fast enough. Just remember to use items.remove() so it'll know to update the observableArray :)