I just taked the default example from
https://lorenzofox3.github.io/smart-table-website/#section-pipe
but it doesn't work, I code copied from the example, and assigned in the code the app: ng-app="myApp", and a controller: ng-controller="pipeCtrl as mc" to make it work, no errors shown in console
I also added some console.log prints, to know when the specific code lines are executed, and I see the next in console:
pushed it
ctr init
here is the code:
var app = angular.module('myApp', ['smart-table']);
app.controller('pipeCtrl', ['Resource', function (service) {
var ctrl = this;
console.log("ctr init");
this.displayed = [];
this.callServer = function callServer(tableState) {
console.log("callserv");
ctrl.isLoading = true;
var pagination = tableState.pagination;
var start = pagination.start || 0; // This is NOT the page number, but the index of item in the list that you want to use to display the table.
var number = pagination.number || 10; // Number of entries showed per page.
service.getPage(start, number, tableState).then(function (result) {
console.log("getP");
ctrl.displayed = result.data;
tableState.pagination.numberOfPages = result.numberOfPages;//set the number of pages so the pagination can update
ctrl.isLoading = false;
});
};
}]);
app.factory('Resource', ['$q', '$filter', '$timeout', function ($q, $filter, $timeout) {
var randomsItems = [];
function createRandomItem(id) {
var heroes = ['Batman', 'Superman', 'Robin', 'Thor', 'Hulk', 'Niki Larson', 'Stark', 'Bob Leponge'];
return {
id: id,
name: heroes[Math.floor(Math.random() * 7)],
age: Math.floor(Math.random() * 1000),
saved: Math.floor(Math.random() * 10000)
};
}
for (var i = 0; i < 1000; i++) {
randomsItems.push(createRandomItem(i));
}
console.log("pushed it");
//fake call to the server, normally this service would serialize table state to send it to the server (with query parameters for example) and parse the response
//in our case, it actually performs the logic which would happened in the server
function getPage(start, number, params) {
var deferred = $q.defer();
console.log("getting p svc");
var filtered = params.search.predicateObject ? $filter('filter')(randomsItems, params.search.predicateObject) : randomsItems;
if (params.sort.predicate) {
filtered = $filter('orderBy')(filtered, params.sort.predicate, params.sort.reverse);
}
var result = filtered.slice(start, start + number);
$timeout(function () {
console.log("timeout");
//note, the server passes the information about the data set size
deferred.resolve({
data: result,
numberOfPages: Math.ceil(filtered.length / number)
});
}, 1500);
return deferred.promise;
}
return {
getPage: getPage
};
}]);
The page html rendered in asp.net mvc:
#section scripts {
<script src="~/Scripts/angular.js"></script>
<script src="~/Scripts/app1.js"></script>
<script src="~/Scripts/smart-table.js"></script>
}
<div class="row" ng-app="myApp">
<div class="col-md-12">
<h2>Smart Ajax Data Tables</h2>
<p ng-controller="pipeCtrl as mc">
<table class="table" st-pipe="mc.callServer" st-table="mc.displayed">
<thead>
<tr>
<th st-sort="id">id</th>
<th st-sort="name">name</th>
<th st-sort="age">age</th>
<th st-sort="saved">saved people</th>
</tr>
<tr>
<th><input st-search="id" /></th>
<th><input st-search="name" /></th>
<th><input st-search="age" /></th>
<th><input st-search="saved" /></th>
</tr>
</thead>
<tbody ng-show="!mc.isLoading">
<tr ng-repeat="row in mc.displayed">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
<td>{{row.age}}</td>
<td>{{row.saved}}</td>
</tr>
</tbody>
<tbody ng-show="mc.isLoading">
<tr>
<td colspan="4" class="text-center">Loading ... </td>
</tr>
</tbody>
<tfoot>
<tr>
<td class="text-center" st-pagination="" st-items-by-page="10" colspan="4"></td>
</tr>
</tfoot>
</table>
</p>
</div>
</div>
Related
I really am struggling with something that I thought was simple...
I am making a simple search-result table based on $.getJSON call, and want to keep my code as "generic" as possible.
In my (simplified) HTML :
<form id="searchForm">
(...)
<button type="button" onclick="search()">Search</button>
</form>
(...)
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: FOO"></td>
(...)
<td data-bind="text: BAR"></td>
</tr>
</tbody>
Then in my javascript (in script tags lower in the page):
var search = function(){
var form = $('#searchForm');
$.getJSON("php/query/jsonQuery.php?jsonQuery=search", form.serialize(), function(jsonAnswer, textStatus) {
console.log(jsonAnswer);
if(typeof viewModel === 'undefined'){
var viewModel = ko.mapping.fromJS(jsonAnswer);
ko.applyBindings(viewModel);
}
else{
ko.mapping.fromJS(jsonAnswer, viewModel);
}
$('#divResults').show();
// console.log(viewModel)
});
}
This works fine on the first "search" click... but not the following : Error You cannot apply bindings multiple times to the same element.
As you can guess, this very ugly "if" testing viewModel is a desperate attempt to get rid of that error.
I've tried many things but I just can't figure out how to do it properly...
I've read this Knockout JS update view from json model and this KnockoutJs v2.3.0 : Error You cannot apply bindings multiple times to the same element but it didn't help me much... maybe because the search() function isn't called on load (and indeed shouldn't be).
Any KO master to give me a clue? Thanks in advance for your help!
This is how I would be approaching what you are trying to accomplish.
var searchService = {
search: function(form, vmData) {
//$.getJSON("php/query/jsonQuery.php?jsonQuery=search", form.serialize(), function(jsonAnswer, textStatus) {
var jsonAnswer = [{
FOO: "Item 1 Foo",
BAR: "Item 1 Bar"
}, {
FOO: "Item 2 Foo",
BAR: "Item 2 Bar"
}]
ko.mapping.fromJS(jsonAnswer, [], vmData);
//})
}
};
var PageViewModel = function() {
var self = this;
self.data = ko.observableArray();
self.hasResults = ko.pureComputed(function() {
return self.data().length > 0;
});
self.search = function() {
var form = $('#searchForm');
searchService.search(form, self.data);
};
};
ko.applyBindings(new PageViewModel());
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.0/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<form id="searchForm">
<button type="button" data-bind="click:search">Search</button>
</form>
<div data-bind="visible: !hasResults()"><b>No Results</b></div>
<div data-bind="visible: hasResults">
<table class="table">
<thead>
<tr>
<td>FOO</td>
<td>BAR</td>
</tr>
</thead>
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: FOO"></td>
<td data-bind="text: BAR"></td>
</tr>
</tbody>
</table>
</div>
<br/>
<pre><code data-bind="text: ko.toJSON($root)"></code></pre>
I want to repeat a dropdownlist that is already bound using a Viewbag property and another textbox when a user click on Add Course.
I have used asp.net mvc and knockout.js based on a tutorial i saw, but the tutorial does not exactly handle using bound controls, please how can i achieve this using asp.net mvc and knockout.js.
Below is my code.
Thanks
<table id="jobsplits">
<thead>
<tr>
<th>#Html.DisplayNameFor(m => m.selectedCourse.FirstOrDefault().FK_CourseId)</th>
<th>#Html.DisplayNameFor(m => m.selectedCourse.FirstOrDefault().CourseUnit)</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: courses">
#for (int i = 0; i < Model.selectedCourse.Count; i++)
{
<tr>
<td>
#Html.DropDownListFor(model => model.selectedCourse[i].FK_CourseId, new SelectList(ViewBag.Course, "Value", "Text", Model.FK_CourseId), "Select Course", new { #class = "form-control", data_bind = "value: courses" })
</td>
<td>
#Html.TextBoxFor(model => model.selectedCourse[i].CourseUnit, new { htmlAttributes = new { #class = "form-control", #readonly = "readonly", data_bind = "value: courseUnit" } })
</td>
<td>
<button type="button" data-bind="click: $root.removeCourse" class="btn delete">Delete</button>
</td>
</tr>
}
</tbody>
</table>
<div class="col-md-4">
<button data-bind="click: addCourse" type="button" class="btn">Add Course</button>
</div>
This is the script section
#section Scripts{
#Scripts.Render("~/bundles/knockout")
<script>
function CourseAdd(course, courseUnit) {
var self = this;
self.course = course;
self.courseUnit = courseUnit;
}
function CourseRegViewModel() {
var self = this;
self.addCourse = function () {
self.courses.push(new CourseAdd(self.course, self.courseUnit));
}
self.courses = ko.observableArray([
new CourseAdd(self.course, self.courseUnit)
]);
self.removeCourse = function (course) {
self.courses.remove(course)
}
}
ko.applyBindings(new CourseRegViewModel());
</script>
}
Edit:
i have been able to get this sample working from: http://learn.knockoutjs.com/#/?tutorial=collections
but it is only an hard-coded observableArray.
I want to be able to populate the select from the database. But it is not getting populated.
This is my sample code below:
<table id="jobsplits">
<thead>
<tr>
<th>Persenger Name</th>
<th>Meal</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: seats">
<tr>
<td>
<input data-bind="value: name" />
</td>
<td>
<select data-bind="options:coursesArray, optionsText:'Text', optionsValue:'Value', optionsCaption: 'Choose...'"></select>
</td>
<td>
<button type="button" data-bind="click: $root.removeSeat" class="btn delete">Delete</button>
</td>
</tr>
</tbody>
</table>
<div class="col-md-4">
<button data-bind="click: addSeat">Add Seat</button>
</div>
This is the adjusted script section:
<script>
function SeatReservation(name, initialMeal) {
var self = this;
self.name = name;
self.meal = ko.observable(initialMeal);
}
function ReservationsViewModel() {
var self = this;
//This is what i want to put in dropdown instead
self.thecourses.subscribe(function () {
getCourses();
});
// Editable data
self.seats = ko.observableArray([
new SeatReservation("Steve", self.thecourses),
new SeatReservation("Bert", self.thecourses)
]);
self.addSeat = function () {
self.seats.push(new SeatReservation("", self.availableMeals[0]));
}
self.removeSeat = function (seat) { self.seats.remove(seat) }
var getCourses = function () {
var collegeCode = $("#Colleges").val();
var departmentCode = $("#Departments").val();
var url = '#Url.Action("GetCourses", "Student")';
$.getJSON(url, { deptId: departmentCode, collegeId: collegeCode }, function (data) {
self.coursesArray(data)
});
}
}
ko.applyBindings(new ReservationsViewModel());
</script>
here is the html table I am working on:
<tbody data-bind="foreach: xusers.list">
<tr data-bind="css:{'approved-false' : member()==1 }">
<td data-bind="text: member"></td>
<td data-bind="text: expired"></td>
<td data-bind="text: name"></td>
<td data-bind="text: email"></td>
</tr>
</tbody>
The value of the array item member displays in the table just fine but my css call is not working no matter what syntax I have tried. I just want to apply this class if member is true (member contains either 1 or 0).
What am I missing?
Additional Code - Am I overwriting observable?...Here is the only place anything is written to the array.
if (!data) {
...
}
else {
if (data.length) {
var curItem;
for (var i=0; i<data.length; i++) {
curItem = new xuser();
curItem.name = data[i].name;
curItem.email = data[i].email;
curItem.city = data[i].city;
curItem.region = data[i].region;
curItem.country = data[i].country;
curItem.expires = data[i].expires;
curItem.member = data[i].member;
curItem.expired = data[i].expired;
xusers.list.push(curItem)
totalRecs = data[i].TotalCount;
}
}
} // if (!data)/else
You were close, just had it backward: see http://knockoutjs.com/documentation/css-binding.html
<tbody data-bind="foreach: xusers.list">
<tr data-bind="css:{'approved-false': member() == 1 }">
<td data-bind="text: member"></td>
<td data-bind="text: expired"></td>
<td data-bind="text: name"></td>
<td data-bind="text: email"></td>
</tr>
</tbody>
You are also overwriting your observables, see below (this assumes that all fields on curItem are observables, update your own as needed):
if (!data) {
...
}
else {
if (data.length) {
var curItem;
for (var i=0; i<data.length; i++) {
curItem = new xuser();
curItem.name(data[i].name);
curItem.email(data[i].email);
curItem.city(data[i].city);
curItem.region(data[i].region);
curItem.country(data[i].country);
curItem.expires(data[i].expires);
curItem.member(data[i].member);
curItem.expired(data[i].expired);
xusers.list.push(curItem)
// this should probably be somewhere else
totalRecs = data[i].TotalCount;
}
}
} // if (!data)/else
My app is based on the HotTowel template so it includes Durandal, Knockout & Breeze. I have a page with 3 tables side by side. The first table has a list of "templates', the 2nd table shows "sections" for the selected "template" and the 3rd table shows "items" for the selected "section". The "sections" and "items" tables are collections accessed via navigation properties. I find that I get intermittent binding issues. The data in the "templates" table always shows correctly, however related "sections" and "items" sometimes show correctly and other times one of the other is not populated. It would seem to be a timing issue. My view model and view are below. Am I just going about all of this the wrong way?
define(['services/dataservice', 'services/logger', 'services/model'],
function (ds, logger, model) {
var templates = ko.observableArray();
var selectedTemplate = ko.observable();
var selectedSection = ko.observable();
var selectedItem = ko.observable();
var newTemplateTitle = ko.observable();
var newSectionTitle = ko.observable();
var newItemTitle = ko.observable();
function activate() {
newTemplateTitle('');
newSectionTitle('');
newItemTitle('');
logger.log('Templates view activated', null, 'templates', false);
return ds.getTemplatePartials(templates, false, false);//.then(succeeded);
//function succeeded() {
// var firstTemplate = templates()[0];
// setSelectedTemplate(firstTemplate);
//}
}
templates.subscribe(function() {
var firstTemplate = templates()[0];
setSelectedTemplate(firstTemplate);
});
var deactivate = function () {
templates([]);
};
function refresh() {
return ds.getTemplatePartials(templates, true, false);
}
var viewAttached = function (view) {
bindEventToList(view, '#template-list', setSelectedTemplate);
bindEventToList(view, '#section-list', setSelectedSection);
bindEventToList(view, '#item-list', setSelectedItem);
return true;
};
var addTemplate = function () {
var newTemplate = ds.createEntity(model.entityNames.document);
newTemplate.title(newTemplateTitle());
newTemplate.isTemplate(true);
newTemplate.organisation(ds.getCurrentOrganisation()());
return ds.saveChanges().then(saveSucceeded);
function saveSucceeded() {
templates.push(newTemplate);
templates.sort();
newTemplateTitle('');
}
};
var addSection = function () {
var newSection = ds.createEntity(model.entityNames.section);
newSection.title(newSectionTitle());
newSection.isTemplate(true);
newSection.document(selectedTemplate());
return ds.saveChanges().then(saveSucceeded);
function saveSucceeded() {
newSectionTitle('');
}
};
var addItem = function () {
var newItem = ds.createEntity(model.entityNames.item);
newItem.title(newItemTitle());
newItem.isTemplate(true);
newItem.section(selectedSection());
return ds.saveChanges().then(saveSucceeded);
function saveSucceeded() {
newItemTitle('');
}
};
var isTemplateSelected = function (template) {
if (template && selectedTemplate()) {
var thisId = ko.utils.unwrapObservable(selectedTemplate().id);
return ko.utils.unwrapObservable(template.id) == thisId;
}
return false;
};
var isSectionSelected = function (section) {
if (section && selectedSection()) {
var thisId = ko.utils.unwrapObservable(selectedSection().id);
return ko.utils.unwrapObservable(section.id) == thisId;
}
return false;
};
var isItemSelected = function(item) {
if (item && selectedItem()) {
var thisId = ko.utils.unwrapObservable(selectedItem().id);
return ko.utils.unwrapObservable(item.id) == thisId;
}
return false;
};
var vm = {
activate: activate,
deactivate: deactivate,
templates: templates,
//sections: sections,
//items: items,
selectedTemplate: selectedTemplate,
selectedSection: selectedSection,
selectedItem: selectedItem,
title: 'Template Maintenance',
refresh: refresh,
viewAttached: viewAttached,
addTemplate: addTemplate,
addSection: addSection,
addItem: addItem,
newTemplateTitle: newTemplateTitle,
newSectionTitle: newSectionTitle,
newItemTitle: newItemTitle,
isTemplateSelected: isTemplateSelected,
isSectionSelected: isSectionSelected,
isItemSelected: isItemSelected
};
return vm;
//#region internal methods
function setSelectedTemplate(data) {
if (data) {
selectedTemplate(data);
return selectedTemplate().entityAspect.loadNavigationProperty("sections").then(setFirstSectionSelected);
} else {
return false;
}
function setFirstSectionSelected() {
setSelectedSection(selectedTemplate().sections()[0]);
}
}
function setSelectedSection(data) {
if (data) {
selectedSection(data);
return selectedSection().entityAspect.loadNavigationProperty("items").then(setFirstItemSelected);
} else {
selectedSection();
selectedItem();
return false;
}
function setFirstItemSelected() {
setSelectedItem(selectedSection().items()[0]);
}
}
function setSelectedItem(data) {
if (data) {
selectedItem(data);
} else {
selectedItem();
}
}
function bindEventToList(rootSelector, selector, callback, eventName) {
var eName = eventName || 'click';
$(rootSelector).on(eName, selector, function () {
var item = ko.dataFor(this);
callback(item);
return false;
});
}
//#region
}
);
<section>
<div class="row-fluid">
<header class="span12">
<button class="btn btn-info pull-right push-down10" data-bind="click: refresh">
<i class="icon-refresh"></i> Refresh</button>
<h4 class="page-header" data-bind="text: title"></h4>
</header>
</div>
<div class="row-fluid">
<section class="span3">
<header class="input-append">
<input id="newTemplateName"
type="text"
data-bind="realTimeValue: newTemplateTitle"
placeholder="New template name"
class="input-medium" />
<button class="btn btn-info add-on" data-bind="click: addTemplate, disable: newTemplateTitle() === ''">
<i class="icon-plus"></i> Add</button>
</header>
<article>
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Templates</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: templates -->
<tr id="template-list" data-bind="css: { 'selected': $root.isTemplateSelected($data) }">
<td data-bind="text: title" />
</tr>
<!-- /ko -->
</tbody>
</table>
<span>Count: <span data-bind="text: templates().length"></span></span>
</article>
</section>
<section class="span5">
<header class="input-append">
<input id="newSectionName"
type="text"
data-bind="realTimeValue: newSectionTitle"
placeholder="New section name"
class="input-medium" />
<button class="btn btn-info add-on" data-bind="click: addSection, disable: newSectionTitle() === ''">
<i class="icon-plus"></i> Add</button>
</header>
<article data-bind="if: selectedTemplate">
<table class="table table-striped table-bordered table-hover" >
<thead>
<tr>
<th data-bind="text: 'Sections for ' + selectedTemplate().title()"></th>
</tr>
</thead>
<tbody>
<!-- ko foreach: selectedTemplate().sections() -->
<tr id="section-list" data-bind="css: { 'selected': $root.isSectionSelected($data) }">
<td data-bind="text: title" />
</tr>
<!-- /ko -->
</tbody>
</table>
<span>Count: <span data-bind="text: selectedTemplate().sections().length"></span></span>
</article>
</section>
<section class="span4">
<header class="input-append">
<input id="newItemName"
type="text"
data-bind="realTimeValue: newItemTitle"
placeholder="New item name"
class="input-medium" />
<button class="btn btn-info add-on" data-bind="click: addItem, disable: newItemTitle() === ''">
<i class="icon-plus"></i> Add</button>
</header>
<article data-bind="if: selectedSection">
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th data-bind="text: 'Items for ' + selectedSection().title()"></th>
</tr>
</thead>
<tbody>
<!-- ko foreach: selectedSection().items() -->
<tr id="item-list" data-bind="css: { 'selected': $root.isItemSelected($data) }">
<td data-bind="text: title" />
</tr>
<!-- /ko -->
</tbody>
</table>
<span>Count: <span data-bind="text: selectedSection().items().length"></span></span>
</article>
</section>
</div>
Similar issue (same server, same client but sometimes some navigators not working in breeze) happened to me. In my opinion it can be a timing bug. Or the create/load order of entities count.
I've changed this paralell async load entities from server:
return promise = Q.all([
getPackage(),
getClubPartials(null, true),
getAddressPartials(null, true),
getEventPartials(null, true)
]).then(success);
to this get them one by one:
return getClubPartials(null, true).then(function () {
getAddressPartials(null, true).then(function () {
getEventPartials(null, true).then(function () {
return getPackage().then(success);
})
})
});
And my problem gone!
There is just a lot of code to wade through and try to make sense of in the imagination.
You seem to say it works some of the time. That does sound like a timing issue.
One thing that is disturbing is the async methods (e.g. setSelectedTemplate) that return either false or a promise; not sure why the inconsistency. But that is probably not the real problem.
You could try putting a setTimeout(..., 10) before exiting the async methods. See if that changes the behavior.
If that doesn't reveal it, you'll have to boil it down to just those the essentials that reveal the problem.
Sadly no sudden flash of insight today.
Update June 3
My first concern is that some of your methods return promises and some return the value false. I'd feel more comfortable if they all returned promises. Look at Q.resolve().
Other code baffles me. For example, what is going on in the many variations on this pattern:
function setSelectedItem(data) {
if (data) {
selectedItem(data);
} else {
selectedItem();
}
}
The else{...} isn't doing anything useful. It unwraps the selectedItem property ... and discards the value. What is the point?
And what is the difference between this:
thisId = ko.utils.unwrapObservable(selectedTemplate().id); // obscure
and this?
thisId = selectedTemplate().id(); // clear
Are you unsure whether selectedTemplate().id is a KO observable? The only reason to use unwrapObservable is when you are unsure.
As for timing, you can let KO know that it should re-fresh a binding by calling valueHasMutated() on an observable. For example:
function setFirstItemSelected() {
setSelectedItem(selectedSection().items()[0]);
selectedItem.valueHasMutated();
}
It might help. I don't know ... you're stacking a long chain of dependencies and it isn't easy to see which are bailing out when. That would take study of actual data flows as well as the code. You cannot reasonably expect your StackOverflow audience to work that hard for free.
Try cutting this way down to a super-simple example that displays the troublesome behavior.
Best of luck.
Working on learning bootstrap and knockout.js. This is more of a knockout question.
I would like to populate a new row of a table (using addSeat function), and if the name field on that new row is empty, add the bootstrap 'error' class to the row. It is empty by default. Once the name field is entered, the style should change to 'success'.
The basic code is taken from the Seat Reservation samples. Here is the markup:
<div id="food" class="span10">
<h2>Your seat reservations (<span data-bind="text: seats().length"></span>)</h2>
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>Passenger name</th><th>Meal</th><th>Surcharge</th><th></th>
</tr></thead>
<tbody data-bind="foreach: seats">
<tr data-bind="css: isnameBlank">
<td><input data-bind="value: name" /></td>
<td><select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select></td>
<td data-bind="text: formattedPrice"></td>
<td><i class="icon-remove"></i>Remove</td>
</tr>
</tbody>
</table>
<button data-bind="click: addSeat, enable: seats().length < 8">Reserve another seat</button>
<h3 data-bind="visible: totalSurcharge() > 0">
Total surcharge: $<span data-bind="text: totalSurcharge().toFixed(2)"></span>
</h3>
</div>
Here is the js file:
// 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.formattedPrice = ko.computed(function () {
var price = self.meal().price;
return price ? "$" + price.toFixed(2) : "None";
});
self.isnameBlank = ko.computed(function () {
var ib = self.name().length;
console.log(ib);
return ib == 0 ? "warning" : "success";
}, self);
}
// 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.remove(seat) }
}
ko.applyBindings(new ReservationsViewModel(), document.getElementById('food'));
When I run this the console logs the correct length (the ib variable), but the css class does not change.
Thank you for your help!
Where you have this line:
var ib = self.name.length;
You should be doing this:
var ib = self.name().length;
This seems to be working just fine when I test it in Chrome. Here is the jsFiddle:
http://jsfiddle.net/Xfv2g/
The only thing I can assume is that you are expecting it to change as they type. In order to do that you will have to change when the name field binds by putting the valueUpdate: 'afterkeydown' modifier to the value binding.
Here is the same fiddle with that being the only difference.
http://jsfiddle.net/Xfv2g/1/