Warm tip: This article is reproduced from serverfault.com, please click

Filtering knockout observable array based on string

发布于 2020-12-11 15:40:57

I'm currently trying to solve a little problem.

I have the following code. I try to filter and re-render the fetched movielist based on the chosen genre.

So far i am able to cast the selected option to an object in my js-script but i don't know where to go from here. The genre values in my observable array is an array of its own since one movie can have multiple genres.

Here's my script so far:

define(['knockout', 'dataservice'], (ko, dataservice) => {
return function () {
    let movies = ko.observableArray([]);
    let movieList = ko.observableArray([])
    let genres = ko.observableArray([]);
    let pageSizes = ko.observableArray();
    let prev = ko.observableArray();
    let next = ko.observableArray();
    let selectedPage = ko.observableArray([10]);
    self.selectedGenre = ko.observable();
    let objGenre = ko.observable();
    let movieGenres = ko.observable();

    self.selectedGenre.subscribe(() => {
        objGenre = selectedGenre().name;
        console.log(objGenre)
        });   

    console.log(chosenGenre)

    self.getMovies = function () {
        ko.mapping.fromJS(data.movies, {}, self.movies)
    }

    let getMovies = url => {
        dataservice.getMovies(url, data => {
            pageSizes(data.pageSizes);
            prev(data.prev || undefined);
            next(data.next || undefined);
            movieList(data.movieList);
            movieGenres(movieList.genre)
        });
    }
   
    let getGenres = function () {
        fetch('http://localhost:5001/api/genre')
            .then(function (response) {
                return response.json();
            })
            .then(function (data) {
                genres(data);

                console.log(genres());
            })
    };


    // INDSÆT GAMMEL FETCH

    let showPrev = movie => {
        console.log(prev());
        getMovies(prev());
    }

    let enablePrev = ko.computed(() => prev() !== undefined);

    let showNext = product => {
        console.log(next());
        getMovies(next());
    }

    let enableNext = ko.computed(() => next() !== undefined);

    selectedPage.subscribe(() => {
        var size = selectedPage()[0];
        getMovies(dataservice.getMoviesUrlWithPageSize(size));
    });


    document.getElementById("scrolltotop").addEventListener("click", function () {
        console.log("Clicked!");
        $('html,body').animate({scrollTop: $('#scrolltothisdiv').offset().top}, 1000);
    });

    document.getElementById("prevscrolltotop").addEventListener("click", function () {
        console.log("Clicked!");
        $('html,body').animate({scrollTop: $('#scrolltothisdiv').offset().top}, 1000);
    });

    getMovies();
    getGenres();

    self.optionsAfterRender = function (option, view) {
        if (view.defaultView) {
            option.className = 'defaultViewHighlight';
        }
    };


    return {
        pageSizes,
        selectedPage,
        movies,
        movieList,
        showPrev,
        enablePrev,
        showNext,
        enableNext,
        genres,
        optionsAfterRender,
        selectedGenre
    };
}

});

Dataservice script bound with require js and the fetched data in postman + html

    <section class="after-head d-flex section-text-white position-relative">
    <div class="top-block top-inner container">
        <div class="top-block-content">
            <h1 class="section-title">Movies</h1>
            <div class="page-breadcrumbs">
                <a class="content-link" href="#">Home</a>
                <span class="text-theme mx-2"><i class="fas fa-chevron-right"></i></span>
                <span>Movies</span>
            </div>
        </div>
    </div>

</section>
<a id="topOfPage"></a>
<a id="top"></a>
<section class="section-long">   <!-- GENRES CONFIG HERE-->
    <div class="container">
        <div class="section-pannel">
            <div class="grid row">
                <div class="col-md-10">
                    <form autocomplete="off">
                        <div class="row form-grid">
                            <div class="col-sm-6 col-lg-3">
                                <div id="scrolltothisdiv"></div>
                                <div class="input-view-flat input-group">
                                    <select class="form-control" data-bind=
                                            "options: genres,
                                             optionsText: 'name',
                                             value: selectedGenre
                                          ">
                                    </select>
                                </div>
                            </div>
                            <div class="col-sm-6 col-lg-3">  <!-- START YEAR AND END YEAR HERE -->
                                <div class="input-view-flat date input-group" data-toggle="datetimepicker"
                                     data-target="#release-year-field">
                                    <input class="datetimepicker-input form-control" id="release-year-field"
                                           name="releaseYear" type="text" placeholder="release year"
                                           data-target="#release-year-field" data-date-format="Y"/>
                                    <div class="input-group-append">
                                        <span class="input-group-text"><i class="fas fa-calendar-alt"></i></span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>

        <!-- WRAP DEN HER IN I KNOCKOUT ARRAY -->
        <table>

            <tbody>  <!-- FOR HVER FETCHED -->
            <tr>

                <td data-bind="foreach: movieList">
                    <article class="movie-line-entity"> <!-- HVER ARTICLE I TABLE HER CONFIG-->
                        <div class="entity-poster" data-role="hover-wrap"> <!-- HOVER OVER BILLEDER -->
                            <div class="embed-responsive embed-responsive-poster">
                                <img class="embed-responsive-item" data-bind="attr:{src: poster}" alt=""/>
                                <!-- BILLEDE SRC HER -->
                            </div>
                            <div class="d-over bg-theme-lighted collapse animated faster"
                                 data-show-class="fadeIn show" data-hide-class="fadeOut show">
                                <div class="entity-play">  <!-- NEDENSTÅENDE ER LOGO -->
                                    <svg width="1em" height="1em" viewBox="0 0 16 16"
                                         class="bi bi-box-arrow-up-right" fill="currentColor"
                                         xmlns="http://www.w3.org/2000/svg">
                                        <path fill-rule="evenodd"
                                              d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
                                        <path fill-rule="evenodd"
                                              d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
                                    </svg>
                                </div>
                            </div>
                        </div>
                        <div class="entity-content">
                            <h4 class="entity-title">
                                <a class="content-link" href="movie-info-sidebar-right.html"><span
                                        data-bind="text: title_name"></span></a>
                            </h4>
                            <div data-bind="foreach: genre">
                                <div class="entity-category">
                                    <a class="content-link" href="movies-blocks.html"><span
                                            data-bind="text: $data"></span></a>,
                                </div>
                            </div>
                            <div class="entity-info">
                                <div class="info-lines"> <!--  RATING -->
                                    <div class="info info-short">
                                        <span class="text-theme info-icon"><i class="fas fa-star"></i></span>
                                        <span class="info-text"><span data-bind="text: rating"></span></span>
                                        <span class="info-rest">/10</span>
                                    </div>
                                    <div class="info info-short"> <!-- RUNTIME -->
                                        <span class="text-theme info-icon"><i class="fas fa-clock"></i></span>
                                        <span class="info-text"><span data-bind="text: runtime"></span></span>
                                        <span class="info-rest">&nbsp;<span>min</span></span>
                                    </div>
                                </div>
                            </div>
                            <p class="text-short entity-text">
                                <span data-bind="text: plot"></span>
                            </p>
                        </div>
                    </article>
                </td>
            </tr>
            </tbody>
        </table>


        <div class="section-bottom"> <!-- PAGING CONFIG HER -->
            <div class="paginator">
                <div class="navigation-buttons">
                    <button id="prevscrolltotop" onclick="scrolltotop" type="button"  class="btn btn-inverse btn-warning" data-bind="click: showPrev, enable: enablePrev" href="#"> Prev</button>
                    <button id="scrolltotop" onclick="scrolltotop" type="button" class="btn btn-warning btn-rounded" data-bind="click: showNext, enable: enableNext" href="#">Next</button>
                </div>
            </div>
        </div>

      
    </div>
</section>

GET postman

{
"pageSizes": [
    5,
    10,
    15,
    20
],
"count": 55076,
"pages": 2754,
"prev": null,
"next": "http://localhost:5001/api/title?page=1&pageSize=20",
"movieList": [
    {
        "title_id": "tt0052520",
        "title_name": "The Twilight Zone",
        "poster": "https://m.media-amazon.com/images/M/MV5BNTAzMDI5MzgtMGNkMC00MzllLWJhNjctNjA1NmViNGUxMzYxXkEyXkFqcGdeQXVyMTQxNzMzNDI@._V1_SX300.jpg",
        "plot": "Ordinary people find themselves in extraordinarily astounding situations, which they each try to solve in a remarkable manner.",
        "runtime": "51",
        "genre": [
            "Fantasy",
            "Horror",
            "Drama"
        ],
        "votes": "68643",
        "rating": "9.0",
        "type": "tvSeries",
        "url": "http://localhost:5001/api/title/tt0052520"
    },
    {
        "title_id": "tt0063929",
        "title_name": "Monty Python's Flying Circus",
        "poster": "https://m.media-amazon.com/images/M/MV5BMWY2ZGI0OGUtZDc3YS00ZmVjLWJiNWQtZDdmNzFmM2UzYWFhXkEyXkFqcGdeQXVyNTA4NzY1MzY@._V1_SX300.jpg",
        "plot": "The irreverent Monty Python comedy troupe present a series of skits which are often surreal, bawdy, uncompromising and/or tasteless, but nearly always hilarious.",
        "runtime": "30",
        "genre": [
            "Comedy"
        ],
        "votes": "65618",
        "rating": "8.8",
        "type": "tvSeries",
        "url": "http://localhost:5001/api/title/tt0063929"
    },
    {
        "title_id": "tt0078672",
        "title_name": "Pride and Prejudice",
        "poster": "https://m.media-amazon.com/images/M/MV5BMjA5MTg2OTYyN15BMl5BanBnXkFtZTcwMzAwODUyMQ@@._V1_SX300.jpg",
        "plot": "Mrs. Bennet is determined to find husbands for her five daughters. The arrival of a new wealthy neighbor seems like the answer to her predicament. But while eldest daughter Jane catches Mr. Bingley''s eye, middle child Mary has her nose stuck in a book, and youngest girls, Kitty and Lydia, chase after officers in uniform; Elizabeth, the willful, intelligent, and opinionated second daughter, is snubbed by haughty gentleman Mr. Darcy... In this class-minded society, can love triumph over pride and prejudice?",
        "runtime": "265",
        "genre": [
            "Romance",
            "Comedy",
            "Drama"
        ],
        "votes": "2128",
        "rating": "7.3",
        "type": "tvMiniSeries",
        "url": "http://localhost:5001/api/title/tt0078672"
    },
    {
        "title_id": "tt0088634",
        "title_name": "The Twilight Zone",
        "poster": "https://m.media-amazon.com/images/M/MV5BOTc4YjU0ZDktNzE2Ny00MDdmLWIwZDQtMzU2NzhmODA4YmMwXkEyXkFqcGdeQXVyNjExODE1MDc@._V1_SX300.jpg",
        "plot": "An updated version of the famous 1960''s TV series created by Rod Serling. Each week presents one to three tales about some unusual situation that turns out to be even more unusual than initially suspected. Whether the tone of the story is horror, suspense or humor, there is always a surprise twist at the end.",
        "runtime": "45",
        "genre": [
            "Fantasy",
            "Horror",
            "Drama"
        ],
        "votes": "9822",
        "rating": "7.8",
        "type": "tvSeries",
        "url": "http://localhost:5001/api/title/tt0088634"
    },
    {
        "title_id": "tt0098286",
        "title_name": "Good News, Bad News",
        "poster": "N/A",
        "plot": "In this episode, the predecessor to Seinfeld (1989), Jerry is expecting a woman that he met in Michigan to come and visit him in New York. Throughout the first part of the show Jerry and George are discussing the situation. Later we meet \"Kessler\" who comes in to Jerry''s apartment to borrow some meat and uncharacteristically knocks on the door before entering.",
        "runtime": "23",
        "genre": [
            "Comedy"
        ],
        "votes": "3501",
        "rating": "7.6",
        "type": "tvEpisode",
        "url": "http://localhost:5001/api/title/tt0098286"
    },
    {
        "title_id": "tt0098769",
        "title_name": "The Civil War",
        "poster": "https://m.media-amazon.com/images/M/MV5BZDc1NzI2MGEtZDA2Yy00ZWExLTgwYmItNjU3N2QyYmM0MzYwXkEyXkFqcGdeQXVyNTA4NzY1MzY@._V1_SX300.jpg",
        "plot": "This highly acclaimed mini series traces the course of the U.S. Civil War from the abolitionist movement through all the major battles to the death of President Lincoln and the beginnings of Reconstruction. The story is mostly told in the words of the participants themselves, through their diaries, letters, and Visuals are usually still photographs and illustrations of the time, and the soundtrack is likewise made up of war-era tunes played on period instruments. Several modern-day historians offer periodic comment and insight on the war''s causes and events.",
        "runtime": "680",
        "genre": [
            "History",
            "War",
            "Documentary"
        ],
        "votes": "13075",
        "rating": "9.0",
        "type": "tvMiniSeries",
        "url": "http://localhost:5001/api/title/tt0098769"
    }

Dataservice

define([], () => {
const movieApiUrl = "api/title";
const genreApiUrl = "api/genre";


let getJson = (url, callback) => {
    fetch(url).then(response => response.json()).then(callback);
};

let getMovies = (url, callback) => {
    if (url === undefined) {
        url = movieApiUrl;
    }
    getJson(url, callback);
};

let getGenres = (url, callback) => {
    if(url === undefined) {
        url = genreApiUrl;
    }
    getJson(url, callback)
};

let getMoviesUrlWithPageSize = size => movieApiUrl + "?pageSize=" + size;


return {
    getMovies,
    getMovie: getJson,
    getMoviesUrlWithPageSize,
    getGenres
};

I want to achieve something like this but from a drop down which i have in my select tag:

https://jsfiddle.net/jchaplin2/9o4utk6t/1/

Questioner
Niklas Munkholm Hjort
Viewed
0
jellyfith 2020-12-12 09:27:01

You could add a computed observable filteredMoviesList which would go through each of the filters you describe and filter for the selected genre. Then in your html you would just bind your foreach binding to that instead of moviesList. Here is a simple example:

JS

let filteredMovieList = ko.computed(() => {
    let tempList = ko.toJS(movieList);
    if (this.selectedGenre()) {
        tempList = tempList.filter(movie => movie.genre == this.selectedGenre().name);
    }
    return tempList;
});

HTML

<div data-bind="foreach: filteredMovieList">
    <span data-bind="text: title + genre"></span>
</div>

You will have to edit it to match your use case but similar solutions have worked for me in the past. Good luck!