You have asked for gift(s)
KnockOut / SignalR: Full Table IUD With Validation KnockOut / SignalR: Select For LookUp Table ModelViewController: Standard Razor Knockout Tablets ▶MVC Entry Point NorthWind Category Verbal_ActionResult_Controller HomePolicy Verbal_ActionResult_Controller HomeAbout Detailed KnockOut Library Sample
The MVC / KnockOut GameSample loads the page via the Controller returning the DbContext/DbSet for the Gift Table View( db.Gifts.ToList() );
( remember this view is controlled by @model System.Collections.Generic.IEnumerable< project.Models.Gift > )
into var initialData = function () { return @Html.Raw(Json.Encode(Model)); }(); which is the constructor for the knockout view model to populate the ko.observableArray([]).
The Sample adds the Database interaction via JSON as suggested in the following article:
Posted on July 12, 2010 Steven Sanderson's blog Editing a variable-length list, Knockout-style
ko.utils.postJson() - Could not get this to call the Controller [HttpPost] IEnumerable with a list so used jQuery ajax to POST the JSON.
Should Try: ko.utils.postJson(top.location.href + "/Update", ko.toJSON(this.gifts));
jQuery Validation: Probably need to add international locale; No doubt need to pull the form data-bind or the form will call save twice.
Do not know the result of ko data-bind "uniqueName: true" and the jQuery "required" class in the article.
Personally I prefer KnockOut extenders as field managers for typed input ( numbers, numbers with places, email, phone, post code etc ) KnockOut bindingHandlers are cool as well,
( remember this view is controlled by @model System.Collections.Generic.IEnumerable< project.Models.Gift > )
into var initialData = function () { return @Html.Raw(Json.Encode(Model)); }(); which is the constructor for the knockout view model to populate the ko.observableArray([]).
The Sample adds the Database interaction via JSON as suggested in the following article:
Posted on July 12, 2010 Steven Sanderson's blog Editing a variable-length list, Knockout-style
ko.utils.postJson() - Could not get this to call the Controller [HttpPost] IEnumerable with a list so used jQuery ajax to POST the JSON.
Should Try: ko.utils.postJson(top.location.href + "/Update", ko.toJSON(this.gifts));
jQuery Validation: Probably need to add international locale; No doubt need to pull the form data-bind or the form will call save twice.
Do not know the result of ko data-bind "uniqueName: true" and the jQuery "required" class in the article.
Personally I prefer KnockOut extenders as field managers for typed input ( numbers, numbers with places, email, phone, post code etc ) KnockOut bindingHandlers are cool as well,
ko.bindingHandlers.dateString = { init : function( ... update: function ( ... };For example bind-a-date data-bind="dateString: Birthday, valueUpdate: 'afterkeydown'" and your update by keystroke could be a date.js input which tries to date guess as you type.
- Below are the Controller, Models, View and Knockout observable Model View View Model javascript.
- You might find it easier to understand if you read the code from thebottomof the file back up to here.
A Plain Old Class Object. Note the Holding (table) part of the data model is not used in this view (model)
Gift Class Object
namespace project.Models { public class Gift { public int Id { get; set; } public string Title { get; set; } public double Price { get; set; } } } using System.Web.Configuration; using System.Data.Entity; namespace project.Models { public class HoldingContext : DbContext { // If you want Entity Framework to drop and regenerate your database // automatically whenever you change your model schema, add the following // code to the Application_Start method in your Global.asax file. // Note: this will destroy and re-create your database with every model change. // // System.Data.Entity.Database.SetInitializer( // new System.Data.Entity.DropCreateDatabaseIfModelChanges<project.Models.HoldingContext>()); public HoldingContext() : base(WebConfigurationManager.ConnectionStrings["ProjectConnect"].ConnectionString) { } public DbSet<Holding> Holding { get; set; } public DbSet<Gift> Gifts { get; set; } } }
This is the HTML for the KnockOut Grid (Shopping Cart) Form
HTML Page Coding
@model System.Collections.Generic.IEnumerable< project.Models.Gift > @{ ViewBag.Title = "Index"; } <script type="text/javascript"> var initialData = function () { return @Html.Raw(Json.Encode( Model )); }(); </script> <p>You have asked for <span data-bind="text: gifts().length"> </span> gift(s) <span id="ChatBack"></span></p> <script type="text/html" id="giftRowTemplate"> <tr> <td>Gift name: <input class="required" maxlength="10" data-bind="value: Title, uniqueName: true"/></td> <td>Price: € ¥ <input class="required number" data-bind="value: Price, uniqueName: true"/></td> <td><a href="#" data-bind="click: function () { $parent.removeGift($data) }">Delete</a></td> </tr> </script> <form id="jQVerify" class="giftListEditor" data-bind="submit: save" > <table> <tbody data-bind="template: { name: 'giftRowTemplate', foreach: gifts }"></tbody> </table> <button data-bind="click: addGift">Add Gift</button> <button data-bind="enable: gifts().length > 0" type="submit" id="idsave">Submit</button> </form>
It is possible to write the @Html.Raw() to a <div> that has style visiblility hidden display none.
The Binding
@model System.Collections.Generic.IEnumerable<montegodata.Models.Gift> <script type="text/javascript" src="~/Scripts/knockout-3.3.0.js"></script> <script type="text/javascript"> var initialData = function () { return @Html.Raw(Json.Encode( Model )); }(); </script>
The KnockOut View Model ( ko.observableArray( [] ) ) makes jQuery ajax calls to the Controller ActionResult ( IEnumerable< POCO > )
The KnockOut View Model
<script type="text/javascript"> $(document).ready(function () { function MyViewModel(initialData) { this.gifts = ko.observableArray(initialData); this.addGift = function () { this.gifts.push({ Title: "", Price: "" }); }; this.removeGift = function (gift) { var callback = this.gifts; var url = top.location.href + "/Delete?id=" + gift.Id; $.ajax({ url: url, type: 'POST', contentType: 'application/json; charset=utf-8', success: function (status) { callback.remove(gift); } }); }; this.save = function () { var url = top.location.href + "/Update"; $.ajax({ url: url, type: 'POST', contentType: 'application/json; charset=utf-8', data: ko.toJSON(this.gifts), success: function (status) { var now = new Date(); $("#ChatBack").html('Saved ' + now.toString("DD-MMM-YYYY hh:mm:ss")); //alert(status); } }); }; } var vm = new MyViewModel( initialData ); ko.applyBindings( vm, document.body ); }); </script>
The Gift Controller
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using project.Models; namespace project.Controllers { public class GiftShopController : Controller { private HoldingContext db = new HoldingContext(); // DbContext,DbSet : Plain Old Class Object's // [Views/]GiftShop/ GET: /Gift/ public ActionResult Index() { var initialState = new [] { new Gift { Id=1, Title = "Tall Hat", Price = 49.95 }, new Gift { Id=2, Title = "Long Cloak", Price = 78.25 } }; return View( db.Gifts.ToList() ); // return View( db.Gifts.ToArray() ); } [HttpPost] public ActionResult Update( IEnumerable< Gift > gifts ) { foreach (Gift g in gifts) { var record = db.Gifts.FirstOrDefault(x => x.Id == g.Id); if (record == null) { record = db.Gifts.Create(); record.Title = g.Title; record.Price = g.Price; db.Gifts.Add(record); } else { record.Title = g.Title; record.Price = g.Price; } } db.SaveChanges(); return View("Index"); //return View("Saved", gifts);//return View(db.Gifts.ToList()); } [HttpPost] public ActionResult Delete(int id) { try { using (var context = new HoldingContext()) { var record = context.Gifts.FirstOrDefault(x => x.Id == id); if (record != null) { context.Gifts.Remove(record); context.SaveChanges(); } } } catch (Exception) { } return View("Index"); } } }