2

I'm trying to use knockout.js to perform add/remove operations on a server-side property. Here's what my C# Model looks like:

public class MyModel
{
    [Key]
    public int MyModelId { get; set; }

    [Required]
    public string Name { get; set; }

    private IEnumerable<string> _Items;

    [Required]
    public IEnumerable<string> Items
    {
        get { return _Items; }
        set { _Items = value; }
    }

    //Used to store in SQL database
    public string ItemsSQL
    {
        get { return _Items != null ? String.Join(";", _Items) : null; }
        set { _Items = value != null ? value.Split(';').ToList() : null; }
    }
}

Here's what my ViewModel looks like:

@model MyProject.Models.MyModel

<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>
<script type="text/javascript">
    @{
        var initialData = new JavaScriptSerializer().Serialize(Model);
    }
    var viewModel = {
        MyModel: ko.observable(@Html.Raw(initialData)),

        //Commented out on purpose
        //Items: ko.observableArray(this.MyModel.Items),

        addItem: function() { 
            this.MyModel.Items.push("");
        },

        removeItem: function(item) {
            this.MyModel.Items.remove(item);
        },
    };

    alert(JSON.stringify(@Html.Raw(initialData)));

    $(document).ready(function() {ko.applyBindings(viewModel); });
</script>

And Here's my view:

Name: <span data-bind="text: MyModel().Name"></span>
<br />
Items: <button data-bind="click: addItem">Add Item</button>

<table>
    <tbody data-bind="template: { name: 'itemTemplate', foreach: MyModel().Items}"></tbody>
</table>

<script type="text/html" id="itemTemplate">
    <tr>
        <td>
            <input data-bind="value: $data" />
            <a href="#" data-bind="click: function() { 
                                   viewModel.removeItem($data) }">Remove Item</a>
        </td>
    </tr>
</script>

This displays MyModel.Name along with each item in MyModel.Itemsdisplayed in a textbox. My problem is when I try to instantiate Items, it spits out the error message: Unable to get value of the property 'Items': object is null or undefined. This also affects my add and remove operations.

UPDATE

When I do alert(JSON.stringify(this.MyModel));, it shows that MyModel is undefined, but when I do alert(JSON.stringify(@Html.Raw(initialData))); my model is displayed in JSON format. So, my Model is serializing correctly, but knockout is unable to create an observable property from a C# variable? I don't know. Razor is a pain.

2 Answers 2

5

The ViewModel should be constructed as follows:

@{
    var initialData = new JavaScriptSerializer().Serialize(Model);
}
var data = @Html.Raw(initialData);
function ViewModel(data) {
    var self = this;
    self.Name = ko.observable(data.Name);
    self.Items = ko.observableArray(data.Items);
    self.addItem = function() { self.Items.push(""); };
    self.removeItem = function(data) { self.Items.remove(data); }
}
$(document).ready(function() {ko.applyBindings(new ViewModel(data)); });
Sign up to request clarification or add additional context in comments.

Comments

2

I think that initialData is a C# variable but you're trying to alert it through javascript.

It's a bit confusing because it's hard to tell how razor is treating the var within the @{ }

If you want to stringify it you'll have to put it into a javascript variable first. Something like:

var jsInitialData = @{Html.Raw(initialData)}

Razor is a bit of a pain within javascript blocks so that won't work exactly, but I suspect you need something along those lines.

3 Comments

I should've realized that. So, my model is being serialized correctly, but that still doesn't explain why I can't instantiate Items. Am i using the wrong syntax?
How are you trying to instantiate it?
Items: ko.observableArray(this.MyModel.Items)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.