I have an ember application which has a table and a grid component within a page. I have enabled drag and drop feature which drags a value from a particular cell of a table and drops it into the empty space of the grid. As the tables have more than one column, I want to get the index position or I want to know which column's cell is being dragged. I want that index value of column within the controller.
Suppose a table has 3 rows with 3 columns. For all the elements within the first column being dragged, I want to get the index value as 1 similarly for 2nd and 3rd column.
Here is my .hbs code for the table
<table class = "table table-bordered">
<thead>
<tr>
<th colspan="6">Inbound Units</th>
</tr>
</thead>
<tbody>
{{#each currentbid as |currentbid|}}
<tr>
{{#each pull as |pull|}}
{{#if (eq pull.DRIVERNAME currentbid.DRIVERNAME)}}
<td abbr="P1">{{#draggable-object content=pull position=1 dragEndAction='dragEndAction'}}{{#draggable-object-target action="draganddrop"}}{{pull.P1}}{{/draggable-object-target}}{{/draggable-object}}</td>
<td>{{pull.P2}}</td>
<td>{{pull.P3}}</td>
<td>{{pull.P4}}</td>
{{/if}}
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
As you can see wihtin the table td tag, I have specified abbr attribute. But I have no idea how to get the value of abbr within the controller. Any other way of getting this is also fine.
Thanks !
This answer applies to Ember 2.x.x and was written as of 2.15.
By default, actions assigned to a specific event via closure, like ondragEnd={{action "someAction"}} receive the event target as an argument:
actions: {
someAction(event) {
console.log(event.target)
}
}
Possibly, you could use event.target.parentElement.className in your component to get the class name, then send the action and argument to your controller. Hopefully that selector will return the new parent and not the old one.
You can read about different ways to catch browser events in the Ember Guides.
Related
I am new to the react Js as well front end. Here, I have a table which is like
<div className="col-xs-12 " id="scrollableTable">
<Table striped condensed hover id="jobData">
<thead style={backgrounColour}>
<tr>
<th className='serial-column'>Sr.No.</th>
<th className='company-column'>Company Name</th>
<th className='technology-column'>Technology</th>
<th className='job-column'>Job Title</th>
<th className='total-score-column'>Total Score</th>
<th className='average-score-column'>Average Score</th>
</tr>
</thead>
<tbody>{this.props.jobData.map(function (item, key) {
return (
<tr key={key}>
<td><b style={compName}>{item.id}</b></td>
<td><b style={compName}>{item.attributes.companyName}</b></td>
<td>Xamarian Developer</td>
<td>{item.attributes.name}</td>
<td><b style={compName}>30</b></td>
<td><b style={compName}>30</b></td>
</tr>
)
})}</tbody>
</Table>
</div>
I do have one button which is like
<button className = "btn btn-default">Add Row </button>
It looks like
Now, What I want to have is that on click of this button,
This should get added as a first row in this table.
Actually, I want to have an idea how can I do this? I tried by adding a static row in this but It did not work. So, I am asking this question.
Any help will be great for me.
FOR SINGLE EDITABLE ROW
Create separate component holding the row you would like to add, after clicking the button.
Add state property that will be used to define if your 'edition row' is visible.
It should have default state false (as it should not be visible at initial render)
this.state = {
isRowAddingEditorVisible: false
}
When "Add row" button is clicked, set state of that above property, to true
this.setState({ isRowAddingEditorVisible: true });
And in your component that holds whole table implement it like this:
<tbody>
{this.state.isRowAddingEditorVisible && <RowAddingEditor />} // <--- HERE
{this.props.jobData.map(function (item, key) {
return (
<JobTableRow key={key} data={item}>
)
})}
</tbody>
This way you dont need to modify original data that forms the table.
And JobTableRow would hold everything you have as row currently.
It provides better separation, readability and your closer to SRP (Single Responsibility Princple - code/method/component doing one thing only)
Check this fiddle to get the idea:
https://jsfiddle.net/n5u2wwjg/167786/
Also {this.state.isRowAddingEditorVisible && <RowAddingEditor />} - this is called short circuting.
Am using the fact that $booleanValue && <ReactComponent/> will evaluate to <ReactComponent> when $booleanValue is true and will evaluate to nothing (visually speaking) when $booleanValue is false
FOR MULTIPLE EDITABLE ROWS
https://jsfiddle.net/n5u2wwjg/173218/
Hint 1:
I used Math.random() to get unique IDs for rows. It is just simple, fast, example solution and in that form should not be used in production code.
Usually unique IDs generators are based on Math.random(), but randomized bit more with some additional code.
Hint 2:
Remember that when POSTing row to server, you also need to remove its ID from state.
Hint 3:
Usually when you have method starting with get and returning some DOM elements you use in render method, it is signal that contents of this method should be moved to separate React component.
Hint 4:
In above example I use ES6 syntax. Learn it if you don't know it yet, it's your friend :). Search "ES6 features tutorial".
spread operator - ...
arrow function - => (anonymous also - not stored anywhere or named)
First you have to keep the this.props.jobData in state, Then OnClick of the button you have to update this.state.jobData
add a field in item to identify if it's an editable row or non editable. Let's say isEditable
The code will be:
<tbody>{this.state.jobData.map(function (item, key) {
if (!item.isEditable) {
return (
<tr key={key}>
<td><b style={compName}>{item.id}</b></td>
<td><b style={compName}>{item.attributes.companyName}</b></td>
<td>Xamarian Developer</td>
<td>{item.attributes.name}</td>
<td><b style={compName}>30</b></td>
<td><b style={compName}>30</b></td>
</tr>
)
} else {
return (
//Add your editable row here
)
}
})}</tbody>
#azrahel gave right answer about first problem (modify table view), below I'm writing about how it should work later - action triggered from that 'mini form'.
In short - it's data driven. Update data, view will be updated.
this.props.jobData.map shows that rows are from this.props.jobData array. Add row means then add to array. There is one problem - you can't mutate props. Props are passed from parent component then you have to update data in parent. To handle this you should have addJob method in parent and pass this handler as prop (like jobData, read docs).
But ... probably this array is not a local data, was fetched from server. If you want to update this on server (push to DB, to be available to others) then yon need to post data to server (it will store it in DB). In simplest scenario post request will return new, updated array. Updated data, passed to component should render component with new array (with new row). It's up to you if 'editing row' should be still visible or not.
I'm currently using Nightwatch to do some automated tests, but CSS selectors were extremely complex and did not do the job for me. I've instead started to look at XPath to do the job however the table in question is fairly complex.
I want to be able to .click() a button within a td value, where that specific row in the table contains a specific value. The table looks like this:
Username Email Display Name Buttons to Click (1st one wanted)
test test#example test (1st button)(2nd button)
test2 test2#example test (1st button)(2nd button)
Each of these values are within a tr > td so being able to find it is proving difficult. This is my currently XPath:
.click('/table[#id="admin-user-list"]/tbody/tr[td = "test2"]/td/button')
The HTML tree looks like this:
<div id>
<div class>
<table class>
<tbody>
<tr>
<td data-bind>(username)
<td data-bind>(email)
<td data-bind>(display name)
<td button>
(1st button)
(2nd button)
</tr>
</tbody>
Each row has its own tr with those exact tds inside.
Some help would be appreciated :)
.click('//table[#id="admin-user-list"]/tbody/tr[./td[text()='test2']/td/button')
logic: //table[#id="admin-user-list"]/tbody/tr[./td[text()='test2']
- tr with td that has text
/td/button -button in that row
but actually this is not really good idea to do that, as you are searching for that value in each column. Better to use combination of columnName+value
Let's check on that table sample: https://www.w3schools.com/css/css_table.asp
We'll search for table data in column by name, for example table column = Country, data = UK
//*[#id='customers']//tr/td[count(//*[#id='customers']//th[text()='Country']/preceding-sibling::*)+1][text()='UK']
again, logic is simple:
general locator is:
//*[#id='customers']//tr/td - we are searching for table data
with parameters: [text()='UK'] and position = same, as in column name [count(column_position)]
How to get column position:
just get column with needed text:
//*[#id='customers']//th[text()='Country'] and count it's preceding siblings:
//*[#id='customers']//th[text()='Country']/preceding-sibling::* , also we should add +1 , as we need current element's position. and count that staff, so here is the result: [count(//*[#id='customers']//th[text()='Country']/preceding-sibling::*)+1]
so having column position we can get general locator:
tableId = customers; columnName= Country; dataText = UK;
//*[#id='tableId']//tr/td[count(//*[#id='tableId']//th[text()='columnName']/preceding-sibling::*)+1][text()='dataText']
And here is locator to get hole row by data+columnName
//*[#id='customers']//tr[./td[count(//*[#id='customers']//th[text()='Country']/preceding-sibling::*)+1][text()='UK']]
and basically you can search anything inside of it, for example just add in the end
/td/button - to get button from it
I have a nested ng-repeat on a table. It is an accordion table with child rows for each parent row. In order to accomplish this I created a <tbody> for each parent item, placing the parent row in a <tr> and then using ng-repeat to add all the child rows. Due to the multiple <tbody> elements the zebra striping on the table gets thrown off. Another wrinkle is that the table has the ability to collapse/expand child rows and I need the striping to be correct for whichever rows are visible. So I am trying to manually add striping classes. I'm using Angular's ng-init to toggle a scope variable, and then using ng-class to apply it. The problem is that it appears to be bound to the final state of the variable rather than what it was as the iterator was rendering the row.
HTML:
<tbody ng-repeat="parentRow in myData" ng-init="changeRowClass($even)">
<tr ng-class="{'industry-payments-even-row':industryRowEven,'industry-payments-odd-row':!industryRowEven}">
<td>{{parentRow.industryCode}} - {{parentRow.industryCodeDescription}}</td>
<td class="numeric">{{parentRow.ValueOneSum}}</td>
<td class="numeric">{{parentRow.ValueTwoSum}}</td>
</tr>
<tr ng-repeat="childRow in parentRow.childIndustries" ng-init="changeRowClass($even)" ng-class="{'industry-payments-even-row':industryRowEven,'industry-payments-odd-row':!industryRowEven}">
<td>{{childRow.industryCode}} - {{childRow.industryCodeDescription}}</td>
<td class="numeric">{{childRow.ValueOne}}</td>
<td class="numeric">{{childRow.ValueTwo}}</td>
</tr>
</tbody>
Controller code:
$scope.industryRowEven = false;
$scope.changeRowClass = function(isEven) {
$scope.industryRowEven = isEven;
};
I need each iteration (of parent OR child) to reverse the class for the next one (I'm leaving out the issue of if the child is visible or not for now to keep this more simple). I can see in the debugger that the variable is getting toggled properly on each iteration. The problem is that the ng-class seems to be bound to the current state in scope so when it is true it would apply one class, but then next time it is false and switches the class for all of them.
I need it to just print the class according to the variable state at the time it renders that row, and then ignore the variable unless the ng-repeat is started over (like for sorting or toggling visibility, etc.)
Is there a way to stop the binding?
AngularJS has this build in with directives called ngClassOdd and ngClassEven.
They work just like ngClass, but only on odd/even items inside an ngRepeat.
We have a html data table in which we have alternate rows.One row with status SHOW and another row with status DONE. For example,
<table ng-repeat="item in dataSet| ng-hide="item.Status == 'DONE')"><tr><td ng-click ="showDoneRow();">{{item.PrimaryKey}}</td><td>{{item.Status}}</td></tr></table>
Now on click of PrimaryKey of row with Status SHOW we need to ng-show the next row with Status DONE. Remember the row with status DONE is hidden already.
Please can any one provide me any solution on this.
first is that, you have repeat at a wrong place, you are repeating a table where as you should repeat a row. and then write ng-hide separate from ng-repeat
<table ng-repeat="| )">
<tr ng-repeat="item in dataSet" ng-init="item.display = item.Status !== 'DONE'" ng-show="item.display">
<td ng-click="dataSet[$index+1].display=true">{{item.PrimaryKey}}</td>
<td>{{item.Status}}</td>
</tr>
</table>
and check the ng-click, set ng-show.
using ng-init i created a new property display which decides whether to show/hide the item.
and then on click took the next item index and set its display property to true
I have observable array binding for my HTML table columns.
User can change the table column Order using drag and drop option.
When a column order is changed from UI, the dependency between the UI for that column and observable array is no more exists.
So any change in the observable array is not reflecting UI?
My question is, How to retain the dependency between UI and its item in the observable array.
My view model
var ViewModelInfo={
HeaderColumnList: ko.observableArray([])
};
HTML is
<table>
<thead>
<tr data-bind="foreach : HeaderColumnList">
<th data-bind="text : ColumnName">
</th>
</tr>
</thead>
</thead>
</table>
There are options to change th order,
1) through UI(using drag and drop)
2) through sort function of knockout observable array.
when it is changed through UI, the UI element lost dependency between respective observable item element.