For my first stackoverflow question. Please pardon my general lack of understanding. I'm working on that.
I am auto-generating a table based on user identified dates that have different record values for each day provided. I am referencing a .JSON metagen/metadata file that defines the rows and columns, yet I cannot seem to find a way to filter out the results that are being passed as empty objects. I want to delete the rows without results. I believe I need to create a or reference a viewmodel function inside a binding, but everything I have tried so far has failed.
I have a bindings file that reads in the data and calculates the fields as they are queried based on the record names. The records are then created with knockout foreach statements and the values are taken from a Subgrid() function found in my bindings file. The data is read in as number format.
The View
<!-- ko foreach: $data.cols -->
<th data-bind="text:$data.label === undefined ? $data.value:$data.label"></th>
<!-- /ko -->
</tr>
</thead>
<tbody>
<!-- ko foreach: $data.rows -->
<tr>
<td data-bind="text:$data.label === undefined ? $data.value:$data.label"></td>
<!-- ko foreach: $parent.cols -->
<td data-class="subgridRow"></td>
<!-- /ko -->
</tr>
<!-- /ko -->
The Viewmodel
/*global define,sandbox */
define([
'scalejs.sandbox!subgrid',
], function (
sandbox
) {
'use strict';
return function (node) {
var observable = sandbox.mvvm.observable,
merge = sandbox.object.merge,
text = observable(node.text);
sandbox.flag.invoke('loadUwf');
return merge(node, {
text: text }
The return portion of the bindings file.
//remove values for missing LoB records and return the value
loop1: for (var obj in exdata){
for (var key in exdata[obj]){
if (exdata[obj][key]=== parentValue || ctx.$parent.value === "TOTAL"){
if( !text ){text = 0;}
if (ctx.$parent.value === "TOTAL"){
return {text: recalc.total.toFixed(round) + ' ' + unit,}}
else{return {text: recalc.val.toFixed(round) + ' ' + unit,}
break loop1;}
Related
I'm trying to reimplement a student attendance example from the Udacity JavaScript Design Patterns course. So far I've managed to recreate the table and correctly populate it with some student data, however it appears when I change a checkbox value this isn't updated in the model.
For example, when I display
debugVM.studentList()[0].days();
in the console the output displays the initial attendance data instead of the current state of the checkboxes. A JSFiddle for this can be found here.
index.html
<table>
<thead>
<tr>
<th>Student</th>
<!-- ko foreach: attendance -->
<th data-bind="html: $data"></th>
<!-- /ko -->
</tr>
</thead>
<tbody data-bind="foreach: studentList">
<tr>
<td data-bind="html: name, click: $parent.debugStudent"></td>
<!-- ko foreach: days -->
<td>
<input type="checkbox" data-bind="value: $data, checkedValue: $data, checked: $data" />
</td>
<!-- /ko -->
</tr>
</tbody>
</table>
app.js
var Student = function(student){
this.name = ko.observable(student.name);
this.days = ko.observableArray(student.days);
};
var ViewModel = function() {
var self = this;
this.attendance = ko.observableArray([1,2,3,4,5,6,7,8,9,10,11,12]);
this.studentList = ko.observableArray([]);
students.forEach(function(student) {
self.studentList.push(new Student(student));
});
self.debugStudent = function() {
console.log(self.studentList()[0].days());
};
};
var debugVM = new ViewModel();
ko.applyBindings(debugVM);
From the knockout documentation
Key point: An observableArray tracks which objects are in the array,
not the state of those objects
In your case this means that days should be not observable array, but array of observables.
For example you can add new viewModel Day:
var Day = function(isChecked) {
this.isChecked = ko.observable(isChecked);
}
And set the days property like this
this.days = [];
for (var i = 0; i< student.days.length; i++) {
this.days.push(new Day(student.days[i] == 1));
}
See working fiddle
Here's the scenario. I've built a grid with min. 1 to max. 132 columns with more than 10k rows. I am using knockout for data binding. The data is fetched by $.ajax function of jquery. On first call it fetches 100 rows and 100 rows on each scroll. If less than 100 it returns all the rows.
The browser does not freeze when ajax is call made but the browser freezes after ajax call. So once we've data at client side, it binds to the table. The browser freezes for a second or two while appending rows with data to the table.
function demoData(args){
var self = this;
self.dataList = ko.observableArray();
self.fillData = function () {
self.tableListPager = new ScrollPagerForAccordian($("#tblGrid"), 40, self.fillDetailData);
$.ajax({ //api call });
self.loadDetailData(data);
}
self.fillDetailData = function (pageNo) {
$.ajax({ //api call });
}
self.loadDetailData = function (res) {
self.dataList.push();
}
function ScrollPagerForAccordian(el, recHeight, callback) {
}
Even I tried to put self.loadDetailData(data) in web worker but still the browser freezes.
Update:
Is there any way to load next 100 rows in background and append it without freezing a browser on a scroll ???
Update-2
Please find below <tbody> structure:
<tbody data-bind="foreach: dataList">
<tr data-bind="foreach: rowValues()">
<td data-bind="style :{ width :colwidth + 'em' , 'min-width' :colwidth + 'em' , 'max-width' :colwidth + 'em' }">
<!-- ko if: displayFlag == true -->
<!-- ko if: flagData == false -->
rendering div
<!-- /ko -->
<!-- ko if: flagData == true -->
rendering another div
<!-- /ko -->
<!-- /ko -->
<!-- ko if: displayFlag == false -->
rendering another div
<!-- /ko -->
</td>
</tr>
</tbody>
How do I prevent browser freezing in such a case ???
I am using Chrome Version 38.0.2125.111 m and have to resolve this issue for chrome only as the view will be displayed in awsomium control.
I have been working on KnockoutJS since two weeks and I am trying to add inline editing in a grid using KnockOutJS and jQuery. My html:
<table>
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Excerpts</th>
<th>Content</th>
</tr>
</thead>
<tbody data-bind="foreach: Articles">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: Excerpts, event: { dblclick: $root.editField }"></td>
<td data-bind="text: Excerpts, event: { dblclick: $root.editField }"></td>
<td data-bind="text: Content, event: { dblclick: $root.editField }"></td>
</tr>
</tbody>
My JS:
function Articles(Articles) {
this.id = ko.observable(Articles.id);
this.Title = ko.observable(Articles.Title);
this.Excerpts = ko.observable(Articles.Excerpts);
this.Content = ko.observable(Articles.Content);
}
var ViewModel = {
Articles: ko.observableArray
([new Articles(id = 1, Title = "Title1", Excerpts = "Excerpts1", Content = "Content1")]),
loadArticles: function () {
var self = this;
self.Articles(Articles);
},
editField: function (d, e) {
var currentEle = $(e.target);
var value = $(e.target).html();
$(currentEle).html('<input class="thVal" type="text" value="' + value + '" />');
$(currentEle).find('input').focus();
$(currentEle).find('input').keyup(function (event) {
if (event.keyCode == 13) {
$(currentEle).html($(currentEle).find('input').val().trim());
//CallAjaxWithData('/MTB_Articles/EditArticle', 'POST', ko.toJSON(d), null, null); // To update data in server
}
});
$(document).click(function () {
if ($(currentEle).find('input').val() != null) {
$(currentEle).html($(currentEle).find('input').val().trim());
//CallAjaxWithData('/MTB_Articles/EditArticle', 'POST', ko.toJSON(d), null, null); // To update data in server
}
});
}
}
ko.applyBindings(ViewModel);
ViewModel.loadArticles();
Whenever the user double clicks on any td in the grid, I am adding an input field dynamically using the editField function and binding the updated value to the td again when user presses enter key or clicks somewhere else on the page. The parameter d in the editField function gives the current viewmodel object. I have to update the corresponding value in the parameter d when user edits the value in a particular column, convert d to json format and send it to server via ajax call to be updated in the database. The changes made by the user should be reflected in the view model( the parameter d). So how can we update the view model using dynamically added controls?
JSFiddle for this
You can do it in a more 'ko-ish' way that will make it easier for you.
KO is mostly declarative, and you're mixing declarative and procedural (jQuery) code.
To make it declarative, and much easier to implement, do the following:
add an editing observable property to your Articles. Initialize it to false
inside the <td>'s show either the text, or a data-bound input, depending on the value of the editing observable property
use the double click event, to set editing to true
use the enter key press to do what you need (ajax) with the values in your model, and set the editing to false again
You can do it like this:
<td>
<!-- ko ifnot: editing, text: Excerpts --><!-- /ko -->
<!-- ko if: editing -->
<input class="thVal" type="text" data-bind="value: Excerpts" />
<!--- /ko -->
</td>
Or even shorter:
<td>
<!-- ko ifnot: editing, text: Excerpts --><!-- /ko -->
<input class="thVal" type="text" data-bind="value: Excerpts, if: editing" />
</td>
I'm attempting to create a series of <ul> tags using the foreach: context. The goal is to iterate through the list, and start a new <ul> for every 4th item. My code so far is:
<ul data-bind="foreach: Areas">
<li><span>
<input type="checkbox" data-bind="value: AreaId, checked: $root.AreasImpacted" />
<label><span data-bind="text: Name"></span></label>
</span></li>
<!-- ko if: ($index() % 4 == 0) -->
</ul><ul>
<!-- /ko -->
</ul>
When I do this, I get the exception:
Microsoft JScript runtime error: Cannot find closing comment tag to
match: ko if: ($index() % 4 == 0)
It seems to not like the </li><li> content within the if comment block, probably because the DOM parser is scratching its head on how to actually parse this. If I change it to:
<!-- ko if: ($index() % 4 == 0) -->
<li>Fake!</li>
<!-- /ko -->
Then it'll work perfectly (that is, create a fake <li> every 4th element.
I'm open to other ideas of accomplishing this as well. Thanks!
Yeah, the initial DOM (before Knockout activates) is illegal, and Knockout doesn't work by pasting HTML into the DOM, it actually copies it into a javascript DOM object, which it inserts into the DOM. </ul><ul> isn't a legal object, so Knockout won't be able to turn it into a template. Even if it could, the foreach binding is on the original <ul>, not the new one started by the if, so the Knockout code that added items would still be operating on the first list.
So, in summation, Knockout's foreach and template bindings don't work by building HTML as if it's a string.
You will need a more complex solution.
Something like this would work, but I don't know if this is still what you are going for:
<!-- ko foreach: { data: chunkedList, as: 'areas' } -->
<span>SPLIT!</span>
<ul data-bind="foreach: areas">
<li><span data-bind="text: name"></span></li>
</ul>
<!-- /ko -->
var Viewmodel = function(data) {
var self = this;
self.items = ko.observableArray(data);
self.chunkedList = ko.computed(function() {
var result = [];
var chunk = [];
self.items().forEach(function(item, index) {
if (index % 4 === 0) {
chunk = [];
result.push(chunk);
};
chunk.push(item);
});
return result;
});
};
I'm using Knockoutjs for the first time and I'm having trouble debugging because of my inability to log variables in console. I can see that my JS is loading properly in console, when I enter:
Home.TwitterFeedComponent I see an object returned. How do I use console.log in conjunction with knockout and subscribe?
var Home = Home || {};
var inheriting = inheriting || {};
Home.TwitterFeedComponent = function(attributes) {
if (arguments[0] === inheriting)
return;
Home.OnScreenComponent.call(this, attributes);
var component = this;
var recent_tweets = ko.observableArray();
var url = 'https://twitter.com/search.json?callback=?';
this.attributes.twitter_user_handle.subscribe(function(value) {
var twitter_parameters = {
include_entities: true,
include_rts: true,
from: value,
q: value,
count: '3'
}
result = function getTweets(){
$.getJSON(url,twitter_parameters,
function(json) {
console.log(json)
});
}
console.log(twitter_parameters);
});
};
Home.TwitterFeedComponent.prototype = new Home.OnScreenComponent(inheriting);
Home.TwitterFeedComponent.prototype.constructor = Home.TwitterFeedComponent;
I don't see the problem in your code, but if you want to log 'Observables', you have to log it as follows:
console.log(observableVar());
I'm a little unclear as to the exact scope of the question -- however, if like mine, this question is directed toward the use of console.log within your HTML.
Here is a little set of code that may help:
<div class="tab-content" data-bind="with: ClientSearch.selectedClient">
...
<table class="table table-striped table-condensed table-hover">
<thead></thead>
<tbody>
<!-- ko foreach: { data: _general, as: 'item' } -->
<tr>
<td data-bind="text: eval( 'console.log(\' le item \', item)' )"></td>
</tr>
<!-- /ko -->
</tbody>
</table>
...
</div>
This code simply logs an item inside of a foreach to the console.
Hope this helps!