JavaScript: general questions about how to access/manipulate data

Hi there,

I am using DataAbstract for JavaScript and have some questions about manipulating data in data tables.
Up until now I just used the rows array of the data table.
When manipulating data I just accessed the fields inside a data row directly and afterwards applied the changes.
This has worked out well.

When I tried to add a new row I stumbled upon two functions: intAppendRow and appendRow.
Am I right in the assumption that functions starting with int* are just for internal use?

Now when using intAppendRow everything works fine, I set the properties directly and apply the changes back to the server.
Again, this works out well.

Yet, when using appendRow I have to use setFieldValue/getFieldValue to access the properties otherwise I cannot set their values.
I see that internally setFieldValue/getFieldValue access a row by fIndex which can be moved around with intGoToIndex/first/last/…
So it seems to me that the intended use is that you move the index to the appropriate row and then use setFieldValue/getFieldValue to access the row values.
Is that correct?

Is it bad to access the rows array and the data rows’ properties directly?
It makes things easier for me and I see it is also done in the ExtJS/KnockoutJS example.

Best regards,
Simon

1 Like

Hello, Simon!

No, it’s not bad, of course. Exposing rows’ properties was an attempt to provide more ‘javascript’ API instead of current delphi-like one. But it’s not solid yet and needs further ivestigation. At the moment the only way to construct proper row object is appendRow(or intAppendRow)

Hi Valeriy,

thanks for your response.
I have another problem when adding a new row.
Imagine I add a new row (with intAppendRow) and apply the updates to the server.
After that I change a property and call applyUpdate again.
Now I get the following error:

In this case I only changed the third value from null to “LocalDB”.
It seems like after having added the new row, the old values array was not updated correctly so the subsequent update is rejected because the database values differ from the old values array.

However, it works when I refresh the data table between the add and the change request but I think this should not be necessary right?

Best regards,
Simon

Hello, Simon!

Researching this issue I’ve faced once again that property setters should be more sophisticated. Unfortunately I can’t provide you with a workaround yet (which means I’m not closing the issue).

Also please note that using intAppendRow() is ok only for simple tables, without autoinc fields, default values, etc, so better use appendRow() in all cases.

Hi Valeriy,

I tried to use appendRow() but then I cannot access the properties directly and have to set them via DataTable’s setFieldValue.
If I access them directly I get an error of the following form when trying to apply the changes:

So it fails on the row’s sanity check.

It works though when using setFieldValue but this is kind of cumbersome for me as I’m using 2-way-databinding to the properties and I would have to move around with findId/next/prev etc. when selecting a different row in the interface.
Also what I don’t really understand is that the checkRequired method works on the fRecordBuffer property which is an array of values and is modified when using setFieldValue.
I didn’t really dive into the logic but shouldn’t the sanity checks be performed on the actual row objects instead of the fRecordBuffer array which seems to contain only the lastly added row.
From my current point of view this seems to make sense if you are allowed to add only one row at a time and save the changes back to the server immediately, yet I didn’t really check the whole logic in detail so maybe you can give me some details about that?

In any case (either if adding a row via appendRow/setFieldValue or via intAppendRow/directly accessing properties) a second update (after having saved the new row to the server) will always fail with the error stated in my previous post.
Currently, the only way I can work around that issue is to reload the whole table from the server again after having added new rows which of course is a lot of unnecessary work.

Best regards,
Simon

Weird. appendRow() calls intAppendRow() so properties should just work. Thanks for the hint, I’ll check it.

DataTable mimics Delphi datasets, so only current row can be edited at the moment and recor buffer is needed to implement table.cancel. I agree that checks should be changed to fit both delphi/like and array access to the table data and that’s what I’m working on. Hope it won’t take much time :smile:

Hi Valeriy,

that’s great, I appreciate your effort to fix the appendRow and sanity check issue!

I haven’t worked with Delphi datasets yet, so what do you exactly mean by “only current row can be edited at the moment”?
Should I always move the table index (e.g. with findId) to the appropriate row before editing it?

Can you already give me any information about the issue I posted in the second post?
(add row => save => edit same row => save fails)

Best regards,
Simon

Hello, Simon!

Delphi-style datasets have first/next/prev/last navigation methods and only current row can be changed at the moment. So typical table processing looks like

table.first();
while (!table.eof()) {
table.setFieldValue(“foo”, “bar)”;
table.next()
}

re your issue: the whole sanity checks thing is not solved properly yet, but I’ve tried your case and couldn’t reproduce it
this code works fine for me:

                    t.appendRow();
                    t.rows[t.rows.length - 1].ClientName = “test1”;
                    t.rows[t.rows.length - 1].ClientEmail = “test1”;
                    t.rows[t.rows.length - 1].ClientPassword = “test1”;
                    t.fRecordBuffer = []; //a hack to disable post() code, don’t use it :slight_smile:
                    adapter.applyUpdates(t, function() {
                        t.rows[t.rows.length - 1].ClientName = “test2”;
                        t.fRecordBuffer = []; //a hack again
                        adapter.applyUpdates(t, function() {alert(“update ok”);}, RemObjects.UTIL.showError);
                    }, RemObjects.UTIL.showError);

Any differences between this and your code?

Hi Valeriy,

ok thanks for the clarification! :smiley:

Regarding the add/edit problem:
The thing is that in this case remobject does not call the error callback but returns this as a result to the success callback.
Could you check the actual result in the success function or the actual state of your database after saving the edited row?
In my case the parameter in the success callback looks like that and reveals the error only after detailed inspection of the result object:

If you see the same behavior in your code, this is another thing I wanted to ask you:
It doesnt seem intuitive to me that the success callback is called in that case but until now I just worked around that by manually checking the result in my wrapper function and decide for myself if this is considered an error.
So if you have the same behavior, do you also consider this a bug?

Best regards,
Simon

Yes, it changed ClientName to ‘test2’ and saved it properly to DB.

Could you make reduced test case, maybe based on some Relativity sample?

Hi Valeriy,

sorry for the delay I was a little busy last week.

I prepared a small test case which pretty much represents my setup:

It is an asp.net project with a local database in the App_Data folder.
All you should have to do to get it up and running is pointing the connection string in Connections1.daConnections to the right location on your computer (sorry I couldn’t get it to work with a relative path).

You can find the js code in Scripts/custom/testcase.js
The problem seems to appear when the primary key column is not an AutoInc.
I added 3 tables in the database with different primary key columns (int, int with autoInc, string).
It works fine with the autoInc column but in the other two cases it fails.

When you run the website it should look like that:

When you click one of the buttons the script will first add a row with name set to “test1”, after that it will update the row by setting the name field to “test2”, and finally it will reload the table from database to present it below.
You should be able to reproduce my problem with the tables UsersInt and UsersString.

Best regards,
Simon

Thanks, Simon, the testcase is great!

I’ve reproduced the problem, but it needs more debugging. Getting back to you as soon as I have something.

Simon, please add this line to RemObjects.DataAbstract.RemoteDataAdapter.prototype.applyUpdates:

if (processedTable.findId(baseDelta.data[j].recid)) {
    processedTable.currentRow().state = RemObjects.DataAbstract.Enum.RowStates.rsUnchanged;
    processedTable.currentRow().__oldValues = []; // added

Hi Valeriy,
thanks for the quick fix, works like a charm! :slight_smile:
Have a nice weekend!