flutter_form_builder icon indicating copy to clipboard operation
flutter_form_builder copied to clipboard

Access nested value of initialValues via attribute

Open vasilich6107 opened this issue 5 years ago • 13 comments

Hi. Thanks for your work.

Is there any chance to have possibility to access nested value in initialValues through the attribute field?

For example if I have deeply nested initialValues I would like to have possibility to set attribute like

items.0.some.value

vasilich6107 avatar Mar 05 '20 10:03 vasilich6107

Hi @vasilich6107, I'm glad you find this package useful.

I haven't considered this yet, sounds like an interesting idea worth trying but maybe a bit challenging to implement.

danvick avatar Mar 09 '20 19:03 danvick

Would be awsome to have this! Maybe have a look into sembast package could help you to implement it. There is a implementation like that for order and filter in a query :) https://github.com/tekartik/sembast.dart/blob/master/sembast/lib/src/api/field.dart https://github.com/tekartik/sembast.dart/blob/master/sembast/doc/queries.md#sortingquerying-on-nested-field

guenth39 avatar Jun 08 '20 12:06 guenth39

I am trying to implement this, guide me if I am on the right track. I have written two methods

  1. Extracts the nested value from the map.
  2. Generates the map according to the given attribute.

Works fine for me, any suggestions?

dynamic getInitialValue(String attribute, dynamic initialValue) {
   var props = attribute.split('.');

   //if property is not nested return its value from map
   if (props.length < 2)
     return (initialValue?.containsKey(attribute) ?? false)
         ? initialValue[attribute]
         : null;

   //iterate to find the nested value

   //to track if current value is a Map or a List
   var isMap = initialValue is Map;

   //holds the map values
   Map<String, dynamic> map = isMap ? initialValue : null;

   //holds the list values
   List list = isMap ? null : initialValue;

   //loop all props
   for (var i = 0; i < props.length - 1; i++) {
     //if prop is key for list we need to convert it to INT
     var key = isMap ? props[i] : int.parse(props[i]);

     //if the next prop is a Map type then assign it to the map variable
     if ((isMap && map[key] is Map) || (!isMap && list[key] is Map)) {
       map = isMap ? map[key] : list[key];
       //this tells if the current value is stored in the map or list variable
       isMap = true;
     }
     //else of its a List type then assign it to the list varaible
     else if ((isMap && map[key] is List) || (!isMap && list[key] is List)) {
       list = isMap ? map[key] : list[key];
       //this tells if the current value is stored in the map or list variable
       isMap = false;
     }
   }

   return isMap ? map[props.last] : list[int.parse(props.last)];
 }

 //recursively generates the map accroding to the attribute
 dynamic getMap(List<String> props, dynamic value, dynamic map) {
   if (props.isEmpty) return value;

   if (props.length == 1) return {...map ?? {}, props.single: value};

   //if the next prop is an integer then we have a list
   var key = int.tryParse(props[1]);
   if (key != null) {
     var list = map == null ? [] : [...(map[props.first] ?? {})];
     //if list is shorter than the provided index append null objects to list till we have the requred length of list
     if (list.length <= key)
       list.addAll(List.generate(key + 1 - list.length, (index) => null));

     list[key] = getMap(props.sublist(2), value, list[key]);
     return {...map ?? {}, props.first: list};
   } else {
     return {
       ...map ?? {},
       props.first:
           getMap(props.sublist(1), value, map == null ? {} : map[props.first])
     };
   }
 }

 Map<String, dynamic> initialValue = {
   "prop1": {
     "list": [
       {"key1": "value1"},
       {"key2": "value2"},
       {"key3": "value3"},
       "value4"
     ]
   },
   "prop2" : {"prop3" : {"prop4": "prop4 Value"}}
 };

 print(getInitialValue("prop1.list.0.key1", initialValue));
 print(getInitialValue("prop2.prop3.prop4", initialValue));

 print(getMap("prop1.list.0.key1".split('.'), "new Value 1", initialValue));
 print(getMap("prop2.prop3.prop4".split('.'), "new porp4 Value", initialValue));

AbdulRafaySiddiqui avatar Oct 03 '20 22:10 AbdulRafaySiddiqui

Any chance this would be implemented or any alternative solution?

ekremkenter avatar Apr 20 '22 07:04 ekremkenter

I ended up flattening the map before assigning as initial value, and use innerObject.field as field name. I am assuming we can do that flattening inside framework.

ekremkenter avatar Apr 20 '22 08:04 ekremkenter

Hi! If someone has a solution for this, please feel free to open a PR. Thanks!

deandreamatias avatar Jun 24 '22 15:06 deandreamatias

Consider nested FormBuilder, like this



FormBuilder(
// ...
  FormBuilderTextField(
    name: 'foo', 
    // ...
  ),
  NestedFormBuilder(  // new class
    name: 'inner',
    // ...
    FormBuilderTextField(
      name: 'bar', 
      // ...
    ),
  ),
// ... 
)

value example:

{
  "foo": "some input value",
   "inner":{
      "bar" : "some inner input value"
    }
}

Hu-Wentao avatar Nov 15 '22 02:11 Hu-Wentao

Consider nested FormBuilder, like this

FormBuilder(
// ...
  FormBuilderTextField(
    name: 'foo', 
    // ...
  ),
  NestedFormBuilder(  // new class
    name: 'inner',
    // ...
    FormBuilderTextField(
      name: 'bar', 
      // ...
    ),
  ),
// ... 
)

value example:

{
  "foo": "some input value",
   "inner":{
      "bar" : "some inner input value"
    }
}

it work for me, I will PR soon @deandreamatias

Hu-Wentao avatar Nov 17 '22 10:11 Hu-Wentao