0

I am working on a relatively simple web application, and my supervisor recommended that I use a combination of Knockout, MVC, and Bootstrap to accomplish my task. Unfortunately, I got stuck on what is probably a trivial part of the project.

I have seen a lot of Knockout tutorials out there, and have been trying to mimic their simplest one, found here: http://learn.knockoutjs.com/#/?tutorial=intro

Unfortunately, none of my data-binding appears to work. The observables never retain their assigned value.

I also ran across this thread while searching, and tried to mimic it, to no avail. Basically, I would just like to know how to properly implement Knockout data binding in Visual Studio MVC4. Here is my HTML code, a lot of which I ripped off from the thread mentioned above.

@model FluidBedSimulation.Models.BedState
@using Newtonsoft.Json

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

@if (false)
{
    <script src="../../Scripts/jquery-1.9.1.js" type="text/javascript"></script>
    <script src="../../Scripts/knockout-2.2.0.js" type="text/javascript"></script>

}
<script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-2.2.0.js")" type="text/javascript"></script>



<script type="text/javascript">

    this.BedMass = ko.observable(1);
    this.BedWaterMass = ko.observable(1);
    this.binderFraction = ko.observable(1);

    (function () {
        var model = '@Html.Raw(Json.Encode(Model))';
           var viewModel = ko.mapping.fromJS(model);
           ko.applyBindings(viewModel);

       });

</script>




@*grab values from the view model directly*@

<p>Bed Weight: <strong data-bind="text: BedMass" id="BedMass"></strong></p>
<p>H2O Bed Weight: <strong data-bind="text: BedWaterMass" id="BedWaterMass"></strong></p>
<p>Fraction of Binder in Spray Solution: <strong data-bind="text: binderFraction" id="binderFraction"></strong></p>

<p>
    Enter the Bed Mass: 
            <input data-bind="value: BedMass" />

            @*Html.TextBoxFor(model => model.BedMass, new { data_bind = "value: BedMass" })*@
</p>
<p>
    Enter the H2O Mass in the Bed: 
            @Html.TextBoxFor(model => model.BedWaterMass, new { data_bind = "value: BedWaterMass" })
</p>
<p>
    Enter the Fraction of Binder in the Spray Solution: 
            @Html.TextBoxFor(model => model.binderFraction, new { data_bind = "value: binderFraction" })
</p>

<button data-bind="click: Simulate">Simulate</button>

Here is the simple model I have...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace FluidBedSimulation.Models
{
    public class BedState
    {
        public double BedMass { get; set; }
        public double BedWaterMass { get; set; }
        public int binderFraction { get; set; }
        public double EvapRate { get; set; }
        public double SprayRate { get; set; }
        public double AirTemp { get; set; }
        public double AirFlow { get; set; }
    }
}

And the simple controller. Nothing fancy.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using FluidBedSimulation.Models;
using Newtonsoft.Json;


namespace FluidBedSimulation.Controllers
{
    public class SimulationController : Controller
    {
        //
        // GET: /Simulation/

        public ActionResult Index()
        {


            BedState state = new BedState
            {
                BedMass = 0,
                BedWaterMass = 0,
                binderFraction = 0,
                AirFlow = 0,
                AirTemp = 0,
                EvapRate = 0,
                SprayRate = 0
            };

            FluidBed bed = new FluidBed
            {
                FluidBedStates = new List<BedState> { state }
            };


            return View(state);
        }

    }
}

If you guys could get me started on this thing I would really appreciate it. Otherwise I might just have to stick to good ol' JQuery and html. Thanks in advance!

Community
  • 1
  • 1
tbogatchev
  • 1,531
  • 3
  • 14
  • 21
  • You shouldn't have quotes around `@Html.Raw(Json.Encode(Model))`. The `JSON.Encode` should wrap it already. – Kyeotic Jun 18 '13 at 19:59

2 Answers2

0

Your using the knockout.mapping plugin but I don't see you including the mapping plugin js file. You need to include it after the knockout lib file: https://github.com/SteveSanderson/knockout.mapping/tree/master/build/output.

Also, your anonymous function is never run. To make it immediately invoked you need to end it with parentheses like this:

(function () {
    var model = '@Html.Raw(Json.Encode(Model))';
       var viewModel = ko.mapping.fromJS(model);
       ko.applyBindings(viewModel);

})();
lagerone
  • 1,747
  • 13
  • 13
  • Thanks for the help lagerone, I uploaded and included the mapping file, and corrected the JS function, but I am still not getting anything from the bindings. When I enter values in my text boxes, tags like these `` do not update... do you know why that might be? – tbogatchev Jun 18 '13 at 20:21
  • Well, you have created and bound your viewModel before the DOM is rendered. That could be the issue. Either wrap your viewModel creation in a $(function(){}) (since your using jQuery) or move it to the bottom of the page, right before your closing body tag. – lagerone Jun 18 '13 at 20:27
0

I would recommend checking out a open source project i have put together called Shoelace

Much like @lagerone described in his post with mapping the MVC model to a Knockout model (so you don't have to maintain two model definitions), i have done was well with a different pattern.

UserList.chtml (MVC view)

@Html.PartialResource(@<script src="@Url.Content("~/Scripts/app/Views/Account/userlist.viewmodel.js")" type="text/javascript"></script>, "js")
@Html.PartialResource(
@<script type="text/javascript">
     $(function () {
         var viewModelObj = APP.userListViewModel(ko.mapping.fromJS(@Html.Raw(Model.ToJson())));
            ko.applyBindings(viewModelObj, document.getElementById("cntnrUserList"));
        });

    </script>, "js")

However, My pattern passes the model into a ViewModel object to extend if for validation, complex observables, event function definitions, etc.

userListViewModel (javascript class)

APP.userListViewModel = function (model) {
    var self = model;
    $.extend(APP.manageProfileViewModel, model);
    self.selectedUser = ko.observable({});
    self.confirmDelete = function () {
        $("#hdDeleteUserId").val(this.userId());
    };
    self.performDelete = function () {
        //alert("performDelete called for UserId: " + $("#hdDeleteUserId").val());
        var url = APP.helpers.prepareRelativeUrl("Account/DeleteUser");
        APP.helpers.performAjaxPost(url,
                JSON.stringify({ UserId: $("#hdDeleteUserId").val() }),
                function (result) {
                    if (result != null && result.success == true) {
                        alert('Your profile has been update.');
                        location.reload();
                    }
                },
                function () {
                    alert('An error has occured. Please try again.');
                });
    };
    self.cancel = function () {
        //alert("cancelDelete called");
    };
    self.showUserPermissions = function () {
        self.selectedUser(this);
        //$("#hdPermissionsUserId").val(this.userId());
    };
    self.saveUserPermissions = function () {
        var url = APP.helpers.prepareRelativeUrl("Account/SaveUserRoles");
        APP.helpers.performAjaxPost(url,
                ko.toJSON(self.selectedUser),
                function (result) {
                    //Do nothing.
                },
                function () {
                    alert('An error has occured. Please try again.');
                });
    };
    return ko.validatedObservable(self);
};

Shoelace employs the following bits:

  • ASP.NET MVC 4 using Razor (CSHTML)
  • jQuery
  • Knockout.js (using client bindings/templates instead of Razor bindings)
  • Knockout.Validation.js
  • Knockout.mapping.js
  • Twitter Bootstrap Responsive Design
  • Bootstrap-Admin-Theme by Vincent Gabriel
  • The WebMatrix SimpleMembership Implementation
  • Entity Framework 5 with Code First Data Modeling
hylander0
  • 1,091
  • 11
  • 25