Ng-repeat unexpected multiple repeat - javascript

I have an object that looks like this:
So basically I don't know the length of the object and I'm using the following code to render the result:
<table style="width: 100%" border="0" cellpadding="0" cellspacing="0">
<tr ng-repeat-start="(key, item) in items() track by key" ng-if="item.primary"></tr>
<td first someprop="2">first</td>
<tr ng-repeat-end ng-if="!item.primary">
<td>[NOT] first</tr>
</tr>
</table>
I want the first element in the table(it's also highlighted) to always be the object with the primary=true property.
Expected result with the object represented above:
Current result:
If I add a third element to the object, I'm getting:
FIDDLE: http://jsfiddle.net/UuzZT/

You have a simple semantic error in your HTML, specifically the ng-repeat-start element does not wrap the <td> so the <td> shows up each time even though the ng-if is there. Corrected here:
http://jsfiddle.net/UuzZT/1/
However, there are other problems stemming from the fact that you are trying to iterate over an object; objects do not have ordinal properties.

Your first table row does not include the table data (td outside tr)
Change your code to this:
<table style="width: 100%" border="0" cellpadding="0" cellspacing="0">
<tr ng-repeat-start="(key, item) in items() track by key" ng-if="item.primary">
<td first someprop="2">first</td>
</tr>
<tr ng-repeat-end ng-if="!item.primary">
<td>[NOT] first</tr>
</tr>

Related

Cypress iterate table rows get specific element in cells

I'm trying to iterate through table rows and get each row which includes a specific value,
but it doesn't work for me.
I'm using .each() to iterate the rows and .within() on each $el,
inside that, I use cy.get('td').eq(1).contains('hello') but I the get assertion error:
Timed out retrying: Expected to find content: 'hello' within the element: <td> but never did.
when I console.log cy.get('td').eq(1) it yields the desired cell in each row and the test passes, so I don't understand why chaining .contains() doesn't work...
it('get element in table', () => {
cy.visit('http://localhost:3000/');
cy.get('tbody tr').each(($el) => {
cy.wrap($el).within(() => {
cy.get('td').eq(1).contains('hello') // contains() doesn't work
})
})
});
<table>
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<tbody>
<tr>
<td>January</td>
<td>$100</td>
</tr>
<tr>
<td>February</td>
<td>hello</td>
<td>$80</td>
</tr>
<tr>
<td>$10</td>
<td>hello</td>
</tr>
</tbody>
</table>
should('have.text', text) should work
cy.get('td').eq(1).should('have.text', 'hello')
If there's whitespace around text, use contain.text
cy.get('td').eq(1).should('contain.text', 'hello')
The simple answer is: don't :)
To be more specific use html attribute selection instead. The convention is to have an attribute named data-cy. Furthermore, I discovered it convenient to have a data-cy-identifier for when selecting specific rows. Since I'm not sure what you're trying with your code, I'll use a similar example that can hopefully get you going:
<table data-cy="expences">
<tr>
<td data-cy="month">January</td>
<td data-cy="price">$100</td>
</tr>
<tr data-cy="discounted">
<td data-cy="month">Feburary</td>
<td data-cy="price">$80</td>
</tr>
<tr data-cy="discounted">
<td data-cy="month">March</td>
<td data-cy="price">$10</td>
</tr>
</table>
You can of course do all sorts of combinations of this, but now you can do more specific and useful selections, such as:
cy.get('[data-cy="expenses"]').find('[data-cy="discounted"]').find('[data-cy="price"]').should(...)
And similar. This is flexible, because it reflects the structure of your data, and not the presentation, so you can change this to a list or whatever later. It avoids selecting of volatile things, so it's also more robust. It also uses a hierarchy rather than overly specific selectors.
The idea of adding things like data-cy-identifier allows you to do selections by ID (you can propagate it using javascript, angular, vue or whatever you use) and then checking things like the contents of a row with logically related items.
Hope it can get you going. Also I can recommend reading: https://docs.cypress.io/guides/references/best-practices.html

Angularjs Table error

I am very new to the UI development. I am using the AngularJS. I am trying to print all my values in a table. For that I am using Angularjs table. I have a headers and each header has multiple values. For one header the value will change based on the header. Here is my code.
<div>
<table datatable="ng"
class="table table-striped table-bordered dt-responsive nowrap"
cellspacing="0" width="100%">
<thead>
<tr>
<th>Entity Name</th>
<th>Field Name</th>
<th>{{headerName}}</th>
<th>DataType</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="entity in metadata">
<td>{{ entity.entityName }}</td>
<td>{{ entity.fieldName }}</td>
<td ng-if="entity.fieldLength != 'undefined'">{{entity.fieldLength}}</td>
<td ng-if="entity.familyName != 'undefined'">{{entity.familyName}}</td>
<td><select data-ng-init="dataType = entity.dataTypeList[0]"
data-ng-model="dataType"
data-ng-options="dataType for dataType in entity.dataTypeList"
class="form-control"
ng-change="onChange(entity.entityName,entity.fieldName,entity.fieldLength,dataType)"></select></td>
</tr>
</tbody>
</table>
</div>
Here if we observe one of the header "{{headerName}}" and this corresponding value will change based on this header. This headerName has two values (Field Length and Family Name). I used "ng-if" for this. Here are the two statements.
<td ng-if="entity.fieldLength != 'undefined'">{{entity.fieldLength}}</td>
<td ng-if="entity.familyName != 'undefined'">{{entity.familyName}}</td>
But when I execute this code I am getting an extra "td" in the table. Here is the output (Attachment).
So "FamilyName" are placed under "DataType" td. How can adjust this one. Can any one help me on this.?
Thanks in advance,
Amar.T
Not sure if I exactly follow but Is it possible you have four columns in your header and potentially 5 columns in your actual table? Is this the problem... also what happens when you do the following to check for undefined?
<td ng-if="typeof(entity.fieldLength) !== 'undefined'">{{entity.fieldLength}}</td>
<td ng-if="typeof(entity.familyName) !== 'undefined'">{{entity.familyName}}</td>
but why not combine the logic like so, so a vaue is shown without the ng-if and the need for two HTML columns?
<td>{{entity.fieldLength || entity.familyName}}</td>
Perhaps, also if possible you can move that logic into your controller (service,etc) That would make it easier to test. I think the answer above is correct also.

Angular ng-repeat best practice to only watch for updates inside the specific iteration

I need to render a table in which the user is allowed to edit some fields for each row, fields that will affect other fields in the same row.
For this reason I could not use bind-once on all the data I'm rendering.
If I got it right, simply using a code like the following
<table class="table table-bordered table-condensed table-hover">
<thead>
<tr class="info">
<th>Header</th>
<th>Header</th>
<th>Header</th>
<th>Header</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="a in alist">
<td>{{::a.id}}</td>
<td>{{::a.cod}}</td>
<td>
<input
ng-model="a.sel"
type="checkbox"
class="input-sm"></input>
</td>
<td ng-if="a.sel">
{{::a.desc}}
</td>
<td ng-if="!a.sel">
"Other Content"
</td>
</tr>
</tbody>
will cause that for each time a user checks or uncheks the a.sel checkbox, all the angular {{vars}} in the page (not the {{::vars}}) will be watched for changes.
If this is true (and therefore that's the reason why page is slow when hundreds of rows are loaded) how could I tell angular I only want it to check if something has changed inside that specific row, that specific ng-repeat iteration?
Not sure how to proceed to get good performances, any other tips are appreciated.
Try using track by to tell angular what it should be evaluating on:
<tr ng-repeat="a in alist track by $index">
or
<tr ng-repeat="a in alist track by a.id">
more on that in the ng-repeat documentation

Creating table with unknown headers with angular.js

I have a .json with unknown attributes and want to graph them. In my controller I have already extracted the headers and saved them as table_headers, from the doubleObjects Object. The data looks like this:
$scope.items = {[
...,
{doubleObjects: [{...}, {...}, ...]},
...,
]};
This is what I'm trying to output:
<table style="width:100%" border="1px solid black">
<tr>
<th ng-repeat="y in table_headers">{{y}}</th>
</tr>
<tr ng-repeat="x in items.doubleObjects">
<td ng-repeat="y in table_headers">{{x.y}}</td>
</tr>
</table>
So I would like the entire doubleObjects object to be tabulated. It wont let me do:
{{x.y}}
If I do:
{{x.measurement}}
or another attribute that is in doubleObjects then its all fine. But I cant ensure that the "measurement" attribute will be there. (For my test data it is.)
Thoughts and suggestions are most welcome.
You should not use x and y that way {{x.y}} use it this way {{x[y]}} or like the other way shown in this jsfiddle which shows both possibilities. I stated both possibilities, as I didn't really understood what you are looking for and I'm not allowed to comment your post.
<tr ng-repeat="x in items.doubleObjects">
<td ng-repeat="y in table_headers">{{x.measurement}}.{{y}}</td>
<td ng-repeat="y in table_headers">{{x[y]}}</td>
</tr>

Getting the content of a table td in console

This is proving surprisingly tricky. Attached is a screen of what I want from the DOM:
I want the innerHTML (Or at least I thought I did) of the td with class product-price.
Here's another screen of all the stuff I've tried and the output:
How do I get the console to return $59.99? Important it comes from the first element not the second where 59.99 also exists
Following a comment, here is the broader html:
<table cellspacing="1" class="store_location_list">
<tr>
<th class="item-type">Item<br />Type</th>
<th class="item-desc">Item<br />Description</th>
<th class="total-qty">Total<br />Quantity</th>
<th class="product-price">Product<br/>Price</th>
<th class="total-price">Total<br/>Price</th>
<th class="removeItemLink"> </th>
</tr>
<tr>
<td class="item-type">Metal Wall Art</td>
<td class="product-description-long">Metal Wall Art</td>
<td class="total-qty">1</td>
<!-- ************************************** -->
<td class="product-price">$59.99</td>
<td class="total-price">$59.99</td>
<td class="removeItemLink">Remove</td>
</tr>
</table>
document.querySelector('td.product-price').innerHTML
Use:
document.querySelectorAll('.product-price')[1].innerHTML
jsFiddle example
querySelector only returns the first match, while querySelectorAll returns all of them (hence the [1]) notation to get the second element. You can also use textContent
in place of innerHTML as it works a little faster, but you won't notice much of a difference in your case.
document.querySelectorAll('.product-price')
will return an array of elements with the given class.
document.querySelectorAll('.product-price')[n]
will give you (n+1)th element with the class
.innerHTML : will give you html content inside it, i.e. even the tags.
.textContent : will give you only text, no html tags.
What you want are the properties: innerText and innerHTML.
But you are selecting the correct item. You select all elements that have the product-price class, and the title of the table has that class.
I recommend you first to ensure you select the correct item.

Categories

Resources