1

私は Backbone.js を使い始めたばかりで、私のテスト ケースはかなり奇妙なものを生み出しています。

要するに、私が経験しているのは、バックボーン モデルのコンストラクターを呼び出した後、オブジェクトのフィールドの一部が以前のアイテムから取得されたように見えるということです。たとえば、次のように呼び出します。

var playlist = new Playlist({
    title: playlistTitle,
    position: playlists.length,
    userId: user.id
});

playlist.get('items').length; //1

ただし、そうする場合:

var playlist = new Playlist({
    title: playlistTitle,
    position: playlists.length,
    userId: user.id,
    items: []
});

playlist.get('items').length; //0

コードは次のとおりです。

define(['ytHelper', 'songManager', 'playlistItem'], function (ytHelper, songManager, PlaylistItem) {
    'use strict';
    var Playlist = Backbone.Model.extend({
        defaults: {
            id: null,
            userId: null,
            title: 'New Playlist',
            selected: false,
            position: 0,
            shuffledItems: [],
            history: [],
            items: []
        },
        initialize: function () {
            //Our playlistItem data was fetched from the server with the playlist. Need to convert the collection to Backbone Model entities.
            if (this.get('items').length > 0) {
                console.log("Initializing a Playlist object with an item count of:", this.get('items').length);
                console.log("items[0]", this.get('items')[0]);
                this.set('items', _.map(this.get('items'), function (playlistItemData) {
                    var returnValue;
                    //This is a bit more robust. If any items in our playlist weren't Backbone.Models (could be loaded from server data), auto-convert during init.
                    if (playlistItemData instanceof Backbone.Model) {
                        returnValue = playlistItemData;
                    } else {
                        returnValue = new PlaylistItem(playlistItemData);
                    }
                    return returnValue;
                }));

                //Playlists will remember their length via localStorage w/ their ID.
                var savedItemPosition = JSON.parse(localStorage.getItem(this.get('id') + '_selectedItemPosition'));
                this.selectItemByPosition(savedItemPosition != null ? parseInt(savedItemPosition) : 0);

                var songIds = _.map(this.get('items'), function(item) {
                    return item.get('songId');
                });

                songManager.loadSongs(songIds);
                this.set('shuffledItems', _.shuffle(this.get('items')));
            }
        },
        //TODO: Reimplemnt using Backbone.sync w/ CRUD operations on backend.
        save: function(callback) {
            if (this.get('items').length > 0) {
                var selectedItem = this.getSelectedItem();
                localStorage.setItem(this.get('id') + '_selectedItemPosition', selectedItem.get('position'));
            }

            var self = this;
            console.log("Calling save with:", self);
            console.log("my position is:", self.get('position'));
            $.ajax({
                url: 'http://localhost:61975/Playlist/SavePlaylist',
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json; charset=utf-8',
                data: JSON.stringify(self),
                success: function (data) {
                    console.log('Saving playlist was successful.', data);
                    self.set('id', data.id);
                    if (callback) {
                        callback();
                    }
                },
                error: function (error) {
                    console.error("Saving playlist was unsuccessful", error);
                }
            });
        },
        selectItemByPosition: function(position) {
            //Deselect the currently selected item, then select the new item to have selected.
            var currentlySelected = this.getSelectedItem();
            //currentlySelected is not defined for a brand new playlist since we have no items yet selected.
            if (currentlySelected != null && currentlySelected.position != position) {
                currentlySelected.set('selected', false);
            }

            var item = this.getItemByPosition(position);
            if (item != null && item.position != position) {
                item.set('selected', true);
                localStorage.setItem(this.get('id') + '_selectedItemPosition', item.get('position'));
            }

            return item;
        },
        getItemByPosition: function (position) {
            return _.find(this.get('items'), function(item) {
                return item.get('position') == position;
            });
        },
        addItem: function (song, selected) {
            console.log("this:", this.get('title'));
            var playlistId = this.get('id');
            var itemCount = this.get('items').length;

            var playlistItem = new PlaylistItem({
                playlistId: playlistId,
                position: itemCount,
                videoId: song.videoId,
                title: song.title,
                relatedVideos: [],
                selected: selected || false
            });

            this.get('items').push(playlistItem);
            this.get('shuffledItems').push(playlistItem);
            this.set('shuffledItems', _.shuffle(this.get('shuffledItems')));
            console.log("this has finished calling");

            //Call save to give it an ID from the server before adding to playlist.
            songManager.saveSong(song, function (savedSong) {
                song.id = savedSong.id;
                playlistItem.set('songId', song.id);
                console.log("calling save item");

                $.ajax({
                    type: 'POST',
                    url: 'http://localhost:61975/Playlist/SaveItem',
                    dataType: 'json',
                    data: {
                        id: playlistItem.get('id'),
                        playlistId: playlistItem.get('playlistId'),
                        position: playlistItem.get('position'),
                        songId: playlistItem.get('songId'),
                        title: playlistItem.get('title'),
                        videoId: playlistItem.get('videoId')
                    },
                    success: function (data) {
                        playlistItem.set('id', data.id);
                    },
                    error: function (error) {
                        console.error(error);
                    }
                });
            });

            return playlistItem;
        },
        addItemByVideoId: function (videoId, callback) {
            var self = this;
            ytHelper.getVideoInformation(videoId, function (videoInformation) {
                var song = songManager.createSong(videoInformation, self.get('id'));
                var addedItem = self.addItem(song);

                if (callback) {
                    callback(addedItem);
                }
            });
        },
        //Returns the currently selected playlistItem or null if no item was found.
        getSelectedItem: function() {
            var selectedItem = _.find(this.get('items'), function (item) {
                return item.get('selected');
            });

            return selectedItem;
        }
    });

    return function (config) {
        var playlist = new Playlist(config);
        playlist.on('change:title', function () {
            this.save();
        });
        return playlist;
    };
});

基本的に、項目をまったく指定しない構成オブジェクトを渡したときに、プロパティ「項目」が初期化の内部に取り込まれているのを見ています。構成オブジェクトで空の項目配列を指定すると、初期化に項目はありませんが、これは直感に反するようです。私は何か間違ったことをしていますか?

4

1 に答える 1

1

問題は、オブジェクトで参照型(配列)を使用するdefaultsことです。値を指定せずに新しいPlaylistモデルを作成するitemsと、デフォルトが適用されます。配列とオブジェクトの場合、これは問題があります。本質的に何が起こるかは次のとおりです。

newModel.items = defaults.items

したがって、この方法で初期化されたすべてのモデルは同じ配列を参照します。これを確認するために、次のことをテストできます。

var a = new Playlist();
var b = new Playlist();
var c = new Playlist({items:[]});

//add an item to a
a.get('items').push('over the rainbow');

console.log(b.get('items')); // -> ['over the rainbow'];
console.log(c.get('items')); // -> []

この問題を回避するために、BackboneはModel.defaults関数としての定義をサポートしています。

var Playlist = Backbone.Model.extend({
    defaults: function() {
        return {
            id: null,
            userId: null,
            title: 'New Playlist',
            selected: false,
            position: 0,
            shuffledItems: [],
            history: [],
            items: []
        };
    }
});
于 2013-01-02T14:37:27.260 に答える