Rendering dynamic Enyo Repeaters - javascript

I need to make a data table for an enyo project I am working on that will ultimately display the result of an Ajax call.
This (Blatantly stolen from ryanjduffy here)seems to be a good starting point, but when I try to call setData() from a button event (rather than in the constructor) as I have here I get the following error:
InvalidCharacterError: String contains an invalid character # http://enyojs.com/enyo-2.1/enyo/source/dom/Control.js:681
I looked at the Control.js code and it seems that it tries to create a new node, but the this.tag property is set to null and things break.
I feel like I am missing something really simple, but I just can't see the problem yet...
Can someone tell me what I am doing wrong?
Thanks!
EDIT 1:
Apparently calling render() is not needed. Here is the original working version with render() commented out. Everything looks great. However, if I try to remove render() from the version that requires a button click, the repeater starts creating div's above the table instead of tr td's inside the table...
EDIT 2:
Basically, from what I can tell, the Repeater inside of a table will lose it's parent once the table is rendered (or something like that). The result is the Repeater will start rendering its new items outside of the original table and because a td tag without a table makes no sense, it just renders a div.
The solution I have come up with is to give the Repeater itself a table tag so its children always wind up in the right spot. This adds the challenge of needing to recreate the header line each time, but it is not that big of a deal. I have a working example if anyone is interested.

I'm sure you're not looking for a solution any longer but since I was mentioned in the post, I thought I'd let you know what I figured out. In short, my code shouldn't have worked but since browsers are forgiving, it did ... sort of.
When the code run at create time, it renders something like this:
<table>
<tr> <!-- header row --> </tr>
<div> <!-- repeater tag -->
<tr> <!-- repeater row --> </tr>
</div>
</table>
The browser looks at that and says, "Hey, dummy! No <div>s in a <table>" and kicks it out but leaves the <tr>s.
In your example, since you're delaying the render of the rows, Enyo renders:
<table>
<tr> <!-- header row --> </tr>
<div></div>
</table>
And the browser ejects the <div> and you're left with an empty table. When you later set the data, those rows are rendered into the div. Unfortunately, since you're rendering <tr> and <td>, those aren't valid outside a table so you just get text.
I found a couple solutions. The simplest was to set the tag of the Repeater to be TBODY which is allowed inside a table. The slightly more involved solution was to make the DataTable inherit from Repeater and set the header row to be chrome so they're not removed when updating the data.
Option #2 Fiddle
enyo.kind({
name:"DataTable",
tag: "table",
kind: "Repeater",
published:{
map:0,
data:0
},
handlers: {
onSetupItem: "setupItem"
},
components:[
{name:"row", kind:"DataRow"}
],
create:function() {
this.inherited(arguments);
this.mapChanged = this.dataChanged = enyo.bind(this, "refresh");
this.refresh();
},
refresh:function() {
if(this.map && this.data) {
this.buildHeader();
this.setCount(this.data.length);
}
},
buildHeader:function() {
if(this.$.header) {
this.$.header.destroyClientControls();
} else {
this.createComponent({name:"header", tag:"tr", isChrome: true});
}
for(var i=0;i<this.map.length;i++) {
this.$.header.createComponent({content:this.map[i].header, tag:"th"});
}
this.$.header.render();
},
setupItem:function(source, event) {
for(var i=0;i<this.map.length;i++) {
event.item.$.row.createComponent({content:this.data[event.index][this.map[i].field]});
}
event.item.render();
return true;
}
});

Related

Dojo Event dynamically created events not handled

I have the following Dojo Javascript snippet:
postCreate: function () {
this.inherited(arguments);
var hitchWidgetToaddLevelButtonClickHandler = DojoBaseLang.hitch(this, addLevelButtonClickHandler);
DojoOn(this.AddLevelButton, "click", hitchWidgetToaddLevelButtonClickHandler);
function addLevelButtonClickHandler() {
hierarchyLevelNumber += 1;
var myButton = new Button({
label: "Click me!",
id: "level_button_" + hierarchyLevelNumber,
onClick: function(){
// Do something:
console.log("Hello there, ");
}
}).placeAt(someNode,"last").startup();
var registeredRemovalButton = DijitRegistry.byId(("level_button_" + hierarchyLevelNumber));
DojoOn(registeredRemovalButton, "click", function someFunction() {
console.log("how are you?");
});
}
}
and the following HTML
<div id="settingsBorderContainer" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline', gutters:false, region:'top'" class="${theme}">
<table data-dojo-attach-point="HierarchyLevelsTable">
<tr>
<th>Level</th>
<th> Table Name </th>
<th> Column Name</th>
</tr>
<td id="someNode">
</td>
</table>
</div>
The goal here is, when AddLevelButton is clicked it dynamically creates a row in the table with the necessary information and a new button for each row. Note that the HTML and Javascript are largely simplified to illustrate my problem. I did not include all of the row logic that occurs in addLevelButtonClickHandler() method. The HTMl is built as intended, as you can see in the following screenshot:
The problem is that everytime I add a button, only the last button's event listeners work, i.e. in the case of the screenshot the button circled in red logs: Hello there, you called the remove handler [object MouseEvent], and the previous button in the table no longer can be clicked and catch the necessary event. Sorry if the code is a gross-simplification of my goal, I appreciate any help that is offered.
*********EDIT**************
So I took a different approach and placed only 1 button total, and delete level by level from the bottom up. As long as it resides outside of a <table> it functions..Which brings me to the conclusion of NEVER PUT DIJITS OR BUTTONS IN A TABLE After many hours lost, I would recommend if you absolutely need a table in the first place, just placing the button next to the table as necessary using CSS and floats
I will leave this question as unanswered until someone may be able to offer an explanation of why the inner table approach does not work.
While working with dojo. You should never use node.innerHTML to add or update nodes. It will basically convert all the dijits into HTML string and parse as regular HTML by the browser. which in turn will destroy dom object and event associated to it.
Instead create dom object using dojo/dom-construct and add it with appendChild for DOM nodes or addChild in case of container dijits.

How to adjust the height of table inside a table

I have a web page whose layout is constructed using a Table
<table id="page-head">
<tr style="vertical-align: top">
<td class="s-part">
<table class="s-table" style="height: 131px; ">
<tr class="s-head"><td></td></tr>
<tr class="s-body"><td></td></tr>
</table>
</td>
<td class="main-part">
<div><!-- The Main Part --></div>
</td>
</tr>
</table>
<!-- Menu -->
<table id="page-foot"> . . . </table>
The Main Part contains the content of the page
-> in Page-Head, it contains logo and site links
-> in Page-Foot, it contains the actual content of the page.
The screen is below:
Page screenshot
I faced a problem: The s-part is a table and I had to resize it to match the height of the main-part. So I used this jQuery script
var adjustSideBar = function () {
var e = $("#page-head");
if ($(".s-table", e).outerHeight() < $(".main-part", e).outerHeight()) {
$(".s-table", e).css({ "height": $(".main-part", e).outerHeight() - 5 });
}
var e = $("#page-foot");
if ($(".s-table", e).outerHeight() < $(".main-part", e).outerHeight()) {
$(".s-table", e).css({ "height": $(".main-part", e).outerHeight() - 5 });
}
}
So it automatically adjusted as shown in (1) and (3) from the above Screen Shot
Now the problem is, When I use dynamic content like jQuery PlugIn and Toggling the div elements in page-foot -> main-part, the s-part is not resizing. Again I called the adjustSideBar() function whenever I Toggle the content in the page. But it resulted as below:
Before Toggling, 2. After Toggling & 3. Again Toggling of "Sec 1":
Toggling Screenshot
Note: the gray bar is an Expander constructed using a jQuery PlugIn.
The function call is done from here:
$("div[id^='sec']").click(function (e) {
$(".e-content", this).slideToggle();
// this to toggle the content of the Sec #
adjustSideBar();
});
Can I know what to do now guys, to resize the inner table s-part such that both s-part and main part of same heights?
If you want I can give additional codes that are involved also. But I think they are not concerned here. [ Additionally, I use PHP for server side programming for if there is something I can do with it too ]
Thank you in advance.
PS: I am not allowed to post images as of now. So I'm just Hyperlinking them.
First up, I should discourage using tables for layout.
For the answer, you should use the callback for slideToggle to call the sidebar adjustment after the animation instead of calling it right after the slideToggle function like so:
$("div[id^='sec']").click(function (e) {
$(".e-content", this).slideToggle(500, adjustSideBar);
}
Even like this it will first animate and adjust the background only after the toggle has finished. I am not certain this will fix your problem completely, but something you can work on.
I suggest using http://jsfiddle.net/ or similar to make a test case so your problem can be tested easier.

jquery or javascript: Truncate text in table cell

I need to truncate the text in the first column of a table using javascript or jQuery. Here's where I'm at:
The table:
<table id="bigTable">
<tr><td>Text is tooooo looong</td><td>Just Right</td></tr>
<tr><td>Text is tooooo looong</td><td>Just Right</td></tr>
<tr><td>Text is tooooo looong</td><td>Just Right</td></tr>
</table>
Trying stuff like the following without success ( compiled from other similar posts ):
var lbl = $("#bigTable tbody");
var newLbl = $(lbl[0].outerHTML);
$('td:eq(0)', newLbl).slice(5);
It does not seem to be getting the contents, or text from the cells. Has no effect whatsoever. Tried also -
$('td:eq(0)', newLbl)contents().slice(5);
$('td:eq(0)', newLbl).text().slice(5);
What am I doing wrong?
EDIT
Before more down-voting and general grumpy-ness occurs ...
I have to have the text from a div copied to a variable for manipulation and later display in a different window/div.
This happens in response to a button click.
...but applying the css rules sounds promising. Will try that instead.
Please see this fiddle to understand what I need to do:
http://jsfiddle.net/Vsse3/2/
I have to be able to wrap column cells with a div before using the css idea.
You don't need js, using CSS:
demo:
http://jsfiddle.net/vTDAQ/3/
#elckarns comment is correct but you also need to wrap the cell content in a div to use text-overflow.
Also note that your table is not well formed.
demo updated as requested:
http://jsfiddle.net/Vsse3/6/

How to push top table rows to the bottom - HTML and JavaScript

I want to push certain rows to bottom of table, how can it be achieved in plain JavaScript?
In the table below, I want to push the 1st and 2nd rows to bottom:
<table>
<tr><td>ABCD</td></tr>
<tr><td>XYZ</td></tr>
<tr><td>adfs</td></tr>
<tr><td>asd</td></tr>
<tr><td>asdasd</td></tr>
</table>
Just do something like this:
Say you have table like so:
<table id='table'>
<tr id='row1><td>....</td></tr>
<tr id='row2><td>....</td></tr>
<tr id='row3><td>....</td></tr>
<tr id='row4><td>....</td></tr>
</table>
And an array with the new order like this:
NewOrder[1] = 3;
NewOrder[2] = 4;
NewOrder[3] = 2;
Then, do some something like this (not tested, so you may need to tweak the code, but this is the idea):
for ( i=1; i<=NewOrder.length; i++ ) {
$('row'+i).appendTo( '#table' );
}
This way, they are moved to the end of the table in order.
So you will have 3 moved to the end, then 4 behind it, then 2 behind that, etc. After they have ALL been appended to the end of the table, the first one will become the first row, and the rest will be in the correct order behind it.
Make the table style='display:none;' and then do $('#table').show(); at startup.
Edit Again: You could do a div around the entire body content, like
<body>
<div id='everything' style='display:none;'>
....
</div>
</body>
So that the entire page will be hidden (just blank white) for the fraction of a second it takes to load and order the table.
Then you would use:
$(function(){
$('#everything').show();
}
To show the entire page all at once as soon as the DOM is ready. It will take a fraction of a second longer for the page to load, but it will all load at once, so there won't be any flash of missing tables, etc. As long as EVERYTHING is in #everything, it will just look like the page loaded - should be transparent to the viewer.
Append the first child of the table body to the table body.
Twice.
tablebodyreference.appendChild(tablebodyreference.firstChild);
tablebodyreference.appendChild(tablebodyreference.firstChild);
A tablebody element can only have trs as children.

.focus() doesn't work on an input while orher attributes works

I have a classic table / thead / tbody structure, which I add a line at the end of the tbody. The line contains only an input element. The code works in Firefox 3.6 but not in Chrome v5 or IE8. I'm using jQuery 1.4.2.
Does not work:
$("#" + AJAX_ID).parent().find('tr:last > td:nth-child(2) > input').focus();
Does work:
$("#" + AJAX_ID).parent().find('tr:last > td:nth-child(2) > input').css('background-color', 'red');
even setting an ID on the input, and using document.getElementBuId('id').focus() doesn't work.
*** edit ***
The site is pretty complex (mix of template / static html and dynamic html), but the table
looks like this (rendered html in chrome, layout via tidy) : http://pastebin.com/PHqxAEVm
*** edit 2 ***
Even $("#lectures").find("input:last").focus(); called from the ajax callback doesn't do anything.. $("#lectures").find("input:last").css('background-color', 'red'); turns one red though, but the focus goes to the address bar.
Here's a dump of the returned object from the selector:
http://pastebin.com/Jdw1TZXf
*** edit 3 ***
Here's the JavaScript code that builds the table: http://pastebin.com/cbCfi0UY
on page load, oContainer is $("#lectures") while after the ajax call it's $("#" + AJAX_ID).parent(), which is supposed to point to the table's tbody
*** edit 4 ***
Hard problem... here's the full lectures.js file: http://pastebin.com/Jkg0DZqa
batisses and compteurs are json objects loaded via the template. the user select a batisse then a compteur then press a button that calls buildAjout(), which calls in this example buildElectric($("#lectures"), compteur);. Once the line is filled bu the user, onBlurLecture(tr_parent) is called, the data is sent to the server via AJAX and function callback_compteurs_lecture_add(AJAX_ID) is called after the ajax call is complete. The function SendCommand is a custom function which use jQuery ajax.
The creation of the first input line (and the focus) works, but not the one created in the callback of the ajax.
*** edit 5 ***
The full rendered page looks like: http://pastebin.com/UfBYcjX3
I shortened the batisses variable. The (full) page has no JavaScript errors.
In Chrome's JavaScript console, I cannot focus the inputs.
*** edit 6 ***
Wrong function name in this question for SendCommand. fixed.
Solution found:
.focus() doesn't work on an input while orher attributes works
What ID are you targeting? Because if I replace $("#" + AJAX_ID) with $('table') it works -> demo (at least in Chrome)
and if I wrap the function inside a $(document).ready(function(){...}) it works in IE -> demo
I'm still looking to see what the problem might be, but I have a few comments about your code so far.
I haven't tested this, but I creating a jQuery object then appending another object inside ends up taking a lot of time because of the number of function calls. I've found it easier to just build up a string and only use one append. This example makes it easy to read:
var table = '\
<table style="width: 100%">\
<thead>\
<tr>\
<th>Numéro</th>\
<th>litre</th>\
<th style='width: 100px;'>Status</th>\
</tr>';
// append more to the string
table += '<tbody>.....</tbody></table>';
$('body').append(table);
I found this bit of code and I just wanted to show you that you can shorten it:
$("#no_batisse").css('display', 'none');
$("#lectures").html("");
$("#lectures").css('display', '');
shortens to:
$("#no_batisse").hide();
$("#lectures").empty().hide();
Instead of calling this function after each row addition, you could try adding a live function once that works with dynamically added content:
$(oLigne).find("input").blur(function() { onBlurLecture(oLigne); });
try running this when you initialize the script (just once)
$('#lecture').find('input').live('blur', function(){
onBlurLecture( $(this).closest('tr') );
})
I'll keep looking!
EDIT:
If your code is being triggered via some element that has a default behavior (like an <a> element), try adding return false; to the end of its callback.
Alternatively, if you give a parameter to the event handler's function, like function(e) {...}, you can call e.preventDefault() from within the callback instead of return false;.
First, I don't know if this is the issue, but IDs can not start with a number.
Second, which element has the AJAX ID that are you using? You'll get different results depending on that.
To avoid any ID issues, you could do:
$("#lectures").find('tr:last > td:nth-child(2) > input').focus();
or if you want it to be relative, do:
$("#" + AJAX_ID).closest('tbody').find('tr:last > td:nth-child(2) > input').focus();
got it!
I use "tab" (tabulation) to switch to the next input while writing in them. At the last one, the focus goes out of the window so I cannot set it. I converted the last column "status" into a input so it gets the focus while onBlur is executed. When the ajax load callback is called, the new line is added and the input is focused as it should.
Chrome: works
Firefox: works
IE8: works (with fix)
spent 1½ day on that lol
thanks everyone!
Try to trigger .focusin() (this was added in jQuery's 1.4) See documentation.

Categories

Resources