Select the next data cell given a data cell with Playwright - selector

There are several tables on a page. I'm. trying to get City/Town.
<table class="table" style="margin-top: 35px;">
<thead>
<!--<tr>
<th scope="col"></th>
<th scope="col"></th>
</tr>-->
</thead>
<tbody>
<tr>
<td>Street</td>
<td>61 Essex St</td>
</tr>
<tr>
<td>City/Town</td>
<td>Lynn</td>
</tr>
<tr>
<td>State/Province/Region</td>
<td>Maine</td>
</tr>
<tr>
<td>Zip/Postal Code</td>
<td>01902</td>
</tr>
</tbody>
</table>
I know I can get the element for the cell item above what I need.
let foo = page.locator(`test=City/Town`)
but I'm not sure how to get the next data cell. I googled and tried:
let foo = page.locator(`td:below(:text("street"))`).innerText()
but that didn't work. I also tried:
let foo = page.locator(`td:near(:text'("Street"))+td`).first().textContent()
this didn't work for me either. I tried some other variations.
Can you help a clueless man out?

You can use the adjacent sibling combinator: page.locator('td:text("City/Town") + td').
Example:
const playwright = require("playwright"); // 1.23.3
const html = `<!DOCTYPE html><html><body>
<table class="table" style="margin-top: 35px;">
<thead>
<!--<tr>
<th scope="col"></th>
<th scope="col"></th>
</tr>-->
</thead>
<tbody>
<tr>
<td>Street</td>
<td>61 Essex St</td>
</tr>
<tr>
<td>City/Town</td>
<td>Lynn</td>
</tr>
<tr>
<td>State/Province/Region</td>
<td>Maine</td>
</tr>
<tr>
<td>Zip/Postal Code</td>
<td>01902</td>
</tr>
</tbody>
</table>
</body></html>`;
let browser;
(async () => {
browser = await playwright.chromium.launch();
const page = await browser.newPage();
await page.setContent(html);
const result = await page.locator('td:text("City/Town") + td')
.textContent();
console.log(result); // => Lynn
})()
.catch(err => console.error(err))
.finally(() => browser?.close());

Related

MVC5 razor view dosen't display Bootstrap DataTable with #foreach Loop

I want to get a line like this with MVC5 #foreach method and dataTables.
Here is the index.cshtml file.
<table id="example" class="display" style="width:100%">
<thead>
<tr>
<th>
Optionen
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Position)
</th>
<th>
#Html.DisplayNameFor(model => model.Office)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Position)
</td>
<td>
#Html.DisplayFor(modelItem => item.Office)
</td>
</tr>
}
</tbody>
</table>
#section scripts
{
#Scripts.Render("~/bundles/jquery")
<script>
$(document).ready(function () {
$('#example').DataTable();
});
}
}
The line with 'Show [ ] entries' and 'Search [ ] ' doesen't appear but the rest of the table.
If there is no foreach loop in the table it works. Has someone an idea how to solve the problem?
Yo need to make sure the number of <th> in <tr> is the same with the number of <td> in <tr>.Here is a demo:
<table id="example" class="display" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Position)
</td>
<td>
#Html.DisplayFor(modelItem => item.Office)
</td>
</tr>
}
</tbody>
</table>
result:

How do I set the default attributes for my cells when there is no initial data

I have a scenario where I am filling my data table based on data received over a websocket. So I guess the best approach would be to create an empty table in html and then add the data via the API.
So say I want my final table to appear something like:
<table id="example" class="display" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name">Tiger Nixon</td>
<td class="position">System Architect</td>
<td class="office">Edinburgh</td>
<td class="age">61</td>
<td class="start-date">2011/04/25</td>
<td class="salary">$320,800</td>
</tr>
<tr>
<td class="name">Garrett Winters</td>
<td class="position">Accountant</td>
<td class="office">Tokyo</td>
<td class="age">63</td>
<td class="start-date">2011/07/25</td>
<td class="salary">$170,750</td>
</tr>
</tbody>
</table>
When dynamically loading my table I would only define the first part:
<table id="example" class="display" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
</table>
And then call row.add() in my javascript as the data is received.
But how do I set the default classes for my column cells this way?
When you use row.add(), the return value is the newly created (but not yet displayed) row.
You can use this value to iterate over the cells in the row, where class names can be added. Finally you can draw the row, so it is displayed.
The following shows the basic approach, where we have a pre-defined array of the class names you want to handle:
$(document).ready(function() {
var classNames = [ 'name', 'position' ];
var table = $('#example').DataTable();
var row = table.row.add( ['Quinn Flynn', 'Accountant'] );
var i = 0;
row.cells().every( function () {
var node = this.node();
$( node ).addClass( classNames[i++] );
} );
row.draw();
});
That final section of code from row creation to the row draw would be placed in a function, in your case.
This produces an HTML body as follows:
<tbody>
<tr role="row" class="odd">
<td class="name sorting_1">Quinn Flynn</td>
<td class="position">Accountant</td>
</tr>
</tbody>

Data is not showing up in html

I am running into a issue with showing the data from my variable, itemListPrice. I have checked in the console and the data is populated into the itemListPrice, but its not showing up in my html, am I loading it all wrong?
Here is the markup
<div id="app2">
<div id="TableContainer" style="width:798px !important">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<td><label>Catalog Name</label></td>
</tr>
</thead>
<tbody>
<tr>
<td>{{ currentCatalogName }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td><label>Item Name</label></td>
</tr>
</thead>
<tbody>
<tr>
<td> {{ itemPriceList.Name }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td colspan="2"><label>Item List</label></td>
</tr>
</thead>
<tbody>
<tr>
<td width="575px">${{ itemPriceList.ItemListPrice }}</td>
<td>${{ itemPriceList.ItemListPrice }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td colspan="3"><label>Item Features</label></td>
<td></td>
</tr>
</thead>
<tbody>
<template v-for="item in itemPriceList.ItemFeatures">
<tr v-if="item.FeatureQuantity != 0">
<td width="250px">{{ item.FeatureName }}</td>
<td>{{ item.FeatureQuantity }}</td>
</tr>
</template>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td><label>Item IAM</label></td>
</tr>
</thead>
<tbody>
<tr>
<td>{{ itemPriceList.ItemIAM }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
and here is my code
new Vue({
el: '#app2',
beforeCreate: function () {
StartBusyIndicator("#ItemPriceListWindow");
},
created: function () {
this.GetItemDetails();
},
mounted: function () {
StopBusyIndicator("#ItemPriceListWindow");
},
data: {
itemPriceList: [],
orderItems: currentOrderItems,
currentCatalogName: currentCatalog,
priceList: null
},
methods: {
GetItemDetails: function () {
TryCatch(function() {
let result = GetItemPriceDetails();
this.itemPriceList = result;
priceList = result;
});
},
GetOrderItems: function () {
},
OptionPriceSplitter: function (optionprice) {
return TryCatch(function() {
let sentenceSplit = optionprice.split('& ');
let comp = '';
for (let i = 0; i < sentenceSplit.length; i++) {
comp += sentenceSplit[i] + '\n';
}
return sentenceSplit;
});
},
GlobalListPriceCalculation: function (globalgroupid) {
return TryCatch(function() {
let listPrice = 0.00;
let priceList = this.itemPriceList;
let result = priceList.GlobalListPrices.filter(function(item) {
if (item.GlobalGroupID == globalgroupid) {
listPrice = listPrice + item.Price;
}
});
if (listPrice == 0) {
listPrice = "";
} else {
listPrice = "$" + listPrice.toFixed(2);
}
return listPrice;
});
}
}
});
assuming TryCatch(cb) is something along the lines of
function TryCatch(cb){ let x = null; try { x = cb() } catch(e) {} return x; }
you loose 2 important things:
this (you could bind it via cb.call(this))
utterly usefull error messages
furher points:
I have checked in the console
checkout the excellent browser plugin for ff and chrome vue-devtools
itemPriceList = []
you initalize itemPriceList as array, use it as array item in itemPriceList, but also use it as object {{ itemPriceList.Name }} - what shall it be ?

Simple table with colspan giving errors with datatables

I have this simple html table
<table id="mytable">
<thead>
<tr>
<th>Name</th>
<th colspan="2">Actions</th>
</tr>
<tr>
<th>Delete</th>
<th>Update</th>
</tr>
</thead>
<tbody>
<tr>
<td>MyName</td>
<td onclick="delete()">X</td>
<td onclick="update()">U</td>
</tr>
</tbody>
</table>
<script>
$(document).ready(function(){
$('#mytable').DataTable();
});
</script>
If i open this on the browser i get
"Cannot read property 'mData' of undefined".
I don't undestand where is the problem.. I am following the official example: https://datatables.net/examples/basic_init/complex_header.html
Thank you all!
your html have unmatched number of columns, notice the first row of your header has colspan while the second row doesn't.
what you can do is to provide a rowspan.
<thead>
<tr>
<th rowspan="2">Name</th>
<th colspan="2">Actions</th>
</tr>
<tr>
<th>Delete</th>
<th>Update</th>
</tr>
</thead>
here's a link to the datatables example of complex headers. https://datatables.net/examples/basic_init/complex_header.html

Post form data to controller

I want to submit the table row values of the rows that are checked to my controller. My controller is always receiving a parameter of value null.
#model IEnumerable<GoogleMapsAPIWeb.Models.Schedule>
#using (Html.BeginForm("Route", "Home", FormMethod.Post))
{
<div>
<table id="schedulerTable">
<thead>
<tr>
<th scope="col"></th>
<th scope="col" >View</th>
<th scope="col">Site Id</th>
</tr>
</thead>
<tbody>
#foreach (var row in Model)
{
<tr>
<td>#Html.CheckBoxFor(c=>row.isSelected)</td>
<td class="smallerDataField">
<a>View</a>#*TODO add code to view*#
</td>
<td><div class="smallerDataField">#row.SiteId</div></td>
</tr>
}
</tbody>
</table>
<input id="btnFindRoute" type="submit" value="Plan Route" />
</div>
}
[HttpPost]
public ActionResult Route(IEnumerable<Schedule> Schedules)
{
Use for loop for this purpose. More info
for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>#Html.CheckBoxFor(c=> Model[i].isSelected)</td>
</tr>
...
}