0
votes

I'm trying to teach myself Knockout.js by making a simple list of games one can add and remove from. At the moment I'm stuck on removing a specific item from an observable array. I have an array of games, and I have it foreach bound to a div that lists out the title, genre, platform, etc. for each game. I also have a Remove button for each game, but they don't work. I have it set up exactly like it is in Knockout's documentation here:

https://knockoutjs.com/documentation/click-binding.html

I also found someone else with the exact same problem here:

Remove items from Knockout observable array

However, the splice solution listed did not work for me. The function did fire this time, but instead of removing the correct item from the array, it removed the last item in the array, regardless of which Remove button was clicked. I'm at a loss as to why the code from Knockout's documentation doesn't work, and why the splice solution isn't working correctly. Here is my code. Pardon all the hard coded values. I'm just trying to get the basics of it working at the moment.

@{
    ViewBag.Title = "My Game List";
}

<head>
    <script type="text/javascript">
        $(function () {
            var game1 = ko.observable({
                title: 'Bioshock',
                genre: 'Shooter',
                platform: 'XBox 360',
                releaseDate: '8/21/2007',
                developer: 'Irrational Games',
                publisher: '2K Games',
                imageURL: 'https://upload.wikimedia.org/wikipedia/en/thumb/6/6d/BioShock_cover.jpg/220px-BioShock_cover.jpg'
            });
            var game2 = ko.observable({
                title: 'The Legend of Zelda: Ocarina of Time',
                genre: 'RPG',
                platform: 'N64',
                releaseDate: '11/21/1998',
                developer: 'Nintendo',
                publisher: 'Nintendo',
                imageURL: 'https://cdn-images-1.medium.com/max/1600/1*n2iccNMASW983gg-ZmMdTw.jpeg'
            });
            var game3 = ko.observable({
                title: 'Devil May Cry',
                genre: 'Hack-n-Slash',
                platform: 'PS2',
                releaseDate: '8/23/2001',
                developer: 'Capcom',
                publisher: 'Capcom',
                imageURL: 'https://upload.wikimedia.org/wikipedia/en/thumb/1/1e/DMC1FrontCover.jpg/220px-DMC1FrontCover.jpg'
            });
            var game4 = ko.observable({
                title: 'Comix Zone',
                genre: 'Beat-em-Up',
                platform: 'Sega Genesis',
                releaseDate: '8/2/1995',
                developer: 'Sega',
                publisher: 'Sega',
                imageURL: 'https://upload.wikimedia.org/wikipedia/en/thumb/0/0e/Comix_Zone_Coverart.png/220px-Comix_Zone_Coverart.png'
            });
            var game5 = ko.observable({
                title: 'To the Moon',
                genre: 'Visual Novel',
                platform: 'PC',
                releaseDate: '9/1/2011',
                developer: 'Freebird Games',
                publisher: 'Freebird Games',
                imageURL: 'https://steamcdn-a.akamaihd.net/steam/apps/206440/capsule_616x353.jpg?t=1519836062'
            });
            function gamesViewModel() {
                var self = this;
                self.gamesList = ko.observableArray([game1, game2, game3, game4, game5]);
                self.gameToAdd = ko.observable({
                    title: 'Mass Effect',
                    genre: 'RPG',
                    platform: 'PC',
                    releaseDate: '11/20/2007',
                    developer: 'BioWare',
                    publisher: 'EA',
                    imageURL: 'https://steamcdn-a.akamaihd.net/steam/apps/17460/header.jpg?t=1447351599'
                });
                self.addGame = function () {
                    self.gamesList.push(self.gameToAdd);
                };
                self.removeGame = function (gameToRemove) {
                    self.gamesList.remove(gameToRemove);
                    //var gameIndex = self.gamesList.indexOf(gameToRemove);
                    //self.gamesList.splice(gameIndex, 1);
                };
            }
            ko.applyBindings(new gamesViewModel);
        });

    </script>
</head>
<div class="jumbotron">
    <h1>TOP 5 GAMES</h1>
</div>

<div class="row">
    <h4>Games</h4>
    <div class="card-columns" data-bind="foreach: gamesList">
        <div class="card">
            <a data-bind="attr: {href: imageURL}" target="_blank">
                <img class="card-img-top" data-bind="attr: {src: imageURL}" />
            </a>
            <div class="card-body">
                <h5 class="card-title" data-bind="text: title"></h5>
                <div class="card-text">
                    <div>
                        <span>Genre: </span>
                        <span data-bind="text: genre" />
                    </div>
                    <div>
                        <span>Platform: </span>
                        <span data-bind="text: platform" />
                    </div>
                    <div>
                        <span>Release Date: </span>
                        <span data-bind="text: releaseDate" />
                    </div>
                    <div>
                        <span>Developer: </span>
                        <span data-bind="text: developer" />
                    </div>
                    <div>
                        <span>Publisher: </span>
                        <span data-bind="text: publisher" />
                    </div>
                </div>
                <button class="btn btn-danger" data-bind="click: $parent.removeGame">-Remove</button>
            </div>
        </div>
    </div>
    <button data-bind="click: addGame">+Add</button>
</div>
1

1 Answers

0
votes

Using "()" in knockoutjs is very tricky. Your code is perfect but here is the problem. game objects (game1, game2, ... ) are declared as observable (i would keep them as normal variable) and you are pushing observable reference in gamesList not actual values. Thats why remove method is not able to identify it.

Either declare game objects without observable or assign them with "()" in list.

 $(function () {
            var game1 = ko.observable({
                title: 'Bioshock',
                genre: 'Shooter',
                platform: 'XBox 360',
                releaseDate: '8/21/2007',
                developer: 'Irrational Games',
                publisher: '2K Games',
                imageURL: 'https://upload.wikimedia.org/wikipedia/en/thumb/6/6d/BioShock_cover.jpg/220px-BioShock_cover.jpg'
            });
            var game2 = ko.observable({
                title: 'The Legend of Zelda: Ocarina of Time',
                genre: 'RPG',
                platform: 'N64',
                releaseDate: '11/21/1998',
                developer: 'Nintendo',
                publisher: 'Nintendo',
                imageURL: 'https://cdn-images-1.medium.com/max/1600/1*n2iccNMASW983gg-ZmMdTw.jpeg'
            });
            var game3 = ko.observable({
                title: 'Devil May Cry',
                genre: 'Hack-n-Slash',
                platform: 'PS2',
                releaseDate: '8/23/2001',
                developer: 'Capcom',
                publisher: 'Capcom',
                imageURL: 'https://upload.wikimedia.org/wikipedia/en/thumb/1/1e/DMC1FrontCover.jpg/220px-DMC1FrontCover.jpg'
            });
            var game4 = ko.observable({
                title: 'Comix Zone',
                genre: 'Beat-em-Up',
                platform: 'Sega Genesis',
                releaseDate: '8/2/1995',
                developer: 'Sega',
                publisher: 'Sega',
                imageURL: 'https://upload.wikimedia.org/wikipedia/en/thumb/0/0e/Comix_Zone_Coverart.png/220px-Comix_Zone_Coverart.png'
            });
            var game5 = ko.observable({
                title: 'To the Moon',
                genre: 'Visual Novel',
                platform: 'PC',
                releaseDate: '9/1/2011',
                developer: 'Freebird Games',
                publisher: 'Freebird Games',
                imageURL: 'https://steamcdn-a.akamaihd.net/steam/apps/206440/capsule_616x353.jpg?t=1519836062'
            });
            function gamesViewModel() {
                var self = this;
                self.gamesList = ko.observableArray([game1(), game2(), game3(), game4(), game5()]);
                self.gameToAdd = ko.observable({
                    title: 'Mass Effect',
                    genre: 'RPG',
                    platform: 'PC',
                    releaseDate: '11/20/2007',
                    developer: 'BioWare',
                    publisher: 'EA',
                    imageURL: 'https://steamcdn-a.akamaihd.net/steam/apps/17460/header.jpg?t=1447351599'
                });
                self.addGame = function () {
                    self.gamesList.push(self.gameToAdd);
                };
                self.removeGame = function (gameToRemove) {
                    self.gamesList.remove(gameToRemove);
                    //var gameIndex = self.gamesList.indexOf(gameToRemove);
                    //self.gamesList.splice(gameIndex, 1);
                };
            }
            ko.applyBindings(new gamesViewModel);
        });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="jumbotron">
    <h1>TOP 5 GAMES</h1>
</div>

<div class="row">
    <h4>Games</h4>
    <div class="card-columns" data-bind="foreach: gamesList">
        <div class="card">
            <a data-bind="attr: {href: imageURL}" target="_blank">
                <img class="card-img-top" data-bind="attr: {src: imageURL}" />
            </a>
            <div class="card-body">
                <h5 class="card-title" data-bind="text: title"></h5>
                <div class="card-text">
                    <div>
                        <span>Genre: </span>
                        <span data-bind="text: genre" />
                    </div>
                    <div>
                        <span>Platform: </span>
                        <span data-bind="text: platform" />
                    </div>
                    <div>
                        <span>Release Date: </span>
                        <span data-bind="text: releaseDate" />
                    </div>
                    <div>
                        <span>Developer: </span>
                        <span data-bind="text: developer" />
                    </div>
                    <div>
                        <span>Publisher: </span>
                        <span data-bind="text: publisher" />
                    </div>
                </div>
                <button class="btn btn-danger" data-bind="click: $parent.removeGame">-Remove</button>
            </div>
        </div>
    </div>
    <button data-bind="click: addGame">+Add</button>
</div>