Roland Wales
Roland Wales

Reputation: 73

Using Knockout JS, update a table cell on a screen from Javascript

I am using Knockout JS to work with an editable grid. Updates work fine when typing values into the table cells. However, I need to set a date as a result of clicking a "set complete date" button. The Javascript update sets the value in the view model, but does not update the table cell on the screen. Based on web searches, this should work if the cell is observable. For some reason, it doesn't. The attached code is self contained, and illustrates the problem.

The following code is a simplified version of what I'm trying to accomplish. By clicking on the name field, the view model is updated with "a new name". I want this to update the cell on the html display as well.

<!DOCTYPE html>
 <html lang="en">
     <head>
         <meta charset="utf-8" />
         <title> - Fresh Water Buoy data maintenance</title>
         <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" />
         <meta name="viewport" content="width=device-width" />
         <link href="/Content/site.css" rel="stylesheet"/>

         <script src="/Scripts/modernizr-2.6.2.js"></script>

     </head>
     <body>
         <header>
             <div class="content-wrapper">
                 <div class="float-left">
                     <p class="site-title"><a href="/">Fresh Water Buoys</a></p>
                 </div>
                 <div class="float-right">
                     <!--
                     <section id="login">
                        Hello, <span class="username">SOM\roland.wales</span>!
                     </section>
                         -->
                     <nav>
                         <ul id="menu">
                             <li><a href="/">Home</a></li>
                             <li><a href="/Home/About">About</a></li>
                             <li><a href="/Home/Contact">Contact</a></li>
                         </ul>
                     </nav>
                 </div>
             </div>
         </header>
         <div id="body">

             <section class="content-wrapper main-content clear-fix">
                 <h2>Your seat reservations (<span data-bind="text: seats().length"></span>)</h2>

 <table>
     <thead>
         <tr>
             <th>Passenger name</th>
             <th>Meal</th>
             <th>Modified name</th>
             <th>Surcharge</th>
             <th></th>
         </tr>
     </thead>
     <tbody data-bind="foreach: seats">
         <tr>
             <td><input data-bind="value: name, click: $root.nameClick" /></td>
             <td><select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select></td>
             <td data-bind="text: modifiedName"></td>
             <td data-bind="text: formattedPrice"></td>
             <td><a href="#" data-bind="click: $root.removeSeat">Remove</a></td>
         </tr>
     </tbody>
 </table>

 <button data-bind="click: addSeat, enable: seats().length < 5">Reserve another seat</button>

 <h3 data-bind="visible: totalSurcharge() > 0">
     Total surcharge: $<span data-bind="text: totalSurcharge().toFixed(2)"></span>
 </h3>



             </section>
         </div>
         <footer>
             <div class="content-wrapper">
                 <div class="float-left">
                     <p>&copy; 2016 - My ASP.NET MVC Application</p>
                 </div>
             </div>
         </footer>

         <script src="/Scripts/jquery-1.8.2.js"></script>


     <script src="/Scripts/knockout-2.2.0.js"></script>
     <script type="text/javascript">


 // Class to represent a row in the seat reservations grid
 function SeatReservation(name, initialMeal) {
     var self = this;
     self.name = ko.observable(name);
     self.meal = ko.observable(initialMeal);

     self.modifiedName = ko.computed(function () {
         debugger;
         var name2 = self.name();
         self.name(name2);
         return name2;
     });

     self.formattedPrice = ko.computed(function () {
         var price = self.meal().price;
         return price ? "$" + price.toFixed(2) : "None";
     });
 }


 // Overall viewmodel for this screen, along with initial state
 function ReservationsViewModel() {
     var self = this;

     // Non-editable catalog data - would come from the server
     self.availableMeals = [
         { mealName: "Standard (sandwich)", price: 0 },
         { mealName: "Premium (lobster)", price: 34.95 },
         { mealName: "Ultimate (whole zebra)", price: 290 }
     ];

         // Editable data
     self.seats = ko.observableArray([
             new SeatReservation("Steve", self.availableMeals[0]),
             new SeatReservation("Bert", self.availableMeals[0])
     ]);

     // Computed data
     self.totalSurcharge = ko.computed(function () {
         var total = 0;
         for (var i = 0; i < self.seats().length; i++)
             total += self.seats()[i].meal().price;
         return total;
     });

     // Operations
     self.addSeat = function () {
         self.seats.push(new SeatReservation("", self.availableMeals[0]));
     }
     self.removeSeat = function (seat) { self.seats.destroy(seat) }  /* use destroy instead of remove to set _destroy property */

     /*
      * This is an attempt to update the name in the table cell that displays on the screen.
      * It updates the view model, but not not update the visible display of the table cell as a result of clicking on the field.
      * If the value of the field is changed by typing on the screen, then the "a new name" shows up.
      */
     self.nameClick = function (seat) {
         var name2;
         for (var i = 0; i < self.seats().length; i++)
             name2 = self.seats()[i].name();
         seat.name = ko.observable("a new name");
         seat.name.valueHasMutated();
         for (var i = 0; i < self.seats().length; i++)
             name2 = self.seats()[i].name();
     }
 }

 ko.applyBindings(new ReservationsViewModel());


 </script>


 <!-- Visual Studio Browser Link -->
 <script type="application/json" id="__browserLink_initializationData">
     {"appName":"Internet Explorer","requestId":"d50efb9955144462bb4ac42399aca97d"}
 </script>
 <script type="text/javascript" src="http://localhost:51245/e1dce8087f8e4b289690f61d419887fb/browserLink" async="async"></script>
 <!-- End Browser Link -->

 </body>
 </html>

Upvotes: 0

Views: 761

Answers (1)

Eric Bronnimann
Eric Bronnimann

Reputation: 951

I copied and pasted your code into JSFiddle. The issue is in the nameClick() function. You are setting the seat.name value to a new observable, when it should just update the observable.

Change the following:

seat.name = ko.observable("a new name");

To this:

seat.name("a new name");

And the code should work.

Upvotes: 1

Related Questions