I want to share my 3 approaches of handling cascading form fields.
This is 3rd approach and we are going to deal with dynamic form fields.
Note, it will be easier to understand this post if you take a look at the previous 2 approaches.
First approach, Cascade Form Basic
Second approach, Cascade form improved
Lets' begin,
This is static form page with 5 dropdown fields.
import React, { useState, useEffect } from "react"; import { ScrollView, View, Text, StyleSheet, TouchableOpacity } from "react-native"; import { Dropdown } from "react-native-element-dropdown"; import { Snackbar } from "react-native-paper"; var snackMsg = ""; export default function App() { const [refreshPage, setRefreshPage] = useState(false); const [visible, setVisible] = useState(false); const onToggleSnackBar = () => setVisible(!visible); const onDismissSnackBar = () => setVisible(false); const resetForm = () => { }; return ( <ScrollView> <p>refreshPage state variable is used to refresh the page in all the situations. </p> <p><img src="https://img.php.cn/upload/article/000/000/000/173363402014343.jpg" alt="Cascading Form React Native Advanced" /></p> <p>Now, These fields are going to be converted as dynamic. </p> <h3> Form Field Object </h3> <p>Previously we had 3 separate objects for 3 different fields, but here all fields data placed under one form field object.<br> </p> <pre class="brush:php;toolbar:false"> . . . const formFields = { country: { fieldId: "country", label: "Country", labelField: "name", valueField: "countryId", parents: [], list: [], selectedItem: {}, selectedValue: null, onValueSelected: () => null, }, state: { fieldId: "state",label: "State",labelField: "name",valueField: "stateId", parents: ["country"],list: [],selectedItem: {},selectedValue: null, onValueSelected: () => null, }, city: { fieldId: "city",label: "City",labelField: "name",valueField: "cityId", parents: ["country", "state"],list: [],selectedItem: {}, selectedValue: null,onValueSelected: () => null, }, village: { fieldId: "village",label: "Village",labelField: "name", valueField: "villageId", parents: ["country", "state", "city"], list: [],selectedItem: {},selectedValue: null, onValueSelected: () => null, }, street: { fieldId: "street",label: "Street",labelField: "name", valueField: "streetId", parents: ["country", "state", "city", "village"], list: [],selectedItem: {},selectedValue: null,onValueSelected: () => null, }, }; . . . export default function App() { . . . }
All these properties of a field have benefits, will be useful to handle dynamic rendering.
By iterating the form field object keys we render dropdown fields dynamically, all the required properties are available in the form field object.
export default function App() { . . . return ( <View> <p>handle focus / blur<br> </p> <pre class="brush:php;toolbar:false">var focusField = ""; export default function App() { . . . const changeFocusField = (fld = "") => { focusField = fld; setRefreshPage(!refreshPage); }; . . . }
<ZDropDown . . . isFocus={focusField === ele} onFocus={() => { changeFocusField(ele); }} onBlur={() => changeFocusField("")} onChange={(item) => {}} />
Sample data for fields country, state, city, village and street .
const listCountry = [ { countryId: "1", name: "india" }, { countryId: "2", name: "uk" }, { countryId: "3", name: "canada" }, { countryId: "4", name: "us" }, ]; const listSate = [ { stateId: "1", countryId: "1", name: "state1_india" }, { stateId: "4", countryId: "2", name: "state1_uk" }, { stateId: "7", countryId: "3", name: "state1_canada" }, { stateId: "10", countryId: "4", name: "state1_us" }, ]; const listCity = [ { cityId: "1", stateId: "1", countryId: "1", name: "city1_state1_country1" }, { cityId: "5", stateId: "2", countryId: "1", name: "city5_state2_country1" }, { cityId: "21",stateId: "7",countryId: "3",name:"city21_state7_country3", }, { cityId: "26",stateId: "9",countryId: "3",name: "city26_state9_country3", }, ]; const listVillage = [ { cityId: "1", villageId: "1", name: "village 1 city 1" }, { cityId: "2", villageId: "5", name: "village 5 city 2" }, { cityId: "3", villageId: "9", name: "village 9 city 3" }, { cityId: "4", villageId: "10", name: "village 10 city 4" }, ]; const listStreet = [ { villageId: "1", streetId: "1", name: "village 1 street 1" }, { villageId: "1", streetId: "109", name: "village 1 street 109" }, { villageId: "2", streetId: "2", name: "village 2 street 2" }, { villageId: "2", streetId: "110", name: "village 2 street 110" }, ]; . . . export default function App() { . . . } . . .
Firstly in the functionality side we have to setup some important things. Remember we assigned an empty method to the property onValueSelected, now it's time to assign the actual method. So we need to create 5 methods and assign them to the respective form field.
export default function App() { . . . const allValuesSelected = () => { console.log("All fields value selected"); }; const loadStreet = async () => {}; const loadVillage = async () => {}; const loadCity = async () => {}; const loadState = async () => {}; const loadCountry = async () => {}; const loadPageData = () => { formFields.country.onValueSelected = loadState; formFields.state.onValueSelected = loadCity; formFields.city.onValueSelected = loadVillage; formFields.village.onValueSelected = loadStreet; formFields.street.onValueSelected = allValuesSelected; }; return (. . .); }
When Country value selected, the STATE list has to be loaded, that's why here loadState method assigned to Country's onValueSelected. And likewise other methods assigned.
useEffect(() => { loadPageData(); }, []); return (. . .);
load country list from sample data and call it on initial page load.
const loadCountry = async () => { formFields.country.list = [...listCountry]; setRefreshPage(!refreshPage); }; const loadPageData = () => { formFields.country.onValueSelected = loadState; formFields.state.onValueSelected = loadCity; formFields.city.onValueSelected = loadVillage; formFields.village.onValueSelected = loadStreet; formFields.street.onValueSelected = allValuesSelected; loadCountry(); };
When dropdown field value selected, we need to setup the respective form field value, remove focus and load the next dropdown list.
return ( . . . <ZDropDown // . . . onChange={(item) => { fld.selectedItem = item; fld.selectedValue = item[fld.valueField]; focusField = ""; fld.onValueSelected(); }} /> . . . );
onValueSelected is useful right ?
When the first dropdown(country) changed, rest of the fields will be changed. So need to clear the list and data of all other form fields. For this we write a method which can clear the values from the given field to the end field.
import React, { useState, useEffect } from "react"; import { ScrollView, View, Text, StyleSheet, TouchableOpacity } from "react-native"; import { Dropdown } from "react-native-element-dropdown"; import { Snackbar } from "react-native-paper"; var snackMsg = ""; export default function App() { const [refreshPage, setRefreshPage] = useState(false); const [visible, setVisible] = useState(false); const onToggleSnackBar = () => setVisible(!visible); const onDismissSnackBar = () => setVisible(false); const resetForm = () => { }; return ( <ScrollView> <p>refreshPage state variable is used to refresh the page in all the situations. </p> <p><img src="https://img.php.cn/upload/article/000/000/000/173363402014343.jpg" alt="Cascading Form React Native Advanced" /></p> <p>Now, These fields are going to be converted as dynamic. </p> <h3> Form Field Object </h3> <p>Previously we had 3 separate objects for 3 different fields, but here all fields data placed under one form field object.<br> </p> <pre class="brush:php;toolbar:false"> . . . const formFields = { country: { fieldId: "country", label: "Country", labelField: "name", valueField: "countryId", parents: [], list: [], selectedItem: {}, selectedValue: null, onValueSelected: () => null, }, state: { fieldId: "state",label: "State",labelField: "name",valueField: "stateId", parents: ["country"],list: [],selectedItem: {},selectedValue: null, onValueSelected: () => null, }, city: { fieldId: "city",label: "City",labelField: "name",valueField: "cityId", parents: ["country", "state"],list: [],selectedItem: {}, selectedValue: null,onValueSelected: () => null, }, village: { fieldId: "village",label: "Village",labelField: "name", valueField: "villageId", parents: ["country", "state", "city"], list: [],selectedItem: {},selectedValue: null, onValueSelected: () => null, }, street: { fieldId: "street",label: "Street",labelField: "name", valueField: "streetId", parents: ["country", "state", "city", "village"], list: [],selectedItem: {},selectedValue: null,onValueSelected: () => null, }, }; . . . export default function App() { . . . }
This method can be used for all other dropdown fields and page resetting purpose.
export default function App() { . . . return ( <View> <p>handle focus / blur<br> </p> <pre class="brush:php;toolbar:false">var focusField = ""; export default function App() { . . . const changeFocusField = (fld = "") => { focusField = fld; setRefreshPage(!refreshPage); }; . . . }
State dropdown list is now perfectly loaded.
As like before we load data for rest of the fields.
<ZDropDown . . . isFocus={focusField === ele} onFocus={() => { changeFocusField(ele); }} onBlur={() => changeFocusField("")} onChange={(item) => {}} />
good, all the dropdowns populated with respective list.
Before showing the dropdown list we need to validate its parent field. So we are going to get the parent fields from the form field object. Then iterate them one by one, validate its value and show warning if necessary.
const listCountry = [ { countryId: "1", name: "india" }, { countryId: "2", name: "uk" }, { countryId: "3", name: "canada" }, { countryId: "4", name: "us" }, ]; const listSate = [ { stateId: "1", countryId: "1", name: "state1_india" }, { stateId: "4", countryId: "2", name: "state1_uk" }, { stateId: "7", countryId: "3", name: "state1_canada" }, { stateId: "10", countryId: "4", name: "state1_us" }, ]; const listCity = [ { cityId: "1", stateId: "1", countryId: "1", name: "city1_state1_country1" }, { cityId: "5", stateId: "2", countryId: "1", name: "city5_state2_country1" }, { cityId: "21",stateId: "7",countryId: "3",name:"city21_state7_country3", }, { cityId: "26",stateId: "9",countryId: "3",name: "city26_state9_country3", }, ]; const listVillage = [ { cityId: "1", villageId: "1", name: "village 1 city 1" }, { cityId: "2", villageId: "5", name: "village 5 city 2" }, { cityId: "3", villageId: "9", name: "village 9 city 3" }, { cityId: "4", villageId: "10", name: "village 10 city 4" }, ]; const listStreet = [ { villageId: "1", streetId: "1", name: "village 1 street 1" }, { villageId: "1", streetId: "109", name: "village 1 street 109" }, { villageId: "2", streetId: "2", name: "village 2 street 2" }, { villageId: "2", streetId: "110", name: "village 2 street 110" }, ]; . . . export default function App() { . . . } . . .
Finally, we provide option to reset the form fields.
export default function App() { . . . const allValuesSelected = () => { console.log("All fields value selected"); }; const loadStreet = async () => {}; const loadVillage = async () => {}; const loadCity = async () => {}; const loadState = async () => {}; const loadCountry = async () => {}; const loadPageData = () => { formFields.country.onValueSelected = loadState; formFields.state.onValueSelected = loadCity; formFields.city.onValueSelected = loadVillage; formFields.village.onValueSelected = loadStreet; formFields.street.onValueSelected = allValuesSelected; }; return (. . .); }
All Done. Now we saw how to deal with dynamic form fields, rendering fields, loading data and validating them.
These are the 3 approaches of mine to handle cascading form fields.
Hope this post/series has some useful stuffs which you like. Thank you.
Full code here
The above is the detailed content of Cascading Form React Native Advanced. For more information, please follow other related articles on the PHP Chinese website!