How to bind Knockout.js to existing table grid? - javascript

I'm a newbie to Knockout.js. I implemented the Knockout.js by loading data from ajax source and use foreach loop to create a table of the data. The tutorial i followed is here
http://www.dotnetcurry.com/ShowArticle.aspx?ID=933
My issue here is, due to the nature of my application, I find that the first load is better served from the server side using a grid component and I only want Knockout.js to take care of "Add" row, "Update" a row and "delete" a row.
My question is,
1) how do I replace the "first" load and populate the lookupCollection :ko.observableArray() in the article with the default data in the html table?
2) Related to #1. If the first load, the table layout with data is constructed from the server side, then how do I bind "foreach" to the grid so "add" can be performed on lookupCollection?
Thanks and again, I'm a newbie, I must be missing some key concepts here.

One way would be to pass your initial data into your view model. Since you are using asp.net it would look something like this:
//Dump raw data into javascript variable
var data = #Html.Raw(ViewBag.Data);
function ViewModel(data) {
var self = this;
//Unpack raw data
self.lookupCollection = ko.observableArray(data.lookupCollection);
}
//initialize view model
var viewModel = new ViewModel(data);
ko.applyBindings(viewModel);

Related

oData binding to table generates repeated lines in table output

I have an OData connection using which I read an entityset with conditions that returns me data in the following structure. The data being in the root node itself rather than in an object array-
oData Structure
I am then setting this data to a JSON Model and binding this model to my oTable.
The code in controller looks something like below-
//Get data for the table
var oTabData = sap.ui.getCore().byId("MyTable");
var sServiceUrl = "http://example.com/sap/opu/odata/SAP/ZXX_SRV";
var oModel2 = new sap.ui.model.odata.ODataModel(sServiceUrl,true);
var oJsonModel = new sap.ui.model.json.JSONModel();
oModel2.read("/MyEntitySet(Myvar1='000001',Myvar2='abc')?
$format=json",null,null,true,function(oData,response){
oJsonModel.setData(oData);
});
oTabData.setModel(oJsonModel);
The view contains the table and the binding is as below. Have ignored rest of table declaration code to keep it short. Table declared as oTable with id MyTable and with all the right columns-
//Template to map the data to the respective column
var template = new sap.m.ColumnListItem({
id:"table_template",
type: "Navigation",
visible: true,
cells:[
new sap.m.Label({
text: "{/Myvar1}"
}),
new sap.m.Label({
text: "{/Myvar2}"
}),
new sap.m.Label({
text: "{/Myvar3}"
}),
new sap.m.Label({
text: "{/Myvar4}"
})
]
});
var oFilters = null;
oTable.bindItems("/",template,null,oFilters);
The issues are-
As I have made a service call with conditions the oData output
received does not output an array of data like it does when we make
a call without conditions
(oModel2.read("/MyEntitySet?$format=json",null,null,true,function(oData,response)).
In the latter case the output comes under results array for which
the binding assignment is /results and that works right as expected.
In current case (service call with conditions), as the columns in
output are available right in the root level, have used binding /
and in template as well using / in front of every element (e.g.
{/Myvar1} as seen above). But using this technique it also creates 3
default blank lines in table initially. When the above controller
code is executed, it fills the table 3 x 4 times i.e. 12 rows for a
single row output.
Due to organization restrictions I cannot put the original code here. But this is how the scenario is. Any help would be appreciated.
Thanks.
PS: Haven't used any loop that would generate the repeated lines.
You can bind your table directly to the ODataModel:
Steps:
Before doing binding, you need to create a template for the table rows, in sap.m.Table it is ColumnListItem control, I would suggest to carry it out to the XML fragment. Try to use XML for markup as much as possible, becasue it much clearer and maintainablier than js coding;
All the controls in "cells" should be bound to the rows' properties using relative paths.
Tell the View about your model by calling "oView.setModel(MODEL_INSTANCE)", by this line you set the default model to the view. From now on all the controls inside are able to see it. The model is default because the name of it was not specified.
oTable.bindItems("/MyEntitySet", { template: oColumnListItem, filters: aFilters}); - this will automatically trigger an OData call to grab all needed data. The table will be updated automatically. You should see the rows.

I'm getting a "newItem() was not passed an identity for the new item" error while trying to add a new item to a JSON store

I've seen other posts in this site regarding the same issue and I've tried the solutions given. I've also visited the links that may offer a solution but I'm still stuck with the same error.
I'm using DOJO and something as simple as this won't even work
myStore.newItem({id: 'test', otherfield: 'otherinfohere'});
myStore.save();
Supposedly the "newItem() was not passed an identity for the new item" error appears when you haven't provided an identifier for the new item, which i have.
The whole purpose of this (Just in case anyone can provide a good idea or has done something similar before) is that i want to create a data grid that shows info from a particular store. The problem is, that in that store all the items may not have the same structure. For instance:
I may have a store that looks like this
{identifier: 'id',
label: 'name',
items: [
{ id:'1', name:'Ecuador', capital:'Quito' },
{ id:'2', name:'Egypt', capital:'Cairo' },
{ id:'3', name:'El Salvador', capital:'San Salvador' , additionalField: 'otherinfohere'},
{ abbr:'gq', name:'Equatorial Guinea', capital:'Malabo', additionalField: 'otherinfohere'},
]}
This is possible because I'm the one constructing the store in a Spring Controller (I'm also using the Spring Framework) from information I have locally stored in a Berkeley DB. So what i need is a data grid with a dynamic layout because I don't want blank spaces to show in the view in the rows with lesser amount of fields, and i need to show all the info in the store at the same time, but i don't know how to do this.
I thought of doing it by creating a simple layout of only 1 field. In it I would load data from a store i create dynamically at runtime. The data in the store would be composed of HTML combined with the values coming from the original store so I could obtain something like this, which is inside an attribute of a JavaScript Object and let the browser parse it for me:
<div><span>id: originalID </span>....</div>
This of course is a simple example, the html layout i'm looking for is far more complicated, but i think that passing it as a string to an object might do the trick.
The problem is that i don't even know if that idea will work because i get that error whenever i try to add values to my secondary store.
rdb.modules.monitor.historicStore.fetch({onComplete: function(items, request){
for (var i = 0; i < items.length; i++){
var item = items[i];
var obj = new Object();
obj.id = rdb.modules.monitor.historicStore.getValue(item, "id");;
var html = "<div><span>";
html += rdb.modules.monitor.historicStore.getValue(item, "sql");
html += "</span></div>";
obj.html = html;
myStore.store.newItem(obj);
}
}});
In this context "historicStore" refers to the JSON store that has the values that i need to convert and add to "myStore" after i added some HTML.
I hope you got the main idea of what I'm trying to do. If anyone can help me we either of these problems i would really appreciate it. Thanks in advance
For the issue regarding store:-
"id" is mandatory for a store, if it is going to be used for a grid(datagrid, EnhancedGrid, etc. whatever). The items are handled only on basis of "id" attribute by the grid data structures.
Usually, id can be a loop variable/ auto incrementation, to avoid any cases like you have said. Before adding the store to the grid, ensure that all items have the id attribute. You can write a function which will loop through each item and check for this, else add an auto-incrementing value for the id attribute of that item.

KnockoutJS - Using data from a table in a form

I'm relatively new to knockout and I've been researching my topic for hours to no avail, so I finally find myself actually asking a question on SO that I don't think has been asked.
Anyway, I'm coding a basic one-page app that pulls data from the server via AJAX (done), displays a table of those records (working, but perhaps not done), and enables the user to edit the records from the table in a form on a jQueryUI dialog, preferably without loading more data from the server, as all needed data is currently in the table (this is where I'm messed up,) and then ultimately post just the one record back to the server. As a bonus, I'm hoping to make the form update the content in the table too so that it doesn't need to be reloaded.
My basic viewmodel. I also have some binding code for sorting the columns by clicking on the headers that I've left out as it's irrelevant to the question
function AppViewModel() {
var self = this;
self.records = ko.observableArray(ajaxRecords); //ajaxRecords is a JSON object from the server
/*
THIS SECTION MOVED BELOW FOR EXPLANATION
*/
}
I've been able to get data into the form by using a new viewModel that's passed the object from a click function bind on each row:
self.switchDataToForm = function(clickedItem) {
ko.applyBindings(new FormViewModel(clickedItem), document.getElementById('detailsPage'));
}
But I just feel like that's a bad way of doing things and this should all somehow be achievable through one ViewModel.
I'm interested to see what people say about this. It may be incredibly obvious for some, but I'm just having a hard time wrapping my head around the knockout way of thinking. If this were a jQuery task, it would have been done hours ago, but with quite a bit more markup. I'm really excited about what Knockout JS has to offer the JS dev scene and I want to start using it in practice.
You could try something like this:
function AppViewModel() {
var self = this;
self.records = ko.observableArray(ajaxRecords); //ajaxRecords is a JSON array from the server
self.form = ko.observable();
self.switchDataToForm = function(clickedItem) {
self.form(new FormViewModel(clickedItem));
}
/* another data goes here */
}
function FormViewModel(item) {
var self = this;
self.someProp = ko.observable(item.someProperty);
/* another observables goes here */
}
And html:
<form id="detailsPage" data-bind="with: form">
<input type="text" data-bind="value: someProp" />
...
</form>
You can see how it works in this fiddle: http://jsfiddle.net/Ivan_Srb/MKB8W/2/

Databinding to Windows 8 ListView

I'm having massive problems with databinding to a ListView in a Windows 8 app using Javascript.
Inside the "activated" event on default.js I have written some code to get some data from a web service and push it into an array. This bit works OK and the array is populated.
The problem I have is that the app won't recognise the data. I have this code in a page called inspections.html:
data-win-options="{itemTemplate: select('#imageTextListCollectionTemplate'),
itemDataSource: dataList.dataSource,
layout: {type: WinJS.UI.ListLayout}}
and then in the "activated" event I declare:
var dataList = new Array();
and push the data from the web service into this array. But at runtime I get an error that says something along the lines of "can't find dataSource on undefined dataList".
I've done some of the examples on the MS website and in one of them it creates a dummy dataset and references it from a namespace. I kinda think that what I'm missing here is a namespace too but I don't know what the namespace for default.js is. Or maybe I'm wrong and it's something totally different.
Please help - this is so fundamental (and should be easy) but I can't get my head around it.
Do you want to create datalist in HTML or javascript?
It seems you want to create it from JavaScript. Assuming that you have already pushed your data into array from your webservice, you only need to call:
var dataList = new WinJS.Binding.List(array);
now accessing dataList.dataSource is perfectly valid.
Also, to create the datalist you don't always need an array. You could probably start with an empty list and then keep inserting data directly into the data list from web services, like:
var dataList = new WinJS.Binding.List([]);
dataList.push(value1);
dataList.push(value2);
...
Hope it helps. Let me know if you have any more questions.
If you are getting troubled by assigning datasource in HTML side
Prefer js side like
var list = new WinJS.Binding.List(array here);
listView.itemDataSource = list.dataSource;
by using this you can easily go through the data which you are binding to ListView.

Get full data set, sorted with YUI Data Table with Pagination

I hope I am describing my issue enough.. here goes:
I have a YUI data table, get a server side set of records via JSON, and then populates the data.
Users can click on the headers to sort the data in three of the 6 columns, (which are using a custom sort function for each column). The sorting is done client-side.
When a user sorts the data, I need to be able to get a complete list of the values from one of the columns being shown. I need all the data available, not just what's rendered to the page. The data hidden via pagination must be included.
Any ideas? I've tried the handleDataReturnPayload and doBeforeLoadData methods of the DataTable but both give the original, unsorted data.
I'm really stuck here and I've got a client depending on a feature that depends on me getting this sorted list.
Thanks in advance.
Satyam, over at the YUI Forums answered my question perfectly.
The data is actually stored in the
RecordSet. At any time you can go and
look at it, and it will be sorted as
shown on the screen, but it will have
all the data, whether shown or not.
Method getRecordset() will give you a
reference to it and then you can loop
through its records.
You can listen to the columnSortEvent
to be notified a sort has occurred.
I just subscribed to the columnSortEvent event and looped through the array returned by datatable.getRecordSet().getRecords().
I'd recommend posting this to the YUI forums -- http://yuilibrary.com/forum/ -- that's a great place to get support on DataTable issues.
I stumbled upon this question looking for information on how to retrieve information from a dataset that was NOT displayed in the datatable. IF you place the hidden data in the datasource before any field you wish to be displayed, it will be rendered blank, but if you place it after your last field that will be rendered (as defined by the columns object), then they will not render but still be accessible through the record).
var columns = [
{key:"Whatchamacallits", children:[
{key:"name" },
{key:"price" }
]}
];
var ds = new YAHOO.util.DataSource('index.php...');
oDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
oDataSource.responseSchema = {
fields:["name","price","id"]
};
var dt = new YAHOO.widget.DataTable("dt-id", columns, ds, {});
dt.subscribe("rowClickEvent", dt.onEventSelectRow);
dt.subscribe("rowSelectEvent", function(p){ alert(p.getData('id'); });

Categories

Resources