I am using the Ag-grid in my project. after came far i got to know that text inside the grid user not able to select. Is there any help i can get to select and copy the text from grid or i need to change to different plugin.
I am not in place where i can go back to different UI plugin or i can buy the Ag-grid. Need to find out some code hack for this.
I tried below hack but not work.
import {Directive, EventEmitter, Output} from '#angular/core';
import {AgGridNg2} from 'ag-grid-angular';
import {GridApi} from 'ag-grid';
#Directive({
selector: '[gridRangeRowSelection]',
})
export class AgGridSelectionDirective {
#Output('gridRangeRowSelection') onChangeEvent = new EventEmitter();
constructor(grid: AgGridNg2) {
grid.rangeSelectionChanged.subscribe(event => {
const api: GridApi = event.api;
// deselect previous rows
this.clearPreviousSelections(api);
const selectedRows = this.getSelectedRows(api);
this.onChangeEvent.emit({rows: selectedRows});
});
}
public getSelectedRows(api: GridApi) {
// get all range selections (ctrl+click/drag for multiple selections)
const rangeSelections = api.getRangeSelections();
const selectedRows = rangeSelections ? rangeSelections
.map(rangeSelection => {
let start = rangeSelection.start.rowIndex;
let end = rangeSelection.end.rowIndex;
if (start > end) {
[start, end] = [end, start];
}
// Equivalent of _.range(startRowIndex, endRowIndex).map(api.getDisplayedRowAtIndex)
const selectedRowsInRange = [];
for (let index = start; index <= end; index++) {
const selectedRow = api.getDisplayedRowAtIndex(index);
if (selectedRow) {
selectedRowsInRange.push(selectedRow);
}
}
return selectedRowsInRange;
}).reduce((a, b) => a.concat(b), []) : [];
// Unique selectedRows - as we can have multiple range selections, they may overlap rows.
const selectedRowSet = Array.from(new Set(selectedRows));
const selectedRowData = selectedRowSet.map(row => {
// note we cant use row.setSelected(true), as this will override copy to clipboard
// for cells to the whole row.
row.selected = true;
return row.data;
});
// update all newly selected and previously unselected rows
api.updateRowData({update: selectedRowData});
return selectedRowData;
}
private clearPreviousSelections(api: GridApi) {
// note this is side-effecting selection so we only do 1 pass.
const previousSelected = api['rowModel'].rowsToDisplay
.filter(row => row.selected)
.map(row => {
row.selected = false;
return row.data;
});
api.updateRowData({update: previousSelected});
return previousSelected;
}
}
https://gist.github.com/nite/dea82d184411a51fc6bc6adc7edaa422
Thanks in advance.
There is a flag which will allow you to select text and then CTRL+C will work.
[enableCellTextSelection]="true"
[ensureDomOrder]="true"
This is not an enterprise config and can be at any time to enable cell
text selection.
The above CSS fix is not working in IE>10 versions. So, I thought this would be a better solution.
Docs: https://www.ag-grid.com/javascript-data-grid/selection-overview/
#thirtydot I am not looking range selections, i am looking user can
select the few or all text from a cell.
I use this CSS for some grids where it's useful for the user to be able to select and copy part of the text in a cell:
/* enable basic browser text selection in grid cells */
div.ag-root .ag-cell-focus {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
Some CSS that comes with ag-grid disables text selection in cells (with user-select: none;), presumably because it clashes with the range selection enterprise feature. That's not a problem for you though!
Related
I'm working on column selection from a table. I have a limit of 20 elements. I need to be able to select all items until the limit is passed.
Scenario:
18/20
next column has 10 elements
click on select whole column
it should select only the first two elements
What I've tried:
const myArray = selectedColumn.some(() => itemsOnArray.length <= 20)
? selectedLaborer.map((laborer) => laborer)
: [];
Can you provide more context to your question?
Personally when it comes to selections, i usually gives an id to the object i wish to select if they don't have one (An unique identifier) and store this value in an array as my selected objects.
Adding and removing values is quite easy then :
(This is typescript for more explicit reading, but doesnt change anything)
let selectedItems: string[] = [];
const MAX_ALLOWER_SELECTED = 20;
const selectItem = (itemId: string): void => {
//You might want to check for duplicates before inserting
if (selectedItems.length > MAX_ALLOWED_SELECTED)
return;
selectedItems.push(itemId);
}
const unselectItem = (itemId: string): void => {
selectedItems = selectedItems.filter(id => itemId !== id);
}
const isItemSelected = (itemId: string): boolean => {
return selectedItems.includes(itemId);
}
We're using react-table's useRowSelect for row selection. We want to extend it such that we can define rows that are non-selectable based on a certain condition, in which case they will not be selectable - neither when clicking on the non-selectable row nor when clicking Select All.
Any ideas would be greatly appreciated.
Dunno if you found your answer since but i ran into kinda a similar issue.
You can use conditionnal statement to disable a row check box quite simply.
in your useTable() you pass your props like this to your checkbox :
DataTable.js
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
selectedFlatRows,
state: { selectedRowIds },
} = useTable(
{
columns,
data,
initialState: {
selectedRowIds: INITIAL_SELECTED_ROW_IDS
}
},
useRowSelect,
hooks => {
hooks.visibleColumns.push(columns => [
{
id: 'selection',
Header: ({ getToggleAllRowsSelectedProps }) => (
<Checkbox {...getToggleAllRowsSelectedProps()} />
),
Cell: ({ row }) => <><Checkbox {...row.getToggleRowSelectedProps()} {...row.values} /> </>
//HERE : You pass your rows' values to your component
},
...columns
])
},
)
then in your checkbox component you can tell if you want the checkbox to be disabled or not based on one of your props (in this case i use one of the rows value called "valide")
CheckBox.js
import React, {useState} from 'react'
export const Checkbox = React.forwardRef(({ indeterminate, ...rest }, props, ref) => {
const defaultRef = React.useRef()
const resolvedRef = ref || defaultRef
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate
}, [resolvedRef, indeterminate])
return (
<div className="d-flex justify-content-around">
{
rest.Valide
//i check if res.Valide is true or false (res.Valide is one of my row column's value which takes a boolean)
? <><input type='checkbox' ref={resolvedRef} {...rest} className="mx-auto" /></>
: <><input type='checkbox' ref={resolvedRef} {...rest} className="mx-auto" disabled/></>
}
</div>
)
})
if res.Valide is true then the checkbox is rendered normally, else it's disabled so you won't be able to check it.
I managed to come up with a full solution to this problem, by adapting an answer recently posted in this GitHub issue
In my case, I needed this to work with pagination and with Select Page / Select All (as opposed to only Select All).
I managed to create a solution - based on the solution from the GitHub issue thread - which supports these requirements as well. I then extracted it to an easily reusable helper function. Below is the helper function followed by an example of usage.
The helper function
import { HeaderProps, Row } from 'react-table'
interface GetConditionalSelectHeaderCheckboxProps {
/** react-table's header props */
headerProps: React.PropsWithChildren<HeaderProps<any>>
/** A predicate - based on your business logic - to determine whether a given row should be selectable */
checkIfRowIsSelectable: (row: Row<any>) => boolean
/** Whether to allow page selection. Default: true */
shouldSelectPage?: boolean
}
/**
* A convenience method for react-table headers for allowing conditional select
* #param headerProps react-table's header props
* #param checkIfRowIsSelectable A predicate - based on your business logic - to determine whether a given row should be selectable
* #param shouldSelectPage Whether to allow page selection. Default: true
* #returns Modified `checkboxProps` to enforce the conditional select
*/
export const getConditionalSelectHeaderCheckboxProps = ({
headerProps,
checkIfRowIsSelectable,
shouldSelectPage = true,
}: GetConditionalSelectHeaderCheckboxProps) => {
// Note that in my comments I differentiate between the standard logic and the logic for the conditional select
const checkIfAllSelectableRowsSelected = (rows: Row<any>[]) =>
rows.filter(checkIfRowIsSelectable).every(row => row.isSelected)
// Standard: Here we define the selection type for the next click: Select Page / Select All
const isSelectPage =
shouldSelectPage &&
headerProps.page
// For conditional select: Filter the rows based on your business logic
.filter(checkIfRowIsSelectable)
// Standard: `isSelectPage === true` if some of the rows are not yet selected
// This (standard) logic might be confusing to understand at first, but - as a side note - the idea is as follows:
// This is the variable that defines whether the header props that will be received FOR THE NEXT CLICK will be for Select Page or for Select All
// Try to play this out in your head:
// - Initially, none of the rows are selected, so when we clicking the button initially, we will select only the (selectable) rows on the page (i.e. Select Page), hence the next click will be for Select All, hence `isSelectPage` will be `false`
// - When clicking again, we will select the rest of the (selectable) rows (i.e. Select All). The next click will again be Select All (for de-selecting all), hence `isSelectPage` will be `false`
// - Finally, when clicking again, we will de-select all rows. The next click will be for Select Page, hence `isSelectPage` will `true`
.some(row => !row.isSelected)
// Standard: Get the props based on Select Page / Select All
const checkboxProps = isSelectPage
? headerProps.getToggleAllPageRowsSelectedProps()
: headerProps.getToggleAllRowsSelectedProps()
// For conditional select: The header checkbox should be:
// - checked if all selectable rows are selected
// - indeterminate if only some selectable rows are selected (but not all)
const disabled = headerProps.rows.filter(checkIfRowIsSelectable).length === 0
const checked =
!disabled && checkIfAllSelectableRowsSelected(headerProps.rows)
const indeterminate = !checked && headerProps.rows.some(row => row.isSelected)
// For conditional select: This is where the magic happens
const onChange = () => {
// If we're in Select All and all selectable rows are already selected: deselect all rows
if (!isSelectPage && checkIfAllSelectableRowsSelected(headerProps.rows)) {
headerProps.rows.forEach(row => {
headerProps.toggleRowSelected(row.id, false)
})
} else {
// Otherwise:
// First, define the rows to work with: if we're in Select Page, use `headerProps.page`, otherwise (Select All) use headerProps.rows
const rows = isSelectPage ? headerProps.page : headerProps.rows
// Then select every selectable row
rows.forEach(row => {
const checked = checkIfRowIsSelectable(row)
headerProps.toggleRowSelected(row.id, checked)
})
}
}
// For conditional select: override checked, indeterminate and onChange - to enforce conditional select based on our business logic
return {
...checkboxProps,
checked,
indeterminate,
onChange,
disabled,
}
}
Example usage:
const columns = [
{
accessor: 'foo',
Header(props) {
const checkboxProps = getConditionalSelectHeaderCheckboxProps({
headerProps: props,
// Your business logic, e.g.
checkIfRowIsSelectable: row => row.original.someData !== 'some value'
})
return <Checkbox {...checkboxProps} />
},
// ...
]
Hi i am using Angular8 with Bootstrap, here i have used reactive forms, when i check on Make Default checkbox, then values present in Flowers row (Mail, Electronic,Delivery and Receipent)should be copied same to rest rows.
If the fax number format is not proper, how to show error message just below particular row recepient.
Ts:
checkAll(ev) {
const control = <FormArray>this.exampleForm.get("printList");
if (!this.all) {
this.printListArray.forEach(x => (x.value = false));
control.patchValue(this.printListArray);
} else {
this.printListArray.forEach(x => (x.value = true));
control.patchValue(this.printListArray);
}
console.log(control.value);
}
isAllChecked() {
this.all = !this.all;
}
DEMO
Your code should work more like the following
checkAll(ev) {
const control = <FormArray>this.exampleForm.get("printList");
console.log(this.all);
if (this.all) {
this.all = false;
this.printListArray.forEach(x => (x.electronics = false));
control.patchValue(this.printListArray);
} else {
this.all = true;
this.printListArray.forEach(x => (x.electronics = true));
control.patchValue(this.printListArray);
}
}
The main difference is that instead of changing the value field of x I'm changing the electronics fields, so when you are patching the form latter on, this.printListArray will have the appropriate data in the appropriate state.
In the implementation that I'm suggesting you will be able to toggle all checboxes in the row Electronics
Keep in mind that the printListArray is not an array form FormGroup/FormControl, its an array of regular objects, so the field value that usually exists in the FormControl is not present.
I am trying to create an annotation container that wraps around a node, which uses createTreeWalker to find all the text nodes. I then push those into an array and now I have every single text node I could potentially apply the highlight too and the ability to annotate.
My biggest challenge right now is that it seems like you can only use these global methods with document, such as onselectionchange or onslectionstart. Which means it outputs the start and end ranges for anything i highlight on the entire document.
I want to only allow this on text nodes that i have in my array which i mapped over.
import React, { createRef } from 'react'
// Get all text nodes that Annotate wraps ✅
// Make sure the selection only works within those text nodes
const Annotate = (props) => {
const { children, reference } = props
const node = reference.current
if (node) {
let textNodes;
let nodeArray = []
let walk = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, null, false)
while (textNodes = walk.nextNode()) {
nodeArray.push(textNodes)
}
console.log(nodeArray)
nodeArray.map(tn => {
let range = new Range()
// Setting the starting range of each textContent at 0
range.setStart(tn, 0)
// Setting the ending range of each textContent as the last character
range.setEnd(tn, tn.length)
Number.prototype.inRange = function(a, b) {
return this >= a && this <= b
}
document.onselectionchange = function() {
let {anchorNode, anchorOffset, focusNode, focusOffset} = document.getSelection();
// Making sure selection is within some text node container?
if (anchorOffset.inRange(range.startOffset, range.endOffset) || focusOffset.inRange(range.endOffset, range.startOffset)) {
console.log(anchorOffset, 'start')
console.log(focusOffset, 'end')
}
}
})
}
return children
}
export default Annotate
Mutating the DOM in react is not advisable from what I've been reading but for this case it might not be entirely dumb. However, I need some help advisement here on how to accomplish what I want.
I want to select one row in my first grid grid1 and the event function would then filter my other grid grid2 based on the values found in the selected row. I am using the pure javascript version of the library.
Something like
gridOptions:{
onRowSelected:my_event_filter_func,
rowData: [...],
columnDefs:[...]
}
grid1 = new agGrid.Grid(document.querySelector("#the_place"),gridOptions)
(grid2 is defined the same way based on different data and w/o the event function)
where my_event_filter_func is
my_event_filter_func = function(event) {
let my_name = event.data.name
// filter grid2 to show only the rows where the 'name' column matches my_name
}
Any help is appreciated.
I can't give you a line by line answer, and I am assuming that you are able to get your selected rows. But what I can suggest is, first, you create a copy of your the data that is on your grid2.
function copyData() {
rowData = [];
gridApi.forEachNode(node => rowData.push(node.data));
// temp is the copy of your full data in grid2
temp = [...rowData];
}
Next, on your my_event_filter_func, you can filter out the rows to be shown on grid2, based on the filtered value from grid1.
function my_event_filter_func(event) {
let my_name = event.data.name
// get the rows that do not have the matching value
const rowsToBeRemoved = temp.filter(row => row['name'] !== my_name);
// remove the rows from grid2 that do not have the matching names
gridOptions.api.updateRowData({remove: rowsToBeRemoved});
}
The source for the 2 grids is the underlying data for grid1 so it made my life easier. If that is not the case, you do need to save your base data for grid2 somewhere so that you can access it when the event gets triggered.
I ended up declaring my 2 grids as global variables and using the function below as event function :
var onSelectionChanged = function(event) {
let name = grid1.gridOptions.api.getSelectedRows()[0].name; // we know there is only one
let newRowData = grid1.gridOptions.rowData
.filter(x => x.name===name)
.map(x => {
return {
'name': x.name
// other fields...
}
})
// this overwrites grid2 data so need to save original data somewhere.
grid2.gridOptions.api.setRowData(newRowData);
grid2.gridOptions.api.refreshCells({force:true});
};