Why are my submitError's rendering on the second submit instead of the initial submit - forms

I wanted to create a reusable component that could render server side errors if there are validation errors in the form. On the initial submit even if there are errors coming from the server, they will not be displayed until the "submit" button is pressed again. After the initial submit the meta: {touched} is not changing to true.
Tools Utilized:
"react-final-form": "^6.5.3",
"#material-ui/core": "^4.12.4",
This is my first time asking a question so if more information is needed let me know and I will try and provide you with the necessary information.
I have tried to add an onBlur to the TextField which does change the touched to true but still does not submit the error on the initial submit. I have also tried to switch the && and || to see if that would change anything but it doesn't.
Reusable Form Component (input/index.js)
/* eslint-disable implicit-arrow-linebreak */
import React, { useEffect } from 'react';
import { Field } from 'react-final-form';
import {
MaterialTextField,
} from '#/shared/components/form/MaterialFormElements';
const TextField = ({
input, label, meta: { touched, error, submitError }, children, select, multiline, name, maxLength, value,
}) =>
(
<MaterialTextField
label={label}
type={input.type}
error={touched && (error || submitError)}
helperText={touched && (error || submitError)}
value={input.value || value}
select={select}
multiline={multiline}
defaultValue={input.defaultValue}
name={name}
onChange={(e) => {
e.preventDefault();
input.onChange(e.target.value);
}}
style={{ width: '100%' }}
inputProps={{ maxLength }}
>
{children}
</MaterialTextField>
);
TextField.defaultProps = {
meta: null,
select: false,
children: [],
multiline: false,
};
const validate = value => (value ? undefined : 'Required');
export function Input({
name, label, required, select, children, maxLength,
}) {
return (
<div style={{ width: '100%' }}>
<Field
name={name}
label={label}
select={!!select}
validate={required && validate}
maxLength={maxLength}
>
{props => (
<div>
<TextField {...props}>
{children}
</TextField>
</div>
)}
</Field>
</div>
);
}
CreateCustomerForm.jsx
import React from 'react';
import { Form } from 'react-final-form';
import {
MaterialFormContainer,
ButtonWrap,
} from '#/shared/components/form/MaterialFormElements';
import { Button } from '#/shared/components/Button';
import PropTypes from 'prop-types';
import { Input } from '../../Input';
const CreateCustomerForm = ({ onSubmit, toggle, setError }) => {
const handleClick = (event) => {
event.preventDefault();
setError(false);
toggle(prevState => !prevState);
};
const fields = [
{
name: 'customerName', label: 'Customer Name', required: true, maxLength: 45,
},
{
name: 'address', label: 'Address', required: true, maxLength: 150,
},
{
name: 'city', label: 'City', required: true, maxLength: 45,
},
{
name: 'state', label: 'State', required: true, maxLength: 45,
},
{
name: 'zip', label: 'Zip Code', required: true, maxLength: 10,
},
{
name: 'phone', label: 'Phone', required: true, maxLength: 20,
},
{
name: 'primaryContactName', label: 'Primary Contact Name', required: true, maxLength: 90,
},
{
name: 'primaryContactEmail', label: 'Primary Contact Email', required: true, maxLength: 255,
},
{
name: 'customerKey', label: 'Customer Key', required: true, maxLength: 255,
},
];
return (
<Form onSubmit={onSubmit}>
{({ handleSubmit }) => (
<MaterialFormContainer onSubmit={handleSubmit}>
{fields?.map(field => <Input {...field} />)}
<ButtonWrap>
<Button variant="primary" type="submit">Submit</Button>
<Button variant="secondary" type="button" onClick={handleClick}>
Cancel
</Button>
</ButtonWrap>
</MaterialFormContainer>
)}
</Form>
);
};
CreateCustomerForm.propTypes = {
onSubmit: PropTypes.func.isRequired,
toggle: PropTypes.func.isRequired,
setError: PropTypes.func.isRequired,
};
export default CreateCustomerForm;
createCustomerModal.jsx
import React, { useEffect, useState } from 'react';
import {
StyledModal, ModalHeader, ModalTitle, ModalCloseButton,
} from '#/shared/components/Modal';
import { useSelector, useDispatch } from 'react-redux';
import { Button } from '#/shared/components/Button';
import { createCustomer, toggleCustomerModal } from '../../../redux/actions/customer/customerActions';
import CreateCustomerForm from './CreateCustomerForm';
const CreateCustomer = () => {
const { toggleModal } = useSelector(state => state.customer);
const dispatch = useDispatch();
const [modal, setModal] = useState(toggleModal);
const [error, setError] = useState([]);
useEffect(() => {
setModal(toggleModal);
}, [toggleModal]);
const toggle = () => {
dispatch(toggleCustomerModal());
};
const handleSubmit = ({
customerName,
address,
city,
state,
zip,
phone,
primaryContactName,
primaryContactEmail,
customerKey,
}) => {
setError([]);
dispatch(createCustomer(
customerName,
address,
city,
state,
zip,
phone,
primaryContactName,
primaryContactEmail,
customerKey,
setError,
toggle,
));
if (error?.length > 0) {
let serverErr = {};
error?.forEach((err) => {
serverErr = {
...serverErr,
[err?.loc[1]]: err?.msg,
};
});
return serverErr;
}
return false;
};
return (
<div>
<Button variant="primary" onClick={toggle}>Create Customer</Button>
<StyledModal
show={modal}
onHide={toggle}
color="primary"
>
<ModalHeader className="mb-1">
<ModalCloseButton
className="lnr lnr-cross"
aria-label="close-btn"
type="button"
onClick={toggle}
/>
<ModalTitle>Create Customer</ModalTitle>
</ModalHeader>
<CreateCustomerForm onSubmit={handleSubmit} toggle={toggle} setError={setError} />
</StyledModal>
</div>
);
};
export default CreateCustomer;
CustomerPage.jsx (customer/index.jsx)
import React, { useEffect, useState } from 'react';
import {
StyledModal, ModalHeader, ModalTitle, ModalCloseButton,
} from '#/shared/components/Modal';
import { useSelector, useDispatch } from 'react-redux';
import { Button } from '#/shared/components/Button';
import { createCustomer, toggleCustomerModal } from '../../../redux/actions/customer/customerActions';
import CreateCustomerForm from './CreateCustomerForm';
const CreateCustomer = () => {
const { toggleModal } = useSelector(state => state.customer);
const dispatch = useDispatch();
const [modal, setModal] = useState(toggleModal);
const [error, setError] = useState([]);
useEffect(() => {
setModal(toggleModal);
}, [toggleModal]);
const toggle = () => {
dispatch(toggleCustomerModal());
};
const handleSubmit = ({
customerName,
address,
city,
state,
zip,
phone,
primaryContactName,
primaryContactEmail,
customerKey,
}) => {
setError([]);
dispatch(createCustomer(
customerName,
address,
city,
state,
zip,
phone,
primaryContactName,
primaryContactEmail,
customerKey,
setError,
toggle,
));
if (error?.length > 0) {
let serverErr = {};
error?.forEach((err) => {
serverErr = {
...serverErr,
[err?.loc[1]]: err?.msg,
};
});
return serverErr;
}
return false;
};
return (
<div>
<Button variant="primary" onClick={toggle}>Create Customer</Button>
<StyledModal
show={modal}
onHide={toggle}
color="primary"
>
<ModalHeader className="mb-1">
<ModalCloseButton
className="lnr lnr-cross"
aria-label="close-btn"
type="button"
onClick={toggle}
/>
<ModalTitle>Create Customer</ModalTitle>
</ModalHeader>
<CreateCustomerForm onSubmit={handleSubmit} toggle={toggle} setError={setError} />
</StyledModal>
</div>
);
};
export default CreateCustomer;
package.json
{
"name": "stnportal",
"version": "2.5.0",
"private": true,
"scripts": {
"git-info": "node src/gitInfo.js",
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"lint:css": "stylelint './src/**/*.js'",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
"dependencies": {
"#material-ui/core": "^4.12.4",
"#material-ui/icons": "^4.11.3",
"#material-ui/lab": "^4.0.0-alpha.61",
"#mdi/js": "^7.0.96",
"#mdi/react": "^1.6.1",
"#tanstack/react-query": "^4.22.0",
"#tanstack/react-query-devtools": "^4.22.0",
"axios": "^0.21.4",
"bootstrap": "^5.1.1",
"clsx": "^1.2.1",
"customize-cra": "^1.0.0",
"dotenv": "^16.0.1",
"draft-js": "^0.11.7",
"draftjs-to-html": "^0.9.1",
"final-form": "^4.20.2",
"mdi-react": "^7.4.0",
"moment": "^2.29.4",
"polished": "^4.2.2",
"prop-types": "^15.7.2",
"rc-notification": "^4.5.4",
"rc-time-picker": "^3.7.3",
"react": "^17.0.1",
"react-avatar-editor": "^13.0.0",
"react-beautiful-dnd": "^13.1.0",
"react-big-calendar": "^0.35.0",
"react-bootstrap": "^2.2.1",
"react-datepicker": "^3.7.0",
"react-device-detect": "^1.15.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^17.0.1",
"react-draft-wysiwyg": "^1.14.7",
"react-dropzone": "^11.2.4",
"react-final-form": "^6.5.3",
"react-final-form-arrays": "^3.1.4",
"react-folder-tree": "^5.0.3",
"react-highlight-words": "^0.18.0",
"react-hook-form": "^7.9.0",
"react-html-parser": "^2.0.2",
"react-i18next": "^11.8.4",
"react-icons": "^4.4.0",
"react-qr-code": "^2.0.7",
"react-redux": "^8.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.1",
"react-select": "^5.4.0",
"react-smooth-scrollbar": "^8.0.6",
"react-table": "^7.6.3",
"react-text-mask": "^5.4.3",
"react-toastify": "^8.2.0",
"recharts": "^2.1.2",
"redux": "^4.0.5",
"redux-actions": "^2.6.5",
"redux-persist": "^6.0.0",
"redux-thunk": "^2.3.0",
"smooth-scrollbar": "^8.5.3",
"styled-components": "^5.3.5",
"styled-theming": "^2.2.0"
},
"resolutions": {
"styled-components": "^5",
"#mui/styled-engine": "npm:#mui/styled-engine-sc#latest",
"react-error-overlay": "6.0.9"
},
"devDependencies": {
"#testing-library/jest-dom": "^4.2.4",
"#testing-library/react": "^9.3.2",
"#testing-library/user-event": "^7.1.2",
"eslint-config-airbnb": "^18.2.1",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-import-resolver-node": "^0.3.4",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-react-hooks": "^5.0.0-next-d1bb1c586-20220922",
"react-app-rewired": "^2.1.8",
"react-error-overlay": "6.0.9",
"vitest": "^0.25.2"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Related

Display all row instead of 3 row

Goal:
Display all row in the in table at the same time.
Problem:
It display only 3 row at the same time in the table.
I would like to display all row at the same time without any limitation.
It doesn't work to use "height: '100%'"
Any idea?
Codesandbox:
https://codesandbox.io/s/mkd4dw?file=/demo.tsx
Thank you!
demo.tsx
import * as React from 'react';
import Box from '#mui/material/Box';
import Rating from '#mui/material/Rating';
import {
DataGrid,
GridRenderCellParams,
GridColDef,
useGridApiContext,
} from '#mui/x-data-grid';
function renderRating(params: GridRenderCellParams<number>) {
return <Rating readOnly value={params.value} />;
}
function RatingEditInputCell(props: GridRenderCellParams<number>) {
const { id, value, field } = props;
const apiRef = useGridApiContext();
const handleChange = (event: React.SyntheticEvent, newValue: number | null) => {
apiRef.current.setEditCellValue({ id, field, value: newValue });
};
const handleRef = (element: HTMLSpanElement) => {
if (element) {
const input = element.querySelector<HTMLInputElement>(
`input[value="${value}"]`,
);
input?.focus();
}
};
return (
<Box sx={{ display: 'flex', alignItems: 'center', pr: 2 }}>
<Rating
ref={handleRef}
name="rating"
precision={1}
value={value}
onChange={handleChange}
/>
</Box>
);
}
const renderRatingEditInputCell: GridColDef['renderCell'] = (params) => {
return <RatingEditInputCell {...params} />;
};
export default function CustomEditComponent() {
return (
<div style={{ height: 250, width: '100%' }}>
<DataGrid
rows={rows}
columns={columns}
experimentalFeatures={{ newEditingApi: true }}
/>
</div>
);
}
const columns = [
{
field: 'places',
headerName: 'Places',
width: 120,
},
{
field: 'rating',
headerName: 'Rating',
renderCell: renderRating,
renderEditCell: renderRatingEditInputCell,
editable: true,
width: 180,
type: 'number',
},
];
const rows = [
{ id: 1, places: 'Barcelona', rating: 5 },
{ id: 2, places: 'Rio de Janeiro', rating: 4 },
{ id: 3, places: 'London', rating: 3 },
{ id: 4, places: 'New York', rating: 2 },
];
You need to make use of autoHeight prop supported by the <DataGrid /> component, update your DataGrid component usage to this:
<DataGrid
autoHeight
rows={rows}
columns={columns}
experimentalFeatures={{ newEditingApi: true }}
/>
Reference: https://mui.com/x/react-data-grid/layout/#auto-height

How to remove background when click on material-table action buttons

I want to remove this background grey color when switching on or off in Material-Table.
Here is my table code.
Here is the code for material-table.When I inspect the code I see a button as a parent element of this Switch element. I am pretty sure this is due to that button element. I added a styling to remove the hover color and It's working fine but for this click event I am unable to change it.
here is the code for removing hover styling:
rootTable: {
"& .MuiIconButton-root": {
backgroundColor:'transparent',
},
},
<div className={classes.rootTable}>
<MaterialTable
options={{
paging: false,
addRowPosition: 'first',
search: true,
showTitle: false,
doubleHorizontalScroll: true,
minBodyHeight: '65vh',
maxBodyHeight: '68vh',
actionsColumnIndex: columns.length
}}
style={{ width: '100%' }}
actions={[
//rowData => ({
// icon: (rowData.companyAddress && rowData.companyAddress != null) ? "location_on" : "location_off",
// tooltip: "Update Address",
// onClick: (event, rowData) => dispatch(Actions.setSelectedCompanyData(rowData))
// })
rowData => ({
icon:()=>RenderSwitch(rowData),
tooltip: "Supprimer cet accès Net",
}),
]}
components={{
Toolbar: props => {
const propsCopy = { ...props };
propsCopy.showTitle = false;
return (
<Grid container direction="row" alignItems={"center"}>
<Grid container item xs={3} justify={'flex-end'}>
{t('LBL.NUMBER_OF_LOGINS')+": "+ data.length}
</Grid>
<Grid container item xs={3} justify={'flex-end'}>
{renderPasswordVisibleToggle()}
</Grid>
<Grid item xs={6}>
<MTableToolbar {...propsCopy} />
</Grid>
</Grid>
);
}
}}
isLoading={loading}
localization={{
pagination: {
labelDisplayedRows: `{from}-{to} ${t('TBL.PAGGING.TO')} {count}`,
labelRowsPerPage: t('TBL.PAGGING.LBL')
},
header: {
actions: ['Actions']
},
body: {
emptyDataSourceMessage: t('TBL.EMPTY_RECORDS_MESSAGE'),
addTooltip: t('TBL.ADD'),
editTooltip: t('TBL.EDIT'),
editRow: {
deleteText: 'Voulez-vous supprimer cette ligne?',
cancelTooltip: t('TBL.CANCEL'),
saveTooltip: t('TBL.SAVE')
}
},
toolbar: {
searchTooltip: t('TBL.SEARCH'),
searchPlaceholder: t('TBL.SEARCH')
}
}}
columns={columns.map(column => {
column.title = t(column.translate);
return column;
})}
data={data}
editable={{
onRowAdd: !loggedUser.role.includes("ROLE_SUB_USER") ? (newData) =>
new Promise((resolve, reject) => {
newData.submitted = true;
if (newData.siret.length!==14) {
setNameError({
error: true,
label: "required",
helperText: t('LBL.INVALID_COMPANY_ID') ,
validateInput: true,
});
reject();
newData.submitted = undefined;
return;
}
newData.submitted = undefined;
if (isDevelopmentEnvironment || newData?.societe?.includes('test-qa')){
dispatch(Actions.saveNewLogin(newData,resolve));
}else {
const url = apiConfig.baseUrl + '/api/company-login/check-login-status';
axios
.post(url, newData, { validateStatus: () => true })
.then(response => {
if (response?.data?.validLogin) {
setCreateData(newData);
setTimeout(() => {
resolve();
}, 600);
} else {
dispatch(MessageActions.showMessage({
message: t("LBL.ERROR_MESSAGE"),
variant: 'error'
}));
reject();
}
})
.catch(error => {
console.error(error);
reject();
});
}
}) : null,
onRowUpdate: newData => {
return new Promise(resolve => {
dispatch(Actions.updateCompanyLogin(newData,resolve));
})
}
}}
icons={{
Add: forwardRef((props, ref) => (
<Fab ref={ref} {...props} className={classes.iconButton} aria-label="add">
<AddIcon />
</Fab>
))
}}
/>
</div>

Search bar in React Native not updating or filtering

I'm trying to set up data filtering by SearchBar from react-native-elements. I'm returning some data from the server in a JSON format so it arrives in this form of an Array of Objects.
This is what I have so far:
export default function AktList() {
const [akt, setAkt] = useState([]);
const [temp, setTemp] = useState([]);
async function request() {
fetch("http://192.168.5.12:5000/aktprikaz", {
method: "get"
})
.then(res => res.json())
.then(res => setAkt(res))
.then(temp => setTemp(akt));
}
useEffect(() => {
request();
}, []);
function Item({ title, selected }) {
return (
<TouchableOpacity
onPress={() => console.log(temp)}
style={[
styles.item,
{ backgroundColor: selected ? "#6e3b6e" : "#f9c2ff" }
]}
>
<Text style={styles.title}>{title}</Text>
</TouchableOpacity>
);
}
function contains(text) {
const newData = temp.filter(item => {
const itemData = {title};
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setAkt(newData);
}
return (
<SafeAreaView style={styles.container}>
<Header />
<SearchBar onChangeText={text => contains(text)} />
<FlatList
data={akt}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
Currently, nor is the text updating nor is it filter anything. I've tried following this tutorial online (tho it is written using classes not functions). What am I doing wrong here?
Check below example which i created using flatlist and TextInput. Items are displayed in the form of a dropdown list when you search items. i think this will help you.
import React, { Component } from 'react';
import { View, Text, FlatList, TextInput, ListItem } from 'react-native';
class FlatListDropDown extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
value: '',
};
this.arrayNew = [
{ name: 'Robert' },
{ name: 'Bryan' },
{ name: 'Vicente' },
{ name: 'Tristan' },
{ name: 'Marie' },
{ name: 'Onni' },
{ name: 'sophie' },
{ name: 'Brad' },
{ name: 'Samual' },
{ name: 'Omur' },
{ name: 'Ower' },
{ name: 'Awery' },
{ name: 'Ann' },
{ name: 'Jhone' },
{ name: 'z' },
{ name: 'bb' },
{ name: 'cc' },
{ name: 'd' },
{ name: 'e' },
{ name: 'f' },
{ name: 'g' },
{ name: 'h' },
{ name: 'i' },
{ name: 'j' },
{ name: 'k' },
{ name: 'l' },
];
}
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '100%',
backgroundColor: '#CED0CE',
}}
/>
);
};
searchItems = text => {
const newData = this.arrayNew.filter(item => {
const itemData = `${item.name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
value: text,
});
};
renderHeader = () => {
return (
<TextInput
style={{ height: 60, borderColor: '#000', borderWidth: 1 }}
placeholder=" Type Here...Key word"
onChangeText={text => this.searchItems(text)}
value={this.state.value}
/>
);
};
render() {
return (
<View
style={{
flex: 1,
padding: 25,
width: '98%',
alignSelf: 'center',
justifyContent: 'center',
}}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<Text style={{ padding: 10 }}>{item.name} </Text>
)}
keyExtractor={item => item.name}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
export default FlatListDropDown;
Feel free for doubts.

ag-grid data for detailCellRendererParams not displaying

I'm trying to add a nested grid inside an existing grid. I'm very new at this, and although I'm able to display the grid, I cannot seem to display the data. When I do console.log(params), I have all my data, but no matter what I try, I cannot display the data. I think the problem is with my getDetailRowData, and what I'm passing to the successCallback function. Any ideas?
this is my component ts file
import { Component, OnInit } from '#angular/core';
import { ReportService } from 'src/app/services/report.service';
import { Report } from 'src/app/models/report';
import { agThemeSgBootstrap } from "#sg-bootstrap/ag-grid";
import {GridOptions, Module} from 'ag-grid-community';
import { DatePipe } from '#angular/common';
#Component({
selector: 'app-report',
templateUrl: './report.component.html',
styleUrls: ['./report.component.scss']
})
export class ReportComponent implements OnInit {
apiUrl: any;
reports : Report[];
gridOptions: GridOptions;
dateValue = new Date();
maxDate = new Date();
private detailCellRendererParams;
private columnDefs;
// public modules: Module[] = AllModules;
constructor(private reportService : ReportService, private datePipe: DatePipe) {
this.columnDefs = [
{headerName: 'Report Name', field: 'reportName', cellRenderer: 'agGroupCellRenderer',
resizable: true,
valueGetter: params => { return `${params.data.reportName}.dat` }},
{headerName: 'Sent to FIS', field: 'sentToFis',resizable: false,
valueGetter: params => { return params.data.sentToFis === true ? "Yes" : "No" } },
{headerName: 'File Size', field: 'contentLength', resizable: true,
valueGetter: params => { return `${this.formatBytes(params.data.contentLength)}` }},
{headerName: 'Last Modified', field: 'lastModified', resizable: true,
valueGetter: params => { return this.datePipe.transform(params.data.lastModified, "yyyy-MM-dd HH:mm:ss") }},
];
this.detailCellRendererParams = {
detailGridOptions: {
columnDefs: [
{headerName: 'ADSL Path', field: 'adslPath', resizable: true,
valueGetter: params => { return params.data.adlsFullPath}},
{headerName: 'Version', field: 'version', resizable: true,
valueGetter: params => { return params.data.sentToFis}},
{headerName: 'Source', field: 'source', resizable: true,
valueGetter: params => { return params.data.adlsFullPath}},
],
},
getDetailRowData: function(params) {
params.successCallback(params.data.children);
console.log(params.data);
}
}
}
ngOnInit() {
this.callReportService(new Date())
}
reportDateChange(value: Date) {
let currentValue = this.datePipe.transform(this.dateValue, "yyyyMMdd")
let newSelectedValue = this.datePipe.transform(value, "yyyyMMdd")
if (currentValue != newSelectedValue) {
if (this.gridOptions.api) {
this.gridOptions.api.showLoadingOverlay();
}
this.callReportService(value)
this.dateValue = value;
}
}
callReportService(value: Date) {
this.reportService.getReportsForDate(value).subscribe(x=> {
this.reports = x;
this.gridOptions.api.sizeColumnsToFit();
})
}
ngAfterContentInit() {
let agOpt = { ...{
animateRows: true,
enableRangeSelection: true,
defaultColDef: {
editable: false,
enableValue: false,
enableRowGroup: false,
enablePivot: true,
filter: true,
sortable: true
},
statusBar: {
statusPanels: [{
statusPanel: 'agTotalRowCountComponent',
align: 'left'
},
{
statusPanel: 'agFilteredRowCountComponent'
},
{
statusPanel: 'agSelectedRowCountComponent'
},
{
statusPanel: 'agAggregationComponent'
},
],
}
}, ...agThemeSgBootstrap }
this.gridOptions = { ...this.gridOptions, ...agOpt }
}
onGridReady(params) {
params.api.setDomLayout("autoHeight");
params.api.sizeColumnsToFit();
}
onGridSizeChanged(params) {
params.api.sizeColumnsToFit();
}
ngAfterViewInit() {
this.dateValue = new Date()
}
formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
}
my html file
<div class="container-fluid">
<div class="col-xs-12 col-12 col-md-3 form-group row">
<label for="reportDate" class="col-form-label">Report Date:</label>
<div class="col-sm-4 col-md-7">
<input id="reportDate"
class="form-control"
#dp="bsDatepicker"
(bsValueChange)="reportDateChange($event)"
[maxDate]="maxDate"
bsDatepicker
[bsValue]="dateValue" [bsConfig]="{ isAnimated: true, adaptivePosition: true, dateInputFormat: 'YYYY-MM-DD', containerClass: 'theme-red' }"/>
</div>
</div>
<div class="row">
<div class="col">
<ag-grid-angular
style="width: 100%; height: 300px;"
class="ag-theme-sg-bootstrap"
[gridOptions]="gridOptions"
[masterDetail]="true"
[detailCellRendererParams]="detailCellRendererParams"
[rowData]="reports"
[columnDefs]="columnDefs"
(gridReady)="onGridReady($event)"
(gridSizeChanged)="onGridSizeChanged($event)">
</ag-grid-angular>
</div>
</div>
</div>

Ag-Grid, React, Redux Filters Issue - can not set setFilterModel()

Here is my scenario,
I am making a onFetchEvents Redux Action call and getting all events and passing it as follows. rowData={this.props.events.data}
Everything works fine. Now I need to apply a filter, and this requires another call to the server.
I set enableServerSideFilter: true, so that when a filter applied, datagrid does not make a call on its own.
I monitor filter changes and call handleFilterChanged. I get the const filterModel = this.gridApi.getFilterModel(); and send it to rest endpoint.
Everything works fine but the datagrid does not remember the filter state.
I tried this.gridApi.setFilterModel(filterModel); but it gets me into infinite loop.
To make the long story short, how can I use filters with Redux so that I have full control also where should I use this.gridApi.setFilterModel()
Let me know if you need more clarification.
Thanks
/* eslint-disable */
import React, { Component, Fragment } from 'react';
import { connect } from 'react-redux';
import { AgGridReact } from 'ag-grid-react';
import { fetchEvents } from '#mc/duck/actions/AppActions';
import 'ag-grid-enterprise';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
// import './customTheme.scss';
// col resizing pipe is not visibles
class DatagridPage extends Component {
constructor(props) {
super(props);
this.state = {
params: {
filterModel: {},
page: 1,
limit: 10,
},
gridOptions: {
enableServerSideFilter: true,
enableSorting: true,
enableColResize: true,
suppressMenuHide: true,
pagination: true,
animateRows: true,
onFilterChanged: this.handleFilterChanged,
onSortChanged: () => console.log('onSortChanged'),
columnDefs: [
{
headerName: 'id',
field: 'id',
headerClass: 'test',
checkboxSelection: true,
filter: 'agTextColumnFilter',
},
{
headerName: 'name',
field: 'name',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'risk score',
field: 'risk_score',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'urgency',
field: 'urgency',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{ headerName: 'type', field: 'type', headerClass: 'test', filter: 'agTextColumnFilter' },
{
headerName: 'artifacts',
field: 'artifacts',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'created',
field: 'created',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'updated',
field: 'updated',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{ headerName: 'due', field: 'due', headerClass: 'test', filter: 'agTextColumnFilter' },
{
headerName: 'owner',
field: 'owner',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'status',
field: 'status',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
{
headerName: 'description',
field: 'description',
headerClass: 'test',
filter: 'agTextColumnFilter',
},
],
},
};
}
componentDidMount() {
console.log('componentDidMount');
const { params } = this.state;
this.props.onFetchEvents(params);
}
onGridReady = params => {
this.gridApi = params.api;
this.gridColumnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
console.log('onGridReady', this.gridApi.getFilterModel());
};
onPageSizeChanged(newPageSize) {
var value = document.getElementById('page-size').value;
const paramsCopy = { ...this.state.params };
paramsCopy.limit = Number(value);
paramsCopy.page = 1;
this.setState({ params: paramsCopy }, () => this.props.onFetchEvents(paramsCopy));
this.gridApi.paginationSetPageSize(Number(value));
}
handleFilterChanged = () => {
console.log('handleFilterChanged');
const filterModel = this.gridApi.getFilterModel();
const paramsCopy = { ...this.state.params, filterModel };
console.group('handleFilterChanged');
console.log('filterModel', filterModel);
console.log('state filterModel', this.state.params.filterModel);
if (!areEqualShallow(filterModel, this.state.params.filterModel)) {
this.gridApi.setFilterModel(filterModel);
this.setState({ params: paramsCopy }, () => this.props.onFetchEvents(paramsCopy));
}
console.groupEnd();
function areEqualShallow(a, b) {
for (var key in a) {
if (!(key in b) || a[key] !== b[key]) {
return false;
}
}
for (var key in b) {
if (!(key in a) || a[key] !== b[key]) {
return false;
}
}
return true;
}
// this.gridApi.setFilterModel(filterModel);
};
render() {
return (
<Fragment>
<div>
Page Size:
<select onChange={this.onPageSizeChanged.bind(this)} id="page-size">
<option value="10">10</option>
<option value="100">100</option>
<option value="500">500</option>
<option value="1000">1000</option>
</select>
</div>
<div
className="ag-theme-balham-dark"
style={{
height: '500px',
width: '100%',
}}
>
<AgGridReact
rowSelection="multiple"
gridOptions={this.state.gridOptions}
columnDefs={this.state.columnDefs}
rowData={this.props.events.data}
defaultColDef={this.state.defaultColDef}
onGridReady={this.onGridReady}
rowHeight={49}
/>
</div>
</Fragment>
);
}
}
const mapStateToProps = state => ({
events: state.app.events,
});
const mapDispatchToProps = {
onFetchEvents: params => fetchEvents(params),
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(DatagridPage);
In your column definition try setting the newRowsAction to "keep", e.g.
{
headerName: 'name',
field: 'name',
headerClass: 'test',
filter: 'agTextColumnFilter',
filterParams: {
newRowsAction: 'keep'
}
}
Or set it for all via defaultColDef, e.g.
gridOptions: {
defaultColDef: {
filterParams: {
newRowsAction: 'keep'
}
}
}