Terje Nygård
Terje Nygård

Reputation: 1257

Knockout.js : ObservableArray with ObservableArrays inside?

I'm using this plugin called Dragula which needs an ObservableArray as a source for the data..

HTML/Knockout-bindings

<div class="widget-container">
                <div class="widget-content visible" id="team-setup">
                    <div class="header">
                        <p>Lagoppsett</p>
                        <p></p>
                    </div>

                    <div class="col-sm-12">
                        <div class="row">
                            <div class="widget-container">
                                <div class="col-xs-6 widget-content visible">
                                    <div class="header">
                                        <p>Tilgjengelige spillere</p>
                                        <p></p>
                                    </div>
                                    <div class="player-card-container-mini" data-bind="dragula: { data: availablePlayers, group: 'playerz' } ">

                                        <div class="player-card-mini">
                                            <div class="player-card-left">
                                                <div class="player-avatar" style="margin-left: 85%;">
                                                    <img src="Content/Images/player-female.png" id="imgAvatar" runat="server" />
                                                    <div class="player-shirt-no" data-bind="text: ShirtNo"></div>
                                                </div>
                                            </div>
                                            <div class="player-card-subtext">
                                                <div class="player-text">
                                                    <div class="player-card-header-small" data-bind="text: PlayerName"></div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                                <div class="col-xs-6 widget-content visible">
                                    <div class="header">
                                        <p>Lag</p>
                                        <p></p>
                                    </div>
                                    <div data-bind="foreach: teamsetup">
                                    <div data-bind="foreach: SubTeams">
                                        <h1 data-bind="text: TeamSubName"></h1>
                                        <div class="player-card-container-mini" data-bind="dragula: { data: Players, group: 'playerz' } " style="border: 1px solid red; min-height:200px">

                                            <div class="player-card-mini">
                                                <div class="player-card-left">
                                                    <div class="player-avatar" style="margin-left: 85%;">
                                                        <img src="Content/Images/player-female.png" id="img1" runat="server" />
                                                        <div class="player-shirt-no" data-bind="text: ShirtNo"></div>
                                                    </div>
                                                </div>
                                                <div class="player-card-subtext">
                                                    <div class="player-text">
                                                        <div class="player-card-header-small" data-bind="text: PlayerName"></div>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    </div>
                                </div>
                        </div>
                    </div>
                    <div style="clear:both">&nbsp;</div>
                </div>
            </div>

Knockout code :

var TeamSetupViewModel = function () {
    var self = this;
    self.teamsetup = ko.observableArray();
    self.availablePlayers = ko.observableArray();
    self.testPlayers = ko.observableArray();
}
var model = new TeamSetupViewModel();

ko.applyBindings(model, document.getElementById("team-setup"));

var uri = 'api/MainPage/GetTeamSetup/' + getQueryVariable("teamId");

$.get(uri,
    function (data) {
        model.teamsetup(data);
        model.availablePlayers(data.AvailablePlayers);
        model.testPlayers(data.AvailablePlayers);
        console.log(data);
    }, 'json');

});

The problem is... that i'm having a ObservableArray at the top node, and i do need ObservableArrays further down in the hierarchy.

model.availablePlayers works fine, but when accessing the other players in the html/ko foreach loops through teamsetup -> SubTeams -> Players it doesn't work due to Players isn't an ObservableArray. (There might be everyting from 1 to 7 SubTeams with players).

So how can i make the Players in each SubTeams an ObservableArray ?

See the image for the datastructure :

enter image description here

Upvotes: 0

Views: 796

Answers (1)

Maciej Grzyb
Maciej Grzyb

Reputation: 563

You could use Mapping plugin, but if players is the only thing you need, you can do it manually:

Simplify your view model:

var TeamSetupViewModel = function () {
    var self = this;
    self.availablePlayers = ko.observableArray();
    self.subTeams = ko.observableArray();
}

After you get the data from the server, populate the view model converting the array of players on every team to an observable array of players:

$.get(uri,
    function (data) {
        model.availablePlayers(data.AvailablePlayers);
        model.subTeams(data.SubTeams.map(function(t) { t.Players = ko.observableArray(t.Players); return t; }));
    }, 'json');
});

Finally, remove the following line in your template (with its closing tag) - nothing to iterate over anymore:

<div data-bind="foreach: teamsetup">

... and update the name of the property in the next line, so it is camel case like in the VM:

<div data-bind="foreach: subTeams">

Upvotes: 1

Related Questions