I have the following itemComponent to render currencies with icons:
TypeScriptimport { ReactElementFactory } from "survey-react-ui";
import React from "react";
import { ItemValue, ListModel } from "survey-core";
const CurrencyItemComponent: React.FC<{
item: ItemValue;
key: number;
model: ListModel;
}> = ({ item }) => {
return (
<div className="flex items-center gap-2">
<img
src={`https://hatscripts.github.io/circle-flags/flags/${item.isoCode.toLowerCase()}.svg`}
alt={item.title}
width={20}
height={20}
/>
<span>{item.title}</span>
</div>
);
};
export default CurrencyItemComponent;
ReactElementFactory.Instance.registerElement("currency-item", (props) => {
return React.createElement(CurrencyItemComponent, props);
});
Assuming I also have a custom dropdown component that is being used in the prop
JSONrenderAs: "dropdown-question"
This is the question definition in the schema:
Code{
"type": "dropdown",
"name": "crp-factory-yearly-turnover-currency-dropdown",
"width": "10%",
"maxWidth": "10%",
"renderAs": "dropdown-question",
"titleLocation": "hidden",
"startWithNewLine": false,
"itemComponent": "currency-item",
"choicesByUrl": {
"url": "our_url",
"valueName": "currency",
"titleName": "currency",
}
},
- What is the best way to access the rendered items of the itemComponent function from within the custom dropdown component?
- can I add custom props to "choicesByUrl", for example, "isoCode": "isoCode", so i can accees it from the itemComponent as item.isoCode?
Hello Amit,
You may use the Serializer API to register custom properties for the
itemvalue
class objects. You'll be able to access these custom properties within your custom item component asitem.yourCustomProp
.Unfortunately, I don't completely follow this requirement. Would you please elaborate your task in greater detail?
Thank you
Hey Jane, thank you for your answer.
After adding the custom prop for the itemvalue object like this:
Serializer.addProperty("itemvalue", { name: "isoCode", default: "", });
I then use it in our itemComponent like this:
import { ReactElementFactory } from "survey-react-ui"; import React from "react"; import { ItemValue, ListModel } from "survey-core"; const CurrencyItemComponent: React.FC<{ item: ItemValue; key: number; model: ListModel; }> = ({ item }) => { return ( <div className="flex items-center gap-2"> <img src={`https://hatscripts.github.io/circle-flags/flags/${item.isoCode.toLowerCase()}.svg`} alt={item.title} width={20} height={20} /> <span>{item.title}</span> </div> ); }; export default CurrencyItemComponent; ReactElementFactory.Instance.registerElement("currency-item", (props) => { return React.createElement(CurrencyItemComponent, props); });
Then we have a custom dropdown question that looks something like this:
import { ChangeEvent, FC, createElement, useCallback, useMemo, useState, } from "react"; import { QuestionDropdownModel, RendererFactory } from "survey-core"; import { ReactQuestionFactory } from "survey-react-ui"; import { Dropdown } from "../ui/dropdown"; import classNames from "classnames"; interface DropdownQuestionProps { isDisplayMode: boolean; question: QuestionDropdownModel; } const DropdownQuestion: FC<DropdownQuestionProps> = ({ isDisplayMode, question, }) => { const { value, isReadOnly, name, allowClear, isRequired, placeholder, otherItem, noneItem, hasOther, hasNone, isOtherSelected, commentPlaceHolder, } = question; const [textAreaInput, setTextAreaInput] = useState(question.comment); const isDisabled = useMemo(() => { return isReadOnly; }, [isReadOnly]); const onValueChange = useCallback( (newVal: string) => { question.value = newVal; }, [question] ); const onCommentChange = (event: ChangeEvent<HTMLTextAreaElement>) => { setTextAreaInput(event.target.value); }; const onCommentBlur = () => { question.comment = textAreaInput; }; const getItems = useMemo(() => { const items = question.dataChoices.map(({ title, value }) => ({ title, value, })); if (hasNone) items.push(noneItem); if (hasOther) items.push(otherItem); if (allowClear) items.unshift({ value: undefined, title: "" }); return items; }, [ question.dataChoices, hasNone, noneItem, hasOther, otherItem, allowClear, ]); return ( <div className="flex flex-col gap-2"> <Dropdown aria-required={question.ariaRequired} aria-label={question.ariaLabel} aria-invalid={question.ariaInvalid} aria-errormessage={question.ariaErrormessage} autoComplete={question.autocomplete} onClick={question.onClick} onKeyUp={question.onKeyUp} id={question.id} items={getItems} required={isRequired} disabled={isDisabled} value={value} name={name} dense onValueChange={onValueChange} placeholder={placeholder} defaultValue={question.getDefaultValue()} /> {isOtherSelected && ( <textarea disabled={isDisabled} onChange={onCommentChange} onBlur={onCommentBlur} className={classNames( "appearance-none static border-box bg-white w-full py-2 px-[14px] shadow-xs rounded-lg border border-gray-300 focus:text-gray-800 focus:border-indigo-500 focus:ring-4 focus:ring-indigo-100 outline-none disabled:bg-gray-50 disabled:text-gray-500 text-xs md:text-sm leading-[16px] md:leading-5 text-start font-regular text-gray-500 placeholder-gray-300" )} value={textAreaInput} placeholder={commentPlaceHolder} /> )} </div> ); }; ReactQuestionFactory.Instance.registerQuestion( "sv-dropdown-question", function (props: any) { return createElement(DropdownQuestion, props); } ); RendererFactory.Instance.registerRenderer( "dropdown", "dropdown-question", "sv-dropdown-question" );
As you can see, we use the question.dataChoices as the options values for the dropdown question but this just brings the array of values and titles. What I want is the ReactNodes of all the rendered options with the custom itemComponent.