PDA

View Full Version : UpdateManager and table rows



enrico.triolo
10 Jan 2007, 3:24 AM
Hi, I'm in the process of switching my web application from prototype to yui, and as you can guess I found yui-ext to be very interesting and useful, not to mention the fact that it has an UpdateManager similar to prototype's Ajax.Updater.

So I started converting my first page: it has a table whose rows can be edited through ajax calls.
In the html I have something similar to this:

<table>
<tbody>
<tr id="editRow">
<td><input type="button" name="myBtn" onclick="YAHOO.ext.UpdateManager.updateElement('editRow', 'myServerSideCallback');"></td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>

when I press the 'myBtn' button the ajax call is performed and I correctly get the row in editing state (the server returns an html fragment with only the tds). The only problem I have is that the html fragment returned by the server is inserted in the row as if it was 'indented'; it seems like an empty td is inserted before everything else.

Am I doing something wrong?

Cheers,
Enrico

Animal
10 Jan 2007, 3:32 AM
When you say "it semes like", is it actually doing that? have you tried to debug it?

enrico.triolo
10 Jan 2007, 6:22 AM
Yes, I tried debugging it and noticed that actually it doesn't add an empty td, but the browser renders it as if it was an empty cell.
I'm using firefox 2.0 on Linux, but tested even with firefox 1.5.

Animal
10 Jan 2007, 7:08 AM
So what is the string that's passed back and dumped into the innerHTML of editRow?

enrico.triolo
10 Jan 2007, 7:34 AM
I just find out that disabling both loadScripts and showLoadIndicator everything works correctly...

Anyway the html fragment returned by the server defines the tds. Specifically I get something like this:


<td style="width:30px;">
<input type="image" name="save" id="save" value="save" class="typeImage" alt="save" title="save" src="img/save.gif" onclick="YAHOO.ext.UpdateManager.updateElement('editRow', 'http://.../save');return false;" />
<input type="image" name="cancel" id="cancel" value="cancel" class="typeImage" alt="cancel" title="cancel" src="img/cancel.gif" onclick="YAHOO.ext.UpdateManager.updateElement('editRow', 'http://.../cancel');return false;" />
<input type="hidden" name="idSection" id="idSection" value="1562736445" />
</td>
<td>
<input type="text" name="name" class=" typeText" id="name" size="20" maxlength="255" value="backend" />
</td>
<td>
<input type="text" name="authPage" class=" typeText" id="authPage" size="20" maxlength="255" value="Authenticate" />
</td>
<td>
<input type="text" name="homePage" class=" typeText" id="homePage" size="20" maxlength="255" value="" />
</td>
<td>
<input type="text" name="header" class=" typeText" id="header" size="20" maxlength="255" value="header" />
</td>
<td>
<input type="text" name="footer" class=" typeText" id="footer" size="20" maxlength="255" value="footer" />
</td>
<td>
<input type="text" name="css" class=" typeText" id="css" size="20" maxlength="255" value="style_new.css" />
</td>
<td>
<select name="layout" id="layout" >
<option value="" selected="selected" >---choose---</option>
<option value="StandardPageLayout" >StandardPageLayout</option>
<option value="TabbedModulesPageLayout" >TabbedModulesPageLayout</option>
</select>
</td>

Animal
10 Jan 2007, 8:44 AM
Diabling scripts shouldn't make a difference - ther're disabled by default unless you've enabled them globally. And you won't be loading scripts inside a TR, so yes, that might as well be disabled.

I suspect the show loading indicator. The UPateManager just chucks the indicator into the element you're updating. That won't be valid in a TR - only TDs are allowed there, so that might throw the browser off.

I think the .40 UpdateManager handles it differently.

enrico.triolo
11 Jan 2007, 2:17 AM
Thank you for your assistance... I tried the 0.40 version but I have the same problem.
I managed to get it work with loadScripts turned on: the problem was that the Element.update method appends and empty span at the end of the html fragment and, as you suggested, the browser messes up the layout because in a tr no span are allowed. I resolved this problem by simply modifying the span with a
style="display:none" attribute. Maybe this could be merged in the svn trunk...
The problem with the loading indicator is exactly the same, except for the fact that I can't obviously set it as hidden... I will try to modify the code so that it can detect if the load indicator is added to a table row and in that case it will use a td instead of a div.

Good news you may think... But now I have another problem! In fact trying it with explorer nothing happens at all... When I click on the button it looks like no ajax operation is performed, but debugging the Element class I can see it is actually accomplished.
A fast search on the internet lead me to this url: http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/innerhtml.asp
"The property is read/write for all objects except the following, for which it is read-only: COL, COLGROUP, FRAMESET, HTML, STYLE, TABLE, TBODY, TFOOT, THEAD, TITLE, TR."

So, it seems that innerHTML can't be used for tr elements...
I'm so discouraged... :cry:

Animal
11 Jan 2007, 6:03 AM
That's a bug - the UpdateManager leaving that generated span there! It's there to have something to hang the onAvailable call on so that it can wait for the content to be rendered.

It should remove it.

As far as IE not handling innerHTML in tables. That's a bugger. What you will have to do is write a custom renderer which calls the DOM methods to update it how you want. Set that renderer as the renderer for the R. It will have to look at the response text and "do the right thing". Perhaps your backend could produce JSON for the renderer which would be easy to run through to update the table?

http://www.yui-ext.com/playpen/yui-ext.0.40/docs/output/YAHOO.ext.UpdateManager.html#setRenderer

enrico.triolo
11 Jan 2007, 6:36 AM
Thank you, I will give the renderer option a try...
But I just come up with a solution: instead of using innerHTML inside update method, I'm using DomHelper.insertHtml. That method in fact correctly detects if the user agent is explorer and if we are working inside a table, and performs the needed operations.
Nevertheless I think I discovered a little bug inside DomHelper.insertIntoTable: let's say the server sends back, as in my case, an html fragment containing many tds. The insertIntoTable method performs this operations at a certain point:


tempTableEl.innerHTML = '<table><tbody><tr>'+html+'</tr></tbody></table>';
node = tempTableEl.firstChild.firstChild.firstChild.firstChild;

this way 'node' will refer to the first td. Afterwards, depending on the 'where' parameter, an operation like el.insertBefore(node, el.firstChild); is executed, and the method exits.
But in this way only the first td will be added to the row...
I made a fast hack, just to see if it would work correctly (and the answer is yes!):


if(tag == 'table' || tag == 'tbody'){
tempTableEl.innerHTML = '<table><tbody>'+html+'</tbody></table>';
node = tempTableEl.firstChild.firstChild; //node refers to the tbody element
}else{
tempTableEl.innerHTML = '<table><tbody><tr>'+html+'</tr></tbody></table>';
node = tempTableEl.firstChild.firstChild.firstChild; //node refers to the tr element
}
for(i = 0; i < node.childNodes.length; i++) {
var curNode = node.childNodes[i].cloneNode(true);
if(where == 'beforebegin'){
el.parentNode.insertBefore(curNode, el);
}else if(where == 'afterbegin'){
el.insertBefore(curNode, el.firstChild);
}else if(where == 'beforeend'){
el.appendChild(curNode);
}else if(where == 'afterend'){
el.parentNode.insertBefore(curNode, el.nextSibling);
}
}
return node;


Actually I made another little modification, since I need to overwrite the content of the tr with the one returned by the server and the DomHelper.insertHtml method doesn't support 'overwrite' (or something similar) as the 'where' parameter, so I handled it. If you want I can post this patch too.

Cheers,
Enrico

Animal
11 Jan 2007, 9:24 AM
So you have written a little renderer which uses DomHelper instead of innerHTML, and set that renderer in the UpdateManager? Good solution.