I'm trying to return the row data when a cell is clicked in ag-Grid. I have defined the following component that I pass into the cellRendererFramework shown further down.
import { ICellRendererParams } from "ag-grid-community";
import * as React from "react";
import { CellValue } from "./ClickableCell.style";
export default class ClickableCell extends React.Component<ICellRendererParams> {
public render() {
return (
// this works when the clickable cell is clicked
// but i'm trying to return this data to the component that will be rendering the grid
<div onClick={() => console.log(this.props.data)}>
{this.props.value}
</div>
);
}
}
The following is in the component that defines the column header and row data.
const headers = [
{ headerName: "Name", field: "name", cellRendererFramework: ClickableCell },
{ headerName: "Age", field: "age" },
];
How can I go about receiving the clicked row data in this component, where the headers are defined? Thanks!
EDIT:
I've added a somewhat working version of the code: https://codesandbox.io/s/7qlvyk901
Why don't you use ag-grids event handlers, in this case the onCellClicked event:
<AgGridReact
onCellClicked={this.onCellClicked}
columnDefs={this.props.Headers}
rowData={this.props.Data}
/>
Any cell event will provide you with the following parameters:
column: Column
colDef: ColDef
value: any
I haven't worked with Ag-Grid, but i would raise an event in ClickableCell and make the father handle that event.
export default class ClickableCell extends React.Component<ICellRendererParams> {
public render() {
return (
<div onClick={() => this.props.onEvent(this.props.data)}>
{this.props.value}
</div>
);
}
}
Then on the father component i would define the function to handle that event and save in the state for example.
handleEvent = data => {
console.log(data);
this.setState({ data });
};
And pass that function via props where you call that component. If it's only in that const you should pass the props as well no?
<ClickableCell data={this.state.data} value={?} onEvent={this.handleEvent)}
I am trying to create choice group with dynamic options. also I need to change the styles of each option. I tried it using classname but it was not success.Can some one help for this?
let numbers = [];
for(let i=0;i<10;i++){
numbers.push({
key:i,text:i,checked: false
});
}
<ChoiceGroup className="numbers" key="numbers" options={numbers} onChanged={this.onRecentMatterChanged}/>
The following example demonstrates how to generate options data for ChoiceGroup component and customize options styling via ChoiceGroup.styles prop:
const generateOptions = (count: number): IChoiceGroupOption[] => {
return Array.from(Array(count), (_, i) => {
return {
key: i.toString(),
text: `Option ${i}`,
checked: false
};
});
};
export default class ChoiceGroupBasicExample extends React.Component<{}, {}> {
private options: any[];
constructor(props: {}) {
super(props);
this.options = generateOptions(10);
}
public render() {
return (
<div>
<ChoiceGroup
className="defaultChoiceGroup"
defaultSelectedKey="4"
options={this.options}
label="Pick one"
required={true}
styles={{
flexContainer: [
{
backgroundColor: "#ADD8E6",
selectors: {
".ms-ChoiceField": {
color: "#00008B",
fontWeight: 600
}
}
}
]
}}
/>
</div>
);
}
}
Details:
flexContainer - the name of container for options
ms-ChoiceField - default class name for option container, via selectors property we override styles
Here is a demo for your reference
Follow Component Styling for a comprehensive guide on how to style components in Fabric
I'm using Typescript with React. I'm having trouble understanding how to use refs so as to get static typing and intellisense with respect to the react nodes referenced by the refs. My code is as follows.
import * as React from 'react';
interface AppState {
count: number;
}
interface AppProps {
steps: number;
}
interface AppRefs {
stepInput: HTMLInputElement;
}
export default class TestApp extends React.Component<AppProps, AppState> {
constructor(props: AppProps) {
super(props);
this.state = {
count: 0
};
}
incrementCounter() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div>
<h1>Hello World</h1>
<input type="text" ref="stepInput" />
<button onClick={() => this.incrementCounter()}>Increment</button>
Count : {this.state.count}
</div>
);
}}
If you’re using React 16.3+, the suggested way to create refs is using React.createRef().
class TestApp extends React.Component<AppProps, AppState> {
private stepInput: React.RefObject<HTMLInputElement>;
constructor(props) {
super(props);
this.stepInput = React.createRef();
}
render() {
return <input type="text" ref={this.stepInput} />;
}
}
When the component mounts, the ref attribute’s current property will be assigned to the referenced component/DOM element and assigned back to null when it unmounts. So, for example, you can access it using this.stepInput.current.
For more on RefObject, see #apieceofbart's answer or the PR createRef() was added in.
If you’re using an earlier version of React (<16.3) or need more fine-grained control over when refs are set and unset, you can use “callback refs”.
class TestApp extends React.Component<AppProps, AppState> {
private stepInput: HTMLInputElement;
constructor(props) {
super(props);
this.stepInput = null;
this.setStepInputRef = element => {
this.stepInput = element;
};
}
render() {
return <input type="text" ref={this.setStepInputRef} />
}
}
When the component mounts, React will call the ref callback with the DOM element, and will call it with null when it unmounts. So, for example, you can access it simply using this.stepInput.
By defining the ref callback as a bound method on the class as opposed to an inline function (as in a previous version of this answer), you can avoid the callback getting called twice during updates.
There used to be an API where the ref attribute was a string (see Akshar Patel's answer), but due to some issues, string refs are strongly discouraged and will eventually be removed.
Edited May 22, 2018 to add the new way of doing refs in React 16.3. Thanks #apieceofbart for pointing out that there was a new way.
React.createRef (class comp.)
class ClassApp extends React.Component {
inputRef = React.createRef<HTMLInputElement>();
render() {
return <input type="text" ref={this.inputRef} />
}
}
React.useRef (Hooks / function comp.)
a) Use readonly refs for React-managed DOM nodes:
const FunctionApp = () => {
// note the passed-in `null` arg ----------------v
const inputRef = React.useRef<HTMLInputElement>(null)
return <input type="text" ref={inputRef} />
}
inputRef.current becomes a readonly property by initializing its value with null.
b) Use mutable refs for arbitrary stored values akin to instance variables:
const FunctionApp = () => {
const renderCountRef = useRef(0)
useEffect(() => {
renderCountRef.current += 1
})
// ... other render code
}
Note: Don't initialize useRef with null in this case - it would make the renderCountRef type readonly (see example). If you need to provide null as initial value, do this:
const renderCountRef = useRef<number | null>(null)
Callback refs (both)
// Function component example, class analogue
const FunctionApp = () => {
const handleDomNodeChange = (domNode: HTMLInputElement | null) => {
// ... do something with changed dom node.
}
return <input type="text" ref={handleDomNodeChange} />
}
Note: String Refs are considered legacy and omitted for the scope of this answer.
Playground sample
One way (which I've been doing) is to setup manually :
refs: {
[string: string]: any;
stepInput:any;
}
then you can even wrap this up in a nicer getter function (e.g. here):
stepInput = (): HTMLInputElement => ReactDOM.findDOMNode(this.refs.stepInput);
Since React 16.3 the way to add refs is to use React.createRef as Jeff Bowen pointed in his answer. However you can take advantage of Typescript to better type your ref.
In your example you're using ref on input element. So they way I would do it is:
class SomeComponent extends React.Component<IProps, IState> {
private inputRef: React.RefObject<HTMLInputElement>;
constructor() {
...
this.inputRef = React.createRef();
}
...
render() {
<input type="text" ref={this.inputRef} />;
}
}
By doing this when you want to make use of that ref you have access to all input methods:
someMethod() {
this.inputRef.current.focus(); // 'current' is input node, autocompletion, yay!
}
You can use it on custom components as well:
private componentRef: React.RefObject<React.Component<IProps>>;
and then have, for example, access to props :
this.componentRef.current.props; // 'props' satisfy IProps interface
If you're using React.FC, add the HTMLDivElement interface:
const myRef = React.useRef<HTMLDivElement>(null);
And use it like the following:
return <div ref={myRef} />;
EDIT: This is no longer the right way to use refs with Typescript. Look at Jeff Bowen's answer and upvote it to increase its visibility.
Found the answer to the problem. Use refs as below inside the class.
refs: {
[key: string]: (Element);
stepInput: (HTMLInputElement);
}
Thanks #basarat for pointing in the right direction.
For those looking on how to do it when you have an array of elements:
const textInputRefs = useRef<(HTMLDivElement | null)[]>([])
...
const onClickFocus = (event: React.BaseSyntheticEvent, index: number) => {
textInputRefs.current[index]?.focus()
};
...
{items.map((item, index) => (
<textInput
inputRef={(ref) => textInputs.current[index] = ref}
/>
<Button
onClick={event => onClickFocus(event, index)}
/>
}
To use the callback style (https://facebook.github.io/react/docs/refs-and-the-dom.html) as recommended on React's documentation you can add a definition for a property on the class:
export class Foo extends React.Component<{}, {}> {
// You don't need to use 'references' as the name
references: {
// If you are using other components be more specific than HTMLInputElement
myRef: HTMLInputElement;
} = {
myRef: null
}
...
myFunction() {
// Use like this
this.references.myRef.focus();
}
...
render() {
return(<input ref={(i: any) => { this.references.myRef = i; }}/>)
}
For typescript user no constructor required.
...
private divRef: HTMLDivElement | null = null
getDivRef = (ref: HTMLDivElement | null): void => {
this.divRef = ref
}
render() {
return <div ref={this.getDivRef} />
}
...
If you wont to forward your ref, in Props interface you need to use RefObject<CmpType> type from import React, { RefObject } from 'react';
Lacking a complete example, here is my little test script for getting user input when working with React and TypeScript. Based partially on the other comments and this link https://medium.com/#basarat/strongly-typed-refs-for-react-typescript-9a07419f807#.cdrghertm
/// <reference path="typings/react/react-global.d.ts" />
// Init our code using jquery on document ready
$(function () {
ReactDOM.render(<ServerTime />, document.getElementById("reactTest"));
});
interface IServerTimeProps {
}
interface IServerTimeState {
time: string;
}
interface IServerTimeInputs {
userFormat?: HTMLInputElement;
}
class ServerTime extends React.Component<IServerTimeProps, IServerTimeState> {
inputs: IServerTimeInputs = {};
constructor() {
super();
this.state = { time: "unknown" }
}
render() {
return (
<div>
<div>Server time: { this.state.time }</div>
<input type="text" ref={ a => this.inputs.userFormat = a } defaultValue="s" ></input>
<button onClick={ this._buttonClick.bind(this) }>GetTime</button>
</div>
);
}
// Update state with value from server
_buttonClick(): void {
alert(`Format:${this.inputs.userFormat.value}`);
// This part requires a listening web server to work, but alert shows the user input
jQuery.ajax({
method: "POST",
data: { format: this.inputs.userFormat.value },
url: "/Home/ServerTime",
success: (result) => {
this.setState({ time : result });
}
});
}
}
From React type definition
type ReactInstance = Component<any, any> | Element;
....
refs: {
[key: string]: ReactInstance
};
So you can access you refs element as follow
stepInput = () => ReactDOM.findDOMNode(this.refs['stepInput']);
without redefinition of refs index.
As #manakor mentioned you can get error like
Property 'stepInput' does not exist on type '{ [key: string]:
Component | Element; }
if you redefine refs(depends on IDE and ts version you use)
Just to add a different approach - you can simply cast your ref, something like:
let myInputElement: Element = this.refs["myInput"] as Element
I always do this, in that case
to grab a ref
let input: HTMLInputElement = ReactDOM.findDOMNode<HTMLInputElement>(this.refs.input);
FIRST ADD AN IMPORT
import React, { useRef } from "react";
THEN THIS
const studentCapacityRef = useRef<HTMLInputElement>(null);
OR THIS
const studentCapacityRef = useRef<HTMLAreaElement>(null);
OR THIS
const studentCapacityRef = useRef<HTMLDivElement>(null);
etc...
class SelfFocusingInput extends React.Component<{ value: string, onChange: (value: string) => any }, {}>{
ctrls: {
input?: HTMLInputElement;
} = {};
render() {
return (
<input
ref={(input) => this.ctrls.input = input}
value={this.props.value}
onChange={(e) => { this.props.onChange(this.ctrls.input.value) } }
/>
);
}
componentDidMount() {
this.ctrls.input.focus();
}
}
put them in an object
Need to render a Custom React component array as Virtual Slides using Swiper.
Document says renderExternal can be used for this but there's no example in the API doc http://idangero.us/swiper/api/#virtual
Need to know on how this can be done using renderExternal function.
I'm not sure that's the best way to do it, but that is roughly how I did :
const mySwiper = new Swiper('.swiper-container', {
virtual: {
slides: this.props.array (or this.state.array)
renderExternal: function(data){}
}
})
then you display your component in the template :
<div className="swiper-container">
<div className="swiper-wrapper">
<MyComponent
myProps="mySwiper.virtual.slides[mySwiper.activeIndex]"
/>
</div>
</div>
(I did it with Vue, not React)
The documentation got updated and now you can find an example of how to use it with React. For reference, here is the example that is given:
import React from 'react';
import Swiper from 'swiper/dist/js/swiper.esm.bundle';
export default class extends React.Component {
constructor() {
this.state = {
// dummy slides data
slides: (function () {
var slides = [];
for (var i = 0; i < 600; i += 1) {
slides.push('Slide ' + (i + 1));
}
return slides;
}()),
// virtual data
virtualData: {
slides: [],
},
}
}
componentDidMount() {
const self = this;
const swiper = new Swiper('.swiper-container', {
// ...
virtual: {
slides: self.state.slides,
renderExternal(data) {
// assign virtual slides data
self.setState({
virtualData: data,
});
}
},
});
}
render() {
{/* ... */}
<div className="swiper-container">
<div className="swiper-wrapper">
{/* It is important to set "left" style prop on every slide */}
{this.state.virtualData.slides.map((slide, index) => (
<div className="swiper-slide"
key={index}
style={{left: `${virtualData.offset}px`}}
>{slide}</div>
))}
</div>
</div>
{/* ... */}
}
}
I want to wrap a primeng autocomplete component in my own component but can't figure out haw to provide a formControlName:
Error: Uncaught (in promise): Error: formControlName must be used with a parent formGroup directive.
Wrapper component html:
<p-autoComplete
[formControlName]="formControlName"
[suggestions]="suggestions"
[multiple]="multiple"
[dropdown]="dropdown"
(completeMethod)="search($event)">
</p-autoComplete>
Wrapper component ts:
#Component({
selector: 'logi-autocomplete',
templateUrl: 'autocomplete.component.html',
providers: [AUTOCOMPLETE_VALUE_ACCESSOR]
})
export class AutocompleteComponent implements OnInit, ControlValueAccessor {
#Input() formControlName: string;
#Input() multiple = true;
#Input() dropdown = true;
// Skipped non related code
_value: any = '';
get value(): any { return this._value; };
set value(v: any) {
if (v !== this._value) {
this._value = v;
this.onChange(v);
}
}
writeValue(value: any) {
this._value = value;
this.onChange(value);
}
onChange = (_) => {};
onTouched = () => {};
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}
Use of the component in a custom for component:
<form [formGroup]="form">
<logi-autocomplete
[formControlName]="'groups'"
></logi-autocomplete>
</form>
You have to write code in the HTML where you want to embed autocomplete.
`
<span class="ui-fluid">
<p-autoComplete formControlName="autoComplete"field="label"completeMethod)="search($event)">
</p-autoComplete>
</span>
<ul>
<li *ngFor="let s of autoComplete">{{s.label}}
</li>
</ul>
`
and write code in your component where you will pass the data to autocomplete widget.
autoComplete = new FormControl(null, []);search(event){this.receiptDiarizationFacade.getSubjects(event.query).subscribe(value=>this.autoComplete=value);
}
In this code snapshot, I used observable instead of promise but you can use promise