mcottingham
mcottingham

Reputation: 2056

Knockout Complex Data Model Binding

Let's start with the code..

Javascript to bind viewmodel and display dialog

$("#add-song").click(function () {
    $("<div>")
        .attr("data-bind", "template: { name: 'manage-song-template' }")
        .dialog({
            resizable: false,
            modal: true,
            title: "Add Song",
            width: 960,
            height: 490,
            buttons: [
                {
                    text: "Create Song",
                    'data-bind': 'click: savechanges',
                    click: function () {

                    }
                },
                {
                    text: "Close",
                    click: function () {
                        $(this).dialog("close");
                    }
                }
            ],
            close: function () {
                $(this).dialog('destroy').remove();
            },
            open: function () {
                ko.applyBindings(new my.managesongviewmodel());
                jquerybindings();
            }
        });        
});

Now let's take a look at the view model

my.managesongviewmodel = function () {
    var
        //Scalar Properties
        song = null,

        //Functions
        loadfromserver = function (Id) {
            $.post("/song/getsong", "id=1", function (response) {
                if (response.Success) {
                    var data = response.Data;
                    var song = response.Data.Song;

                    var songdata = {
                        Song: {
                            Id: song.Id,
                            Title: song.Title,
                            Description: song.Description,
                            Lyrics: song.Lyrics,
                            isMaster: song.isMaster,
                            AudioFilePath: song.AudioFilePath,
                            CoverImageFilePath: song.CoverImageFilePath,
                            CreatedDate: song.CreatedDate
                        },
                        Genres: data.Genres,
                        SongAlternateTitles: data.SongAlternateTitles,
                        Exploitations: data.Exploitations,
                        SongWriterSongs: data.SongWriterSongs
                    };                        
                    song = new my.song(songdata);
                    alert(song.title());
                }
            });
        },

        savechanges = function () {
        };

    loadfromserver();
    return {
        song: song,
        loadfromserver: loadfromserver
    };
};

my.song = function (data) {
    var
        //Scalar Properties
        id = ko.observable(data.Song.Id),
        title = ko.observable(data.Song.Title),
        description = ko.observable(data.Song.Description),
        lyrics = ko.observable(data.Song.Lyrics),
        ismaster = ko.observable(data.Song.isMaster),
        audiofilepath = ko.observable(data.Song.AudioFilePath),
        coverimagefilepath = ko.observable(data.Song.CoverImageFilePath),
        createddate = ko.observable(data.Song.CreatedDate),

        //Arrays
        genres = ko.observableArray(data.Genres),
        alttitles = ko.observableArray(data.SongAlternateTitles),
        exploitations = ko.observableArray(data.Exploitations),
        songwritersongs = ko.observableArray(data.SongWriterSongs);

    return {
        id: id,
        title: title,
        description: description,
        lyrics: lyrics,
        ismaster: ismaster,
        audiofilepath: audiofilepath,
        coverimagefilepath: coverimagefilepath,
        createddate: createddate,

        genres: genres,
        alttitles: alttitles,
        exploitations: exploitations,
        songwritersongs: songwritersongs
    };
};

Here is my template

<script type="text/html" id="manage-song-template">
    <div class="modal-form">
        <span data-bind="text: song.title"></span>
    </div>
</script>

Chrome is throwing the error "Cannot read property 'title' of null;" when I launch the dialog. That said, the "alert(song.title());" actually shows a the song title.

Any thoughts on where I've gone wrong?

Update

I have created a jsfiddle that replicates the issue. http://jsfiddle.net/mcottingham/H7jqa/28/

Upvotes: 1

Views: 809

Answers (1)

Sergii Stotskyi
Sergii Stotskyi

Reputation: 5390

It's easy. In the moment when you shows modal window your song var still equals null. So you have to wait while info about song will be loaded from server:

$("<div>").attr("data-bind", "template: { name: 'manage-song-template' }").dialog({
   // another options
   open: function (event, ui) {
     var vm = new my.managesongviewmodel(), domNode = this;
     vm.loadfromserver().done(function() {
       ko.applyBindings(vm, domNode);
       jquerybindings();
     });
   }
})

// another code

And add return statement at the begining of loadfromserver function:

loadfromserver = function (Id) {
   return $.post("/song/getsong", "id=1", function (response) {
    // code
   });
}

Upvotes: 5

Related Questions