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");
}
}
}
