Search code examples
knockout.jsknockout-mapping-plugin

Bind to a selected item of the foreach binding with knockout.js


I am using knockout.js and the ko.mapping plugin to bind a list of json object to an html grid. Lets call each item a card (simplified example below)

{
    "card": [
        {
            "Id": "cards/1",
            "category": "Demo",
            "title": "Card 1",
            "description": "bla bla",
            "picture": "demo1.jpg ",
            "madeBy": "user/1"
        },
        {
            "Id": "cards/2",
            "category": "Demo",
            "title": "Card 2",
            "description": "bla bla",
            "picture": "demo2.jpg",
            "madeBy": "user/2"
        }
    ]
}

I bind each elemet like this:

 <div data-bind="foreach: card">
    <span data-bind="text:title"></span>
    <a data-bind='click: show'><img data-bind="attr:{src: picture}" /></a>
</div>

When the user clicks on a card, I would like to show the selected card in a different div (outside of the foreach) with some more properties from the selected json object

Who do I bind to one selected card from the view model?

I am trying something like (but not getting any data):

<h1 data-bind="text: $data.title"> </h1>

Solution

  • You will want to do this by tracking the selected card on the ViewModel that holds the cards. Here is a fiddle demonstrating this simply. Here is the modified HTML and JS (this has been simplified for the purpose of a demo, and I'm not using mapping, but you get the idea):

    HTML:

    <div data-bind="foreach: cards">
        <span data-bind="text:title"></span>
        <a data-bind='click: $parent.selectedCard'>ImagePlaceholder</a>
        </br>
    </div>
    
    <div data-bind="with: selectedCard">
        <h1 data-bind="text: title"></h1>
        <span data-bind="text: description"></span>
    </div>
    

    JS

    var ViewModel = function(cards) {
        this.cards = ko.observableArray(
            ko.utils.arrayMap(cards, function(c) {return new Card(c);})
        );
        this.selectedCard = ko.observable();
    };
    

    Note that the click setting the selected card directly, without needing to use wrapper "show" function. Since observables are functions, we can skip that step (unless of course you need to do more in the show function).