Knockout Audio Playlist

Я создаю музыкальное приложение, которое использует Knockout для привязки. Все треки, перечисленные в приложении, извлекаются извне из Soundcloud следующим образом:

$.getJSON('http://api.soundcloud.com/users/guy-j/tracks.json?client_id=c4bc3b1a93902abecbaca3fa4582d970', {limit: 200}, function(data) {
            vm.tracks($.map(data, function (track) {
                return {
                    artwork: track.artwork_url,
                    avatar: track.user.avatar_url,
                    date: track.created_at,
                    description: track.description,
                    duration: track.duration,
                    listens: track.playback_count,
                    permalink: track.permalink_url,
                    purhcase: track.purchase_url,
                    stream: track.stream_url,
                    track: track.title
                };
            }));
        });

Эти треки (после получения) помещаются в пустой observableArray, а затем HTML-представление привязывается к этому массиву и генерирует список треков. У меня воспроизведение / пауза звука:

Чтобы выбрать / воспроизвести трек, каждый <li>, созданный из observableArray, имеет обработчик кликов под названием 'goToTrack', который затем передает наблюдаемый объект с именем 'self.chosenTrackData' выбранной дорожки, и мой аудиоэлемент затем связывается с 'selectedTrackData' для воспроизведения выбранный трек.

Моя проблема сейчас заключается в том, что я не совсем уверен, каким будет лучший способ приблизиться к функциям следующей / предыдущей дорожки с помощью Knockout. Мне бы пришлось кое-как различать текущую точку в observableArray, а затем ++ или - в зависимости от того, какой вариант вы выбрали?

Любая помощь будет принята с благодарностью, так как я все еще изучаю Knockout!


person leaksterrr    schedule 05.05.2014    source источник


Ответы (1)


Я бы посоветовал сохранить внутренний номер «текущего трека», который вы увеличиваете или уменьшаете, и основывать его на вычислении.

function PlayerViewModel() {
    var self = this,
        currentTrackNo = ko.observable(0);

    // data
    self.tracks = ko.observableArray();

    // computeds
    self.currentTrack = ko.computed(function () {
        return self.tracks()[currentTrackNo()];
    });
    self.hasTrack = function (track) {
        return !!self.currentTrack();
    };
    self.hasNextTrack = ko.computed(function () {
        return currentTrackNo() < self.tracks().length - 1;
    });
    self.hasPrevTrack = ko.computed(function () {
        return currentTrackNo() > 0;
    });

    // API
    self.setNextTrack = function () {
        if (self.hasNextTrack()) {
            currentTrackNo(currentTrackNo() + 1);
        }
    };
    self.setPrevTrack = function () {
        if (self.hasPrevTrack()) {
            currentTrackNo(currentTrackNo() - 1);
        }
    };
    self.setTrack = function (track) {
        var trackNo = ko.utils.arrayIndexOf(self.tracks(), track);
        currentTrackNo(trackNo);
    };

    // init
    $.getJSON('...', {limit: 200}); etc;
}

Свяжите компонент представления "текущий трек" с currentTrack наблюдаемым. Убедитесь, что вы каким-либо образом используете hasTrack (поскольку currentTrack может быть неопределенным).


Альтернативная реализация, позволяющая избежать ведения отдельного трек-номера:

function PlayerViewModel() {
    var self = this;

    // data
    self.tracks = ko.observableArray();
    self.currentTrack = ko.observable();

    // computeds
    self.currentTrackNo = ko.computed(function () {
        var currentTrack = self.currentTrack();
        return ko.utils.arrayIndexOf(self.tracks(), currentTrack);
    });
    self.hasTrack = function (track) {
        return !!self.currentTrack();
    };
    self.hasNextTrack = ko.computed(function () {
        return self.currentTrackNo() < self.tracks().length - 1;
    });
    self.hasPrevTrack = ko.computed(function () {
        return self.currentTrackNo() > 0;
    });

    // API
    self.setNextTrack = function () {
        if (self.hasNextTrack()) {
            self.currentTrack(self.tracks()[self.currentTrackNo() + 1]);
        }
    };
    self.setPrevTrack = function () {
        if (self.hasPrevTrack()) {
            self.currentTrack(self.tracks()[self.currentTrackNo() - 1]);
        }
    };
    self.setTrack = function (track) {
        // make sure that track is actually in self.tracks here
        self.currentTrack(track);
    };

    // init
    $.getJSON('...', {limit: 200}); etc;
}
person Tomalak    schedule 05.05.2014
comment
@leaksterrr Альтернативой было бы не использовать индекс, а иметь currentTrack наблюдаемую и напрямую использовать ko.utils.arrayIndexOf() в ваших _3 _ / _ 4_ методах. Может быть, это даже лучше, потому что это позволит динамически сортировать списки треков - следующие / предыдущие треки всегда будут правильными в текущем порядке сортировки. - person Tomalak; 05.05.2014
comment
Итак, когда я добавляю фильтры и выполняю поиск в приложение, при воспроизведении следующей дорожки будет воспроизводиться следующая дорожка в фильтре, а не следующая дорожка в массиве? Как лучше всего провести рефакторинг вышеизложенного, чтобы отразить это? Я все еще относительно новичок в нокаутах. - person leaksterrr; 05.05.2014
comment
да. Реализация на самом деле очень похожа. См. Измененный ответ. Вам нужно будет реализовать некоторые средства защиты, которые будут следить за тем, чтобы всегда была одна текущая дорожка (т.е. автоматически выбирать первую дорожку из вновь загруженного списка) и реагировать на пустой список дорожек (т.е. удалять старую текущую дорожку). То же самое и с фильтрами. Вкратце: вы должны убедиться, что ваше приложение ведет себя разумно, когда текущий трек отсутствует в списке по какой-либо причине. - person Tomalak; 05.05.2014