Knockout.js unable to update server database
The following is my single page app using Knockout.js and MVC WebApi. I
have no problem getting the data from WebApi service. However, I can't
neither do a PUT nor a POST to the server. When I click the Update button
which is bound to self.update(), it always says
"baseApiUri/api/user/undefined". I thought the self.user(new
userViewModel(user)) line would set the scope of the current user record,
but it doesn't seem to be the case. I'd like to use the userViewModel
instead of hard code the getting and setting of each of the properties in
the user observable object. Thank you for your help.
@model App4MVC4.Controllers.UserViewModel
@{
ViewBag.Title = "User List";
}
@section scripts{
<script type="text/javascript">
function viewModel() {
var baseApiUri = "@Model.apiBaseUrl";
var self = this;
/********** nested view model **********/
function userViewModel(user) {
var self = this;
self.user_ID = user.user_ID;
self.firstName = user.firstName;
self.lastName = user.lastName;
self.long_name = ko.computed(function(){return
self.firstName + " " + self.lastName});
self.Login = user.Login;
self.dateAdded = user.dateAdded;
self.email = user.email;
self.alertOptIn = user.alertOptIn ? "Yes" : "No";
self.active = user.active ? "Yes" : "No";
}
/********** properties **********/
self.newUser = ko.observable();
self.userBeforeEdit = ko.observable();
self.users = ko.observableArray();
self.user = ko.observable();
self.operationStatus = ko.observable();
self.initials = [
{ "initial": "A", "apiUrl": baseApiUri + "/api/user/A" },
... omitted ....
{ "initial": "Z", "apiUrl": baseApiUri + "/api/user/Z" }
];
/********** methods **********/
//get a list of users (HttpGet)
self.getAll = function () {
self.operationStatus("Processing...");
self.users.removeAll();
$.getJSON(baseApiUri + "/api/user", function (users) {
$.each(users, function (index, user) {
self.users.push(new userViewModel(user));
});
self.operationStatus("Done");
})
.fail(function (xhr, textStatus, err) {
alert("Error: " + err); // ERROR ALERT MESSSAGE
self.user(null);
self.operationStatus(err);
});
}
// get users by initial (httpGet)
self.getByInitial = function (initial) {
var startWith = initial.initial;
if (startWith.length == 0) {
self.operationStatus("An last initial is required");
return;
}
else {
self.operationStatus("Requesting " + baseApiUri +
"/api/user?ini=" + startWith + "...");
$("#detail").hide();
self.users.removeAll();
$.getJSON(baseApiUri + "/api/user?ini=" + startWith,
function (users) {
$.each(users, function (index, user) {
self.users.push(new userViewModel(user));
});
self.operationStatus("Done");
})
.fail(function (xhr, textStatus, err) {
alert("Error: " + err); // ERROR ALERT MESSSAGE
self.user(null);
self.operationStatus(err);
});
}
}
// get user by user id (HttpGet)
self.getById = function (user, e) {
var thisId = user.user_ID;
if (thisId == NaN) {
self.operationStatus = "User Id must be numeric";
return;
}
else {
self.operationStatus("Requesting " + baseApiUri +
"/api/user/" + thisId + " ...");
$.getJSON(baseApiUri + "/api/user/" + thisId,
function (user) {
self.userBeforeEdit = new userViewModel(user);
self.user(new userViewModel(user));
$("#detailTabHandle").toggle(true);
$("#detailTab").show();
$("#detail").css("top", e.pageY >= 250 ?
e.pageY - 250 : e.pageY + "px").show();
self.operationStatus("Done");
})
.fail(function (xhr, textStatus, err) {
alert("Error: " + err); // ERROR ALERT MESSSAGE
self.user(null);
self.operationStatus(err);
});
}
}
// get user by first name, last name, or full name
self.search = function () {
self.operationStatus = "To be implemented...";
}
// update user details (HttpPut)
self.update = function () {
self.operationStatus("Requesting " + baseApiUri + " / api
/ user / " + self.user.user_ID + " ...");
$.ajax({
url: baseApiUri + "/api/user/" + self.user.user_ID,
cache: false,
type: 'PUT',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(self.user),
success: self.getAll //refresh the user list
})
.fail(function (xhr, textStatus, err) {
self.operationStatus(err);
});
}
self.cancelEdit = function () {
self.user(self.userBeforeEdit);
self.operationStatus("Edit cancelled");
$("#detail").hide();
}
// create new user (HttpPost)
self.create = function (formElement) {
self.operationStatus("Creating ...");
$(formElement).validate();
if ($(formElement).valid()) {
$.post(baseApiUri + "/api/user",
$(formElement).serialize(), null, "json")
.done(function (o) {
self.users.push(o);
})
.fail(function (xhr, textStatus, err) {
self.operationStatus(err);
});
}
}
self.cancelAdd = function () {
self.newUser(new userViewModel());
}
self.toggleVM = function () {
if ($("pre#viewModel").css("display").search("none") != -1)
$("pre#viewModel").show();
else
$("pre#viewModel").hide();
}
// initialize the view model, like page_load()
self.operationStatus("Loading....");
$.getJSON(baseApiUri + "/api/user", function (users) {
$.each(users, function (index, user) {
self.users.push(new userViewModel(user));
});
self.operationStatus("Done");
});
}
// instantiate the above ViewModel and apply Knockout binding
ko.applyBindings(new viewModel());
// make jQuery tabs
$("#tabs").tabs();
</script>
}
<section id="search">
<div class="searchCriteria">
<label for="idSearch">ID</label>
<input type="text" title="Enter an user id" id="idSearch"
maxlength="5" size="5" />
<label for="nameSearch">Name</label>
<input type="text" title="Enter a partial name or full name"
id="nameSearch" size="20" />
<br />
<label for="loginSearch">Login</label>
<input type="text" title="Ener a network login" id="loginSearch"
size="10" />
<label for="emailSearch">Email</label>
<input type="text" title="Enter an email address" id="emailSearch"
size="50" />
<br />
<label for="emailAlertSearch">Email Alert</label>
<input type="text" title="Enter an email address"
id="emailAlertSearch" maxlength="1" size="1" />
<label for="statusSearch">Account Status</label>
<input type="text" title="Enter an account status"
id="statusSearch" maxlength="1" size="1" />
</div>
<div>
<button id="searchBtn" title="Search" value="Search"
data-bind="click:search">Search</button>
</div>
</section>
<section id="list">
<h2>@ViewBag.Title</h2>
<div>
<span data-bind="foreach: initials">
<a data-bind="text: initial, click: $parent.getByInitial,
title: initial"></a>
</span>
<span>
<a data-bind="click: getAll">All</a>
</span>
</div>
<div>
<p class="status" data-bind="text: operationStatus"></p>
</div>
<span id="toggleVM" data-bind="click: toggleVM">ViewModel;
<pre id="viewModel" data-bind="text: ko.toJSON($data.users, null,
2)"></pre></span>
<!--p>Enter bid price(Write): <input data-bind="value:
formattedPrice"/></!--p>
Read Price:<p data-bind="text: formattedPrice" /-->
<table class="queryResult">
<thead>
<tr>
<th></th>
<th>User ID
</th>
<th>First Name
</th>
<th>Last Name
</th>
<th>Name
</th>
<th>Login
</th>
<th>Email
</th>
<th>Email Alert
</th>
<th>Account Status
</th>
</tr>
</thead>
<tbody data-bind="foreach: users">
<tr>
<td>
<input type="button" value="Edit" data-bind="click:
$parent.getById" />
<!--input type="button" value="Add New"
data-bind="click: create" /-->
</td>
<td data-bind="text: user_ID"></td>
<td data-bind="text: firstName"></td>
<td data-bind="text: lastName"></td>
<td data-bind="text: long_name"></td>
<td data-bind="text: Login"></td>
<td data-bind="text: email"></td>
<td data-bind="text: alertOptIn"></td>
<td data-bind="text: active"></td>
</tr>
</tbody>
</table>
</section>
<section id="detail">
<div id="tabs">
<ul>
<li><a href="#detailTab" id="detailTabHandle">User
Detail</a></li>
<li><a href="#addNewTab" id="addNewTabHandle ">New User</a></li>
</ul>
<form>
<div id="detailTab">
<div data-bind="if: user()">
<!--
<div>
<label for="userId">User ID:</label>
<span data-bind="text: user().user_ID"
id="userId">ID</span>
</div>
-->
<div>
<label for="firstName">First Name</label>
<input data-bind="value: user().firstName" type="text"
title="First Name" id="firstName" />
</div>
<div>
<label for="lastName">Last Name</label>
<input data-bind="value: user().lastName" type="text"
title="Last Name" id="lastName" />
</div>
<div>
<label for="fullName">Full Name</label>
<input data-bind="value: user().long_name" type="text"
title="Full Name" id="fullName" disabled="disabled" />
</div>
<div>
<label for="login">Login</label>
<input data-bind="value: user().Login" type="text"
title="Login" id="login" />
</div>
<div>
<label for="dateAdded">Date Added</label>
<input data-bind="value: user().dateAdded" type="text"
title="Date Added" id="dateAdded" />
</div>
<div>
<label for="email">Email</label>
<input data-bind="value: user().email" type="text"
title="Email" id="email" />
</div>
<div>
<label for="emailAlert">Email Alert</label>
<span><input data-bind="checked: user().alertOptIn"
type="radio" title="Send Email Alert" value="Yes"
name="alertOptIn"/>Yes</span>
<span><input data-bind="checked: user().alertOptIn"
type="radio" title="No Email Alert" value="No"
name="alertOptIn"/>No</span>
</div>
<div>
<label for="accountStatus">Account Status</label>
<span><input data-bind="checked: user().active"
type="radio" title="Active" value="Yes" name="active"
/>Active</span>
<span><input data-bind="checked: user().active"
type="radio" title="Inactive" value="No"
name="active"/>Inactive</span>
</div>
<input type="button" value="Update" data-bind="click:
update" />
<input type="button" value="Cancel" data-bind="click:
cancelEdit" />
<p data-bind="text:operationStatus" class="status"></p>
</div>
</div>
<div id="addNewTab">
<div>
<label for="firstNameNew">First Name</label>
<input type="text" title="First Name" id="firstNameNew" />
</div>
<div>
<label for="lastNameNew">Last Name</label>
<input type="text" title="Last Name" id="lastNameNew" />
</div>
<div>
<label for="fullNameNew">Full Name</label>
<input type="text" title="Full Name" id="fullNameNew" />
</div>
<div>
<label for="loginNew">Login</label>
<input type="text" title="Login" id="loginNew" />
</div>
<div>
<label for="emailNew">Email</label>
<input type="text" title="Email" id="emailNew" />
</div>
<div>
<label for="emailAlertNew">Email Alert</label>
<input type="text" title="Email Alert" id="emailAlertNew" />
</div>
<div>
<input type="button" value="Save" data-bind="click:
create" />
<input type="button" value="Cancel" data-bind="click:
cancelAdd" />
<p data-bind="text: operationStatus" class="status"></p>
</div>
</div>
</form>
</div>
</section>
No comments:
Post a Comment