ExtJS TreeStore update event fire instead of create - extjs4

I am using tree.Panel with TreeStore when I call
sel_node.parentNode.appendChild(node);
tree.getSampleStore().sync();
ExtJS fire event called sample store proxy: update url instead of create url what could I have done wrong?

What exact version of ExtJS4 do you use?
In my situation, with ext-4.0.7-gpl, I debugged a bit a found out that appendChild method creates a node from and object and then performs some update operations concerning the node's position in the tree, like setting next sibling, parent etc., see [1].
When syncing, the store uses getUpdatedRecords and getNewRecords [2] methods to determine which operation to run. update or create. Somehow, our appended children turn out to be updated, not created.
Note that the method doesn't check whether the children of a parent node was loaded, just pushes the new node into an empty childNodes array; after all these operations end, other children of a parent node are never shown in the tree; and if the update operation caused the serverside generation of new id, the code breaks on the line original = me.tree.getNodeById(record.getId()); - there is no such node with the old id generated on client side..
Simply put, I think it's a bug.
[1] http://docs.sencha.com/ext-js/4-0/source/NodeInterface.html#Ext-data-NodeInterface-method-appendChild
[2] http://docs.sencha.com/ext-js/4-0/source/AbstractStore.html#Ext-data-AbstractStore-method-getUpdatedRecords
Add: ExtJS 4.1 beta 2 doesn't work better for me
Update with temp solution: I hacked a bit and think I solved the issue by overriding the appendChild method of NodeInterface like below (just to set phantom property so that the record becomes created, not updated).
Please note:
1) You should include your appendChild call in the NodeInterface expand method callback, or the bug with pushing to the empty childNodes will remain: the new node will appear somewhere in the wrong place;
2) I had to override updateIndexes of the AbstractView as well, try not to do this and maybe you'll find out why;
3) there are some issues when the store tries to delete our newly created node the next time it syncs - couldn't trace it yet;
0) I am no way ExtJS or even JS guru, so feel free to correct this hack)
Ext.data.NodeInterface.oldGpv = Ext.data.NodeInterface.getPrototypeBody;
Ext.data.NodeInterface.getPrototypeBody = function(){
var ret = Ext.data.NodeInterface.oldGpv.apply(this, arguments);
ret.appendChild = function(node, suppressEvents, suppressNodeUpdate) {
var me = this,
i, ln,
index,
oldParent,
ps;
if (Ext.isArray(node)) {
for (i = 0, ln = node.length; i < ln; i++) {
me.appendChild(node[i]);
}
} else {
node = me.createNode(node);
if (suppressEvents !== true && me.fireEvent("beforeappend", me, node) === false) {
return false;
}
index = me.childNodes.length;
oldParent = node.parentNode;
if (oldParent) {
if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index) === false) {
return false;
}
oldParent.removeChild(node, null, false, true);
}else{
node.phantom = true;
}
if(me.isLoaded()){
index = me.childNodes.length;
if (index === 0) {
me.setFirstChild(node);
}
me.childNodes.push(node);
node.parentNode = me;
node.nextSibling = null;
me.setLastChild(node);
ps = me.childNodes[index - 1];
if (ps) {
node.previousSibling = ps;
ps.nextSibling = node;
ps.updateInfo(suppressNodeUpdate);
} else {
node.previousSibling = null;
}
node.updateInfo(suppressNodeUpdate);
}
//console.log('appendChild was called');
// I don't know what this code mean even given the comment
// in ExtJS native source, commented out
// As soon as we append a child to this node, we are loaded
//if (!me.isLoaded()) {
// me.set('loaded', true);
//}
// If this node didnt have any childnodes before, update myself
//else
//if (me.childNodes.length === 1) {
// me.set('loaded', me.isLoaded());
//}
if (suppressEvents !== true) {
me.fireEvent("append", me, node, index);
if (oldParent) {
node.fireEvent("move", node, oldParent, me, index);
}
}
return node;
}
};
return ret;
};
this is my code to add a node by values taken from a form domainForm. The form opens by clicking an icon in an actioncolumn of our tree grid:
var node = grid.store.getAt(rowIndex);
node.expand(false, function(){
var newDomain = domainForm.getValues();
newDomain.parent = {id: node.raw.id}; // i don't know whether you'll need this
var newNode = node.appendChild(newDomain);
me.store.sync();
});
and updateIndexes overrider:
Ext.override(Ext.view.AbstractView, {
updateIndexes : function(startIndex, endIndex) {
var ns = this.all.elements,
records = this.store.getRange(),
i;
startIndex = startIndex || 0;
endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length < records.length?(ns.length - 1):records.length-1) );
for(i = startIndex; i <= endIndex; i++){
ns[i].viewIndex = i;
ns[i].viewRecordId = records[i].internalId;
if (!ns[i].boundView) {
ns[i].boundView = this.id;
}
}
}
});

Had the same issue, an update to ext-4.1.0-beta-2 fixed it.

The reason might be wrong format of data that comes from the server in response to your request.
Syncronization doesn't happen. Pass 'success' key with the value of TRUE in the server response.

Hmm ... try this ...
beforeitemexpand(node, eOpts){
if (node.data.expanded) return false
}

Related

Invalid Read of Size 4?

I'm running valgrind on a program, and while the program executes just fine, valgrind reports this:
==6542== Invalid read of size 4
==6542== at 0x8049C6F: Table::removeWebsite(Table&) (Table.cpp:146)
==6542== by 0x8049768: main (app.cpp:140)
==6542== Address 0x43f87d4 is 4 bytes inside a block of size 8 free'd
==6542== at 0x402B528: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-x86-linux.so)
==6542== by 0x8049BB0: Table::removeWebsite(Table&) (Table.cpp:124)
==6542== by 0x8049768: main (app.cpp:140)
I'm not entirely sure what is wrong. Here is the code that valgrind is pointing to...
bool Table::removeWebsite(Table& parm)
{
bool flag = false; //flag to show if an item is removed or not
int OriTableSize = currCapacity;
for (int index = 0; index < OriTableSize; index++) //search through array list, starting at index
{
Node *current = parm.aTable[index];
Node *prev = nullptr;
while (current != nullptr) //search through linked lists at array[index]
{
if (current->data->getRating() == 1) //search ratings for 1
{
if (prev == nullptr) //if current is the start of the list
{
if (current->next == nullptr) //if current is the only item in this list
{
delete current;
size--;
parm.aTable[index] = nullptr;
flag = true;
}
else
{
parm.aTable[index] = current->next; //point to the next item in list
delete current;
size--;
flag = true;
}
}
else //reset prev->next pointer to skip current
{
prev->next = current->next;
delete current;
size--;
flag = true;
}
}
prev = current;
current = current->next; //go to next position in linked list
}
}
if (flag == true)//at least one item was removed
return true;
else
return false;
}
That "delete current" points to a Node, which has:
struct Node
{
Node(const SavedWebsites& parm)
{
data = new SavedWebsites(parm);
next = nullptr;
};
~Node()
{
delete data;
next = nullptr;
}
SavedWebsites *data;
Node *next;
};
and the data in SavedWebsites has the destructor:
//Default Destructor
SavedWebsites::~SavedWebsites()
{
if (this->topic)
{
delete [] this->topic;
this->topic = nullptr;
}
if (this->website)
{
delete [] this->website;
this->website = nullptr;
}
if (this->summary)
{
delete [] this->summary;
this->summary = nullptr;
}
if (this->review)
{
delete [] this->review;
this->review = nullptr;
}
rating = 0;
}
*note the items "topic" "website" "summary" and "review" are all created with
... = new char[strlen(topic)+1] //(We have to use Cstrings)
I'm fairly new to all of this, so any ideas of what may be causing this valgrind invalid read?
You did not indicate which line corresponds to Table.cpp:146, but based on the Valgrind output, it looks like the allocation in question represents a Node instance because the size of 8 bytes matches (assuming a 32-bit system). The invalid read is triggered by the offset 4 of that instance and a size of 4, so this must correspond to the member next.
Table.cpp:146:
current = current->next; //go to next position in linked list
In case of a delete in the iteration, you access the deleted node current afterwards. You also initialize prev based on the invalid pointer, so the next pointer of the last valid node will not be updated correctly in case of deletes.
Your code seems to work fine (but is not) because its behavior depends on the allocator in use and other allocations. If the allocator were to fill the freed memory with a pattern or if that memory was reused immediately and written to, you'd see a segmentation fault.
To address this issue, I'd defer the delete. Before the next iteration, you can then either read the next node and delete the original current or update prev and read the next node.
bool del = false;
if (current->data->getRating() == 1) //search ratings for 1
{
if (prev == nullptr) //if current is the start of the list
{
if (current->next == nullptr) //if current is the only item in this list
{
parm.aTable[index] = nullptr;
}
else
{
parm.aTable[index] = current->next; //point to the next item in list
}
del = true;
flag = true;
}
else //reset prev->next pointer to skip current
{
prev->next = current->next;
del = true
flag = true;
}
}
if (del)
{
Node *deletenode = current;
current = current->next;
delete deletenode;
size--;
}
else
{
prev = current;
current = current->next; //go to next position in linked list
}

Vue2 using $set does not fix change detection caveat when using push() on object

Following on from this issue: Strange issue with Vue2 child component object v-for list render
I have a function which loops through an array products, which takes certain values and adds them to an object called attributes. I need to use this.$set to update my attributes object to make sure that Vue can detect the update.
My functions look like this:
//// checks if the value exists in the object to avoid adding duplicates
doesItExist: function(key, value) {
for (let i = 0; i < this.attributes[key].length; i++) {
if (this.attributes[key][i] == value) return true;
}
return false;
},
//// if the value does not exist, then add it to the object
pushIfNotExists: function(key, value) {
if (!this.doesItExist(key, value)) { // returns true/false
this.$set(this.attributes[key], key, this.attributes[key].push(value));
}
},
//// main function looping through the original products array to extract attributes
createAttributes: function() {
for (let i = 0; i < this.products.length; i++) {
for (let j = 0; j < this.products[i]['attributes_list'].length; j++) {
let attributeName = this.products[i]['attributes_list'][j]['attribute_name'];
if (!this.attributes[attributeName]) {
this.attributes[attributeName] = new Array();
};
this.pushIfNotExists(attributeName, this.products[i]['attributes_list'][j]['value']);
}
}
console.log(this.attributes); // outputs expected object
},
The problem I have is that in my child component, the attribute data is still not being rendered, which means this must not be working correctly (even though my console log shows the data is there).
Any ideas?
Thanks

why qml listmodel sync failed when in the worker thread,and how to solve it?

how can i change the text of the button without clicked in the listview delegate?
when i solved the question above,there is a new question.i can't use the worker to sync the model,but when i use sync not in the worker,although it has some error occured from qtcreator,but it works to sync the data role in the model ,and often, sync will fail.
dataloader.js:
WorkerScript.onMessage = function(msg) {
msg.model.sync();
}
PrinterList.qml:
WorkerScript{
id:worker
source:"dataloader.js"
}
function cmpPname(prnName)
{
console.log("cmpPname");
var tmpName = prnName.replace(/_/g," ");
console.log("tmpName=",tmpName);
var pname = new Array;
pname= Jsclient.g_str.split(',');
tmpName = tmpName.split(',');
console.log("pModel.count=",pModel.count);
for(var i = 0;i < pname.length;i++){
if(tmpName == pname[i]){
console.log("pname[%1]=".arg(i),pname[i]);
Jsclient.pstate = "Added";
pModel.setProperty(i,"prstate",qsTr(Jsclient.pstate));
// pModel.get(2).prstate = qsTr(Jsclient.pstate);
sync(); **//this works!!but often failed too.**
}else{
Jsclient.pstate = "Add";
pModel.setProperty(i,"prstate",qsTr(Jsclient.pstate));
}
worker.sendMessage({"model":pModel}); **//this failed,why?**
}
}
finally,i solved the question.
dataloader.js:
WorkerScript.onMessage = function(msg) {
console.log(msg.index,msg.prstate);
msg.model.setProperty(msg.index,"prstate",msg.prstate);
msg.model.sync();
}
list.qml:
worker.sendMessage({"index":i,"prstate":qsTr("Added"),"model":pModel});

Comment Line Dissapears After Rewriting a Node

I was writing simple refactoring and noticed a strange thing. The comment line before the node I am rewriting disappears after refactoring. Also comments after the node in question are transferred inside the node and break the indentation in the new place. This is very strange and I want to ask if it is a bug in jdt or I did something wrong and oblivious.
For example my code suppose to refactor if-else statements in a way that the shortest branch would appear first.
when I try to refactor this:
// pre
if(a==6) {
a = 5;
return false;
} else {
a++;
}
//post
I get this:
if (!(a==6)) {
a++;
}
//post
else {
a = 5;
return false;
}
The relevant snippet where the refactoring is done:
protected ASTRewrite createRewrite(CompilationUnit cu, SubProgressMonitor pm) {
pm.beginTask("Creating rewrite operation...", 1);
final AST ast = cu.getAST();
final ASTRewrite rewrite = ASTRewrite.create(ast);
cu.accept(new ASTVisitor() {
public boolean visit(IfStatement node) {
if (node.getStartPosition() > selection.getOffset() + selection.getLength() || node.getStartPosition() < selection.getOffset())
return true;
if (node.getElseStatement() == null)
return true;
int thenCount = countNodes(node.getThenStatement());
int elseCount = countNodes(node.getElseStatement());
if(thenCount <= elseCount)
return true;
IfStatement newnode = ast.newIfStatement();
PrefixExpression neg = negateExpression(ast, rewrite, node.getExpression());
newnode.setExpression(neg);
newnode.setThenStatement((org.eclipse.jdt.core.dom.Statement) rewrite.createMoveTarget(node.getElseStatement()));
newnode.setElseStatement((org.eclipse.jdt.core.dom.Statement) rewrite.createMoveTarget(node.getThenStatement()));
rewrite.replace(node, newnode, null);
return true;
}
});
pm.done();
return rewrite;
}
The // pre comment goes away because the parser considers it to be part of the next statement (represented by node), which you replace with newNode. When node goes away, so does the attached comment.
still thinking about why the // post ends up where it does... Try replacing the newNode before setting its then and else statements

Dojo scope question for store.query with ItemFileWriteStore

I am writing a method to grab the lowest unique id in an ItemFileWriteStore. I have a boolean to tell me when I reach my condition, but I can't get the scope correct.
The function call works as I expect, apart from when newIdOkay is set to true, it is not recognised in the while loop.
Please can you explain why and what I have to do to get this right?
Many thanks
Here is my code:
function checkNewId( size ) {
if( size == 0 ) {
console.log('found new ID!');
newIdOkay = true;
}
}
function addContentItem( store ) {
// New ID
var newIdOkay = false;
var newId = 0;
while( newIdOkay == false && newId < 8 ) {
newId++;
store.fetch({ query: {id:newId}, onBegin: dojo.hitch(this, "checkNewId"),
start:0, count:0, sync:true });
}
}
To understand the reason, you need to have basic understanding of identifier resolution and scope chain in JavaScript. I recommend you to read this article: http://jibbering.com/faq/notes/closures/
Regarding you question, in the function addContentItem, the check in the while loop is tested against the identifier newIdOkay which is in the of the activation object corresponding to this function execution context. In the function checkNewId, the identifier newIdOkay you set is in the global object. So these two are not the same one. An easy fix is to move the newIdOkay in addContentItem function to the global scope.
var newIdOkay = false;
function checkNewId( size ) {
if( size == 0 ) {
console.log('found new ID!');
newIdOkay = true;
}
}
function addContentItem( store ) {
// New ID
var newId = 0;
while( newIdOkay == false && newId < 8 ) {
newId++;
store.fetch({ query: {id:newId}, onBegin: dojo.hitch(this, "checkNewId"),
start:0, count:0, sync:true });
}
}