Transforming tables in html using tritium - html-table

I'm using the Moovweb SDK and I'd like to transform a table and all it's elements into divs to facilitate mobile web dev/styling. What is the best way to do this?
<body>
<table>
<tbody>
<tr>
<td>
...
</td>
<tr>
</tbody>
</table>
</body>

There's a function I've seen used for this purpose in many projects called table_dump. I can't take credit for inventing it, but here it is in its entirety:
#func XMLNode.table_dump(Text %xpath){
$(%xpath) {
name("div")
add_class("mw_was_table")
$(".//table | .//tr | .//td | .//th | .//thead | .//tfoot | .//tbody | .//col | .//colgroup | .//caption") {
%i = index()
%n = name()
name("div")
attributes(data-mw-id: concat("mw_dump_", %n, %i), width: "")
add_class(concat("mw_was_", %n))
}
yield()
}
}
It really does three things:
Change all tables and table elements to divs
Give each former table element a class mw_was_ with its previous element
Based on the index, give each element a unique id as data-mw-id.
With all three of these you can get rid of a table while preserving the diversity of its elements. This way it remains easily selectable in XPath and CSS, even after you've transformed it.

Related

htmx: How to swap table row with hx-swap-oob?

I want to use hx-swap-oob to replace a table row of the existing page "out of band".
in browser:
<table>
<tr id="offer_1">....</tr>
<tr id="offer_2">....</tr> (old)
<tr id="offer_3">....</tr>
</table>
From Server to client:
<table hx-swap-oob="outerHTML:#offer_2" hx-select="#offer_2">
<tr id="offer_2"> .... </tr> (new)
</table>
But up to now this is the result:
<table>
<tr id="offer_1">....</tr>
<table hx-swap-oob="outerHTML:#offer_2" hx-select="#offer_2">
<tr id="offer_2"> .... </tr> (new)
</table>
<tr id="offer_3">....</tr>
</table>
I guess hx-select does not get evaluated when htmx get this snippet from the server.
How can I swap a row out-of-band?
Take a look at the new extension multi-swap.
https://htmx.org/extensions/multi-swap/
It allows swapping multiple elements marked with the id attribute.
For each element it is possible to choose which swap method should be used.
This does work:
<tr hx-swap-oob="true" id="offer_2"> .... </tr> (new)
But it has a drawback:
You need to modify the method which creates this row. Depending on your context, you might already have a method for this. Why modify this method, just because the result of this method should get used out-of-band?
If you use Django, this snippet could get used to add the hx-swap-oob attribute after the HTML got created:
def add_oob_attribute(html):
"""
I would like to avoid this ugly hack
https://github.com/bigskysoftware/htmx/issues/423
"""
assert isinstance(html, SafeString)
new, count = re.subn(r'(<\S+)', r'\1 hx-swap-oob="true"', html, count=1)
if not count == 1:
raise ValueError(f'Could not add hx-swap-oob: {html}')
return mark_safe(new)
I created an issue to find a better solution in the future:
https://github.com/bigskysoftware/htmx/issues/423

vue application best practice/pattern to display data with people-friendly label?

A high-level vue application question:
A little background:
small team, still learning vue
creating a new large reasonably complex vue app from scratch
vue-cli scaffolded (vue 2, veux, router, i118n, bootstrapVue)
there are many different application entities – e.g. "user", “product”, etc.
there are many of each item, and each has a data record (from a db) that includes a number of boolean fields such as "active", “enabled”, “is_sensitive”.
item data displays in many different contexts, including: search results, table lists (e.g. browse), individual landing views for each (an item detail page), as well as ancillary lists (e.g. “A related item:”
The key problem:
In every display situation, the boolean values should to be translated from machine-friendly to people-friendly terminology.
The appropriate human-friendly term depends on the field itself (so there is no single translation for all fields).
Example:
Machine-friendly data:
[
{
name: “Megaphone”,
is_embarrassing: false,
active: false,
},
{
name: “Wankel Rotary Engine”,
is_embarrassing: true,
active: true,
},
]
Human-friendly list:
+----------------------+----------+---------------------+
| Name | Active? | Embarrassing? |
+----------------------+----------+---------------------+
| Megaphone | Inactive | Not embarassing |
| Wankel Rotary Engine | Active | Item is Embarassing |
+----------------------+----------+---------------------+
The key question:
What is the best way to solve this in a scalable, efficient, elegant, sensible way?
I have thought of a couple of options…neither of these feel scalable nor elegant,
and are brittle.
(1) sequence of in-line v-if conditions within the component view template
<p v-if=“item.property.is_embarrassing">
Item is Embarrassing
</p>
<p v-else>
Not embarassing
</p>
(2) computed properties in the component
<p>
{{ detailsPropertyEmbarrassing }}
</p>
detailsPropertyEmbarrassing() {
return item.property.is_embarrassing ? “Item is Embarrassing : “Not Embarrassing”;
},
I have also been noodling over the idea of some sort of Map that is imported along with the data and used to get the right labels, but I haven’t completely worked that out yet.
What is a solution for transforming data fields to people-friendly labels across an entire application, for a variety of different display situations?
(Side note: I may also need to transform the field in other ways, such as truncating length…)
And is there a way to establish this globally in the app in a manner that is scalable, both technically and organizationally,
so that new components can display as desired, near-automatically?
This seems like a basic fundamental need in any app of size, not just vue,
so I feel like this has been/has to have been solved before, but either I cannot find the right research keywords or am missing something obvious.
Thanks in advance!
You can think of the VueComponent as a ViewModel (or Controller if you like MVC). It's indented purpose is to fetch information from the model (backend) and transform it to something that your view can use.
Thus, the change from a boolean to a string should be done in the VueComponent and not in the view.
In your view component, declare something like myItems = []. When you fetch it, traverse the result:
backendResult.forEach(item => {
this.myItems.push({
name: item.Name,
embarrassing: item.IsEmbarrassing ? "Oh yes!" : "No"
});
Then the view is simplified:
<table>
<tbody>
<tr v-for="item in myItems">
<td>{{item.Name}}</td>
<td>{{item.embarrassing}}</td>
</tr>
</tbody>
</table>
Another solution is to use filters which requires no modification of the vue component and just a small change of the view:
<table>
<tbody>
<tr v-for="item in myItems">
<td>{{item.Name}}</td>
<td>{{item.isEmbarrassing|embarrassing}}</td>
</tr>
</tbody>
</table>
Notice the |embarrassing part in the view. It uses a filter defined as:
Vue.filter('filter', function (value) {
if (!value) return 'not embarrassing'
return 'embarrassing';
})

How do I use chained `css()` calls so the selector in the second call uses the first call as the context?

I'm processing a table row-by-row and need to sniff the ids of the rows:
<table id="tbl">
<tr id="row_1">
<td id="cell_1">...</td>
</tr>
<tr id="row_2">
<td id="cell_2">...</td>
</tr>
</table>
So my code looks something like:
def parse_table(self, response):
rows = response.css('#tbl > tr')
for row in rows:
rowid = row.css('::attr(id)')
if rowid.extract_first().startswith('row'):
...
However, this way, the second call to .css() gives me IDs of all the descendants of row, not just its direct children. I.e. for the above example HTML, it returns "cell_1" as well as "row_1". How do I scope the chained css() call so it only acts on direct children of the given row?
I've tried using the :scope pseudo-class but that doesn't seem to be supported by Scrapy, and :root gives me no results.
Alternately, can I just get the value of the id attribute without going through CSS?
I can show you how to use XPath for the same task:
def parse_table(self, response):
for row in response.xpath('//*[#id="tbl"]/tr'):
rowid = row.xpath('./#id').extract_first()
if rowid.startswith('row'):
...

vuejs best practice to order table columns by order in array

I'm taking first steps with vuejs and came across this issue. I have this data i want to display inside a table, using this quite straightforward piece of code:
<table class="table table-striped table-hover" :class="[hide ? 'thide' : '']">
<thead>
<tr>
<th v-for="(item, index) in _items[0]">{{fixTheaders(index)}}</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in _items">
<td v-for="field in item">{{item}}</td>
</tr>
</tbody>
</table>
now the problem is that each item stores data in the following form, for example:
{
"cost": "123.324",
"placement": "placement-1",
"date": "2017-03-03",
"device": "mobile"
}
so in this case the table will present data in this order :
cost | placement | date | device
but i want the order to be : date | device | cost | placement
i have a feeling that best way will be to have array :
filters = ['date,'device','cost','placement']
and use it but i couldn't find a vuejs way to handle it.
so, any idea what will considered best practice in vuejs to present the data in a table by the order i set in the array?
worth mentioning that when initialized, the order is determined on the server and fixing it from this side is not an option
Personally, I would just hardcode the headers, and get the correct value in your tbody
<tbody>
<tr v-for="(item, index) in _items">
<td>{{item.date}}</td>
<td>{{item.device}}</td>
<td>{{item.cost}}</td>
<td>{{item.placement}}</td>
</tr>
</tbody>
Unless of course you need more stuff done with them, then a property containing the column keys in desired order is what I would go for indeed :-)

Handling of dynamic ids through selenium webdriver

Automate an application through selenium where id changes dynamically.how can i handle this.Pls help me..
HTML code is:-
<table border="0" cellpadding="0" cellspacing="0" width="1000px">
<tbody><tr id="ctl00_ctl00_MainContent_CarQuoteMainContent_rpQuotes_trSelectedQuote_0">
<td align="center" valign="middle" width="12%">
<input id="ctl00_ctl00_MainContent_CarQuoteMainContent_rpQuotes_chkCompare_0" name="ctl00$ctl00$MainContent$CarQuoteMainContent$rpQuotes$ctl00$chkCompare" type="checkbox">
</td>
In both the cases ( and ), I assume that the first part of the ID is unique. So you can use something like this.
//tr[contains(#id,'ctl00_')] and for input field //input[contains(#id,'ctl00_')].
Those don't look like dynamic IDs, but rather non-content-specific row IDs for elements in a list.
If that's the case, you can't immediately ascertain 'This row element is displaying data for MyCarQuotes.com' from this information alone as there's nothing in the HTML shown to base that query on.
If there's something in the rows you can use to 'identify' the content (eg a company name) - and you have a specific 'thing' you want to interact with - you could encapsulate the lookup and do something like
CheckboxForQuoteFromCompany("MyCarQuotes.com").Click();
If you're able to post more of the HTML (at least a full row), and more importantly the intention of your test, we may be able to be of more help.