Answers.Body: <p>First, from your field type’s <code>inputHtml()</code> method, make sure you’re passing the field instance to the template as a <code>field</code> variable, and the value as a <code>value</code> variable:</p>
  <pre class="lang-php prettyprint-override"><code>protected function fieldHtml($value, ElementInterface $element = null): string
  {
      return Craft::$app-&gt;view-&gt;renderTemplate('plugin-handle/path/to/CustomField_input', [
          'field' =&gt; $this,
          'value' =&gt; $value,
      ]);
  }
  </code></pre>
  <p>Then from your template, each of your input names should resemble how you want them to be accessed from the field data – <em>namespaced by the field handle</em>, and you should pull the values the same way you want to on the front end:</p>
  <div class="s-table-container">
  <table class="s-table">
  <thead>
  <tr>
  <th>Input name</th>
  <th>Input value</th>
  </tr>
  </thead>
  <tbody>
  <tr>
  <td><code>&quot;#{field.handle}[subGroupA][text]&quot;</code></td>
  <td><code>value.subGroupA.text</code></td>
  </tr>
  <tr>
  <td><code>&quot;#{field.handle}[subGroupA][dropdown]&quot;</code></td>
  <td><code>value.subGroupA.dropdown</code></td>
  </tr>
  </tbody>
  </table>
  </div>
  <p>You’ll also want to “namespace” your input IDs by prefixing them with the field handle, in case the same field type is used multiple times in a field layout.</p>
  <p>Here’s what it will look like:</p>
  <pre><code>{{ forms.textField({
      id: &quot;#{field.handle}-subGroupAText&quot;,
      name: &quot;#{field.handle}[subGroupA][text]&quot;,
      type: 'number',
      value: value.subGroupA.text ?? '',
  }) }}
  {{ forms.selectField({
      id: &quot;#{field.handle}-subGroupADropdown&quot;,
      name: &quot;#{field.handle}[subGroupA][dropdown]&quot;,
      options: dropdownValues,
      value: field.subGroupA.dropdown ?? 'defaultDropdownValue',
  }) }}
  </code></pre>
  <p>The field will need to store its data as JSON. To get that working…</p>
  <ul>
  <li><p>Your <code>getContentColumnType()</code> method should return <code>text</code>:</p>
  <pre class="lang-php prettyprint-override"><code>public function getContentColumnType()
  {
      return \yii\db\Schema::TYPE_TEXT;
  }
  </code></pre>
  </li>
  <li><p>Your <code>normalizeValue()</code> method should check if the value is a string, and if so, JSON-decode it:</p>
  <pre class="lang-php prettyprint-override"><code>public function normalizeValue($value, ElementInterface $element = null)
  {
      if (is_string($value)) {
          $value = \craft\helpers\Json::decode($value);
      }
      return $value;
  }
  </code></pre>
  </li>
  </ul>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-12T12:28:02.687Z
Answers.Text: Wow, thanks for the detailed write-up, BK! After having done my best to implement it and still not getting data saved, I have a few questions:
  1) is `protected function inputHtml()` (I assume the `fieldHtml` in the code is a typo) the same as the `public function getInputHtml()` that pluginfactory.io started me out with, in `plugin-handle/src/fields/CustomFieldField.php`?
  2) `value: value.subGroupA...` or `value: field.subGroupA...`?
  3) `getContentColumnType()` was set to `TYPE_STRING`; is that different from `TYPE_TEXT`?
  Thanks again!!!!
Answers.UserDisplayName: Sandwich
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-12T14:36:30.297Z
Answers.Text: @Sandwich 1) Yeah, in general the protected one is recommended, but either one will work. I’ll PR pluginfactory.
  2) `value`
  3) yeah `string` will give you a `VARCHAR` column; `TEXT` is better for storing JSON.
Answers.UserDisplayName: Brandon Kelly
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-12T14:54:28.293Z
Answers.Text: @Sandwich Just updated the example with namespaced input IDs as well. Forgot you’ll want to do that to ensure you don’t get conflicts if the same field type is used multiple times within the same field layout.
Answers.UserDisplayName: Brandon Kelly
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-12T17:18:13.083Z
Answers.Text: Ok, that got things working! I think the main thing left is to figure out how to get `{{ entry.fieldHandle.subGroupA` to custom-output the same as `{{ entry.fieldHandle.subGroupA.text ~ entry.fieldHandle.subGroupA.dropdown }}`. I haven't done any searching yet on the subject (although I'm not quite sure what to search _for_); could you point me in the right direction?
Answers.UserDisplayName: Sandwich
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-12T20:38:00.090Z
Answers.Text: @Sandwich That would be a bit more complicated; you would need to start normalizing your field value into nested objects using custom classes in `normalizeValue()`. See Craft’s built-in Color field type + the ColorData model for a relatively simple example.
Answers.UserDisplayName: Brandon Kelly
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-14T08:33:00.910Z
Answers.Text: Ok, that's certainly pointed me in the right direction! I have the basic "sub" functions working - `entry.fieldHandle.subGroupA` is spitting out what I want it to. It seems like that comes at the cost of being able to reference field values by their data structure, though (eg. `entry.fieldHandle.subGroupA.dropdown`). Is there any way to have my cake and eat it too? ;)
Answers.UserDisplayName: Sandwich
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-20T23:37:28.853Z
Answers.Text: @Sandwich If the subgroups are represented via instantiated class(es), you can add a `__toString()` method to it to determine what should be output when the object itself is output “as a string”, e.g. `entry.fieldHandle.subGroupA`.
Answers.UserDisplayName: Brandon Kelly
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-21T04:42:47.400Z
Answers.Text: Ahh, so however I would want multiple "levels" of output variables, I'd need everything down to the next-to-last level to be a class (assuming the straight-up data hierarchy didn't suffice because things needed massaging before output)?
Answers.UserDisplayName: Sandwich
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-07-12T05:17:40.640Z
Answers.LastActivityDate: 2021-07-12T14:53:34.073Z
Answers.LastEditDate: 2021-07-12T14:53:34.073Z
Answers.UserDisplayName: Brandon Kelly
Body: <p>I'm working on what I thought would be a relatively simple plugin; a custom fieldtype that would have 8 &quot;sub-groups&quot;, each of which would consist of a numeric text field and a dropdown field with a few predefined options.</p>
  <p>At the Twig/templating end of things, I'd like it to work like this:</p>
  <pre><code>{{ entry.customField.subGroup.text }} // (outputs the text field for that sub-group)
  {{ entry.customField.subGroup.dropdown }} // (outputs the dropdown field for that sub-group)
  {{ entry.customField.subGroup }} // (outputs a combined string of `subGroup.text` and `subGroup.dropdown`)
  </code></pre>
  <p>Additionally, in the field's settings, I have 8 lightswitch fields to disable any individual sub-group (the <code>{% if %}</code> statement wrapping the code below).</p>
  <p>All together, I'm putting it all together in the <code>CustomField_input.twig</code> file like this:</p>
  <pre><code>{% set dropdownValues = [
      { label: 'Foo', value: 'foo' },
      { label: 'Bar', value: 'bar' },
      { label: 'Baz', value: 'baz' }
  ] %}
  {% if field.subGroup %}
      {% embed '_includes/forms/field' with {
          label: 'Sub-Group A'|t,
          instructions: &quot;A sub-group of the field&quot;|t,
          id: 'subGroupA'
      } %}
          {% import &quot;_includes/forms&quot; as forms %}
          {% block input %}
              &lt;div class=&quot;flex&quot;&gt;
                  {{ forms.textField({
                      id: 'subGroupAText',
                      name: 'subGroupAText',
                      type: 'number',
                      value: field.subGroupAText ?? ''
                  }) }}
                  {{ forms.selectField({
                      id: 'subGroupADropdown',
                      name: 'subGroupADropdown',
                      options: dropdownValues,
                      value: field.subGroupADropdown ?? 'defaultDropdownValue'
                  }) }}
              &lt;/div&gt;
          {% endblock %}
      {% endembed %}
  {% endif %}
  </code></pre>
  <p>First off, the above code renders the fields I want correctly, but no values get saved. Not sure what that's about. But before I resolve that, I suspect I should figure out what I need to alter in the above code to get the nested aspect of the custom field. Is it as simple as changing:</p>
  <pre><code>id: 'subGroupAText',
  name: 'subGroupAText',
  ...
  
  value: field.subGroupAText ?? ''
  </code></pre>
  <p>...to this...?</p>
  <pre><code>id: 'subGroupA[text]',
  name: 'subGroupA[text]',
  ...
  
  value: field.subGroupA[text] ?? ''
  </code></pre>
  
Tags: <plugin-development><custom-fieldtype>
Title: How to build a custom fieldtype containing multiple nested groups of text and dropdown pairs?
ThreadText: # How to build a custom fieldtype containing multiple nested groups of text and dropdown pairs?
  
  (Asked by: username_0 on 2021-07-11)
  
  I'm working on what I thought would be a relatively simple plugin; a custom fieldtype that would have 8 "sub-groups", each of which would consist of a numeric text field and a dropdown field with a few predefined options.
  
  At the Twig/templating end of things, I'd like it to work like this:
  
  ```
  {{ entry.customField.subGroup.text }} // (outputs the text field for that sub-group)
  {{ entry.customField.subGroup.dropdown }} // (outputs the dropdown field for that sub-group)
  {{ entry.customField.subGroup }} // (outputs a combined string of `subGroup.text` and `subGroup.dropdown`)
  
  ```
  
  Additionally, in the field's settings, I have 8 lightswitch fields to disable any individual sub-group (the `{% if %}` statement wrapping the code below).
  
  All together, I'm putting it all together in the `CustomField_input.twig` file like this:
  
  ```
  {% set dropdownValues = [
      { label: 'Foo', value: 'foo' },
      { label: 'Bar', value: 'bar' },
      { label: 'Baz', value: 'baz' }
  ] %}
  {% if field.subGroup %}
      {% embed '_includes/forms/field' with {
          label: 'Sub-Group A'|t,
          instructions: "A sub-group of the field"|t,
          id: 'subGroupA'
      } %}
          {% import "_includes/forms" as forms %}
          {% block input %}
              <div class="flex">
                  {{ forms.textField({
                      id: 'subGroupAText',
                      name: 'subGroupAText',
                      type: 'number',
                      value: field.subGroupAText ?? ''
                  }) }}
                  {{ forms.selectField({
                      id: 'subGroupADropdown',
                      name: 'subGroupADropdown',
                      options: dropdownValues,
                      value: field.subGroupADropdown ?? 'defaultDropdownValue'
                  }) }}
              </div>
          {% endblock %}
      {% endembed %}
  {% endif %}
  
  ```
  
  First off, the above code renders the fields I want correctly, but no values get saved. Not sure what that's about. But before I resolve that, I suspect I should figure out what I need to alter in the above code to get the nested aspect of the custom field. Is it as simple as changing:
  
  ```
  id: 'subGroupAText',
  name: 'subGroupAText',
  ...
  
  value: field.subGroupAText ?? ''
  
  ```
  
  ...to this...?
  
  ```
  id: 'subGroupA[text]',
  name: 'subGroupA[text]',
  ...
  
  value: field.subGroupA[text] ?? ''
  
  ```
  
  **Tags:** plugin-development, custom-fieldtype
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2021-07-12. Score: 2 | Accepted answer
  
  First, from your field type’s `inputHtml()` method, make sure you’re passing the field instance to the template as a `field` variable, and the value as a `value` variable:
  
  ```php
  protected function fieldHtml($value, ElementInterface $element = null): string
  {
      return Craft::$app->view->renderTemplate('plugin-handle/path/to/CustomField_input', [
          'field' => $this,
          'value' => $value,
      ]);
  }
  
  ```
  
  Then from your template, each of your input names should resemble how you want them to be accessed from the field data – *namespaced by the field handle*, and you should pull the values the same way you want to on the front end:
  
  | Input name | Input value |
  | --- | --- |
  | `"#{field.handle}[subGroupA][text]"` | `value.subGroupA.text` |
  | `"#{field.handle}[subGroupA][dropdown]"` | `value.subGroupA.dropdown` |
  
  You’ll also want to “namespace” your input IDs by prefixing them with the field handle, in case the same field type is used multiple times in a field layout.
  
  Here’s what it will look like:
  
  ```
  {{ forms.textField({
      id: "#{field.handle}-subGroupAText",
      name: "#{field.handle}[subGroupA][text]",
      type: 'number',
      value: value.subGroupA.text ?? '',
  }) }}
  {{ forms.selectField({
      id: "#{field.handle}-subGroupADropdown",
      name: "#{field.handle}[subGroupA][dropdown]",
      options: dropdownValues,
      value: field.subGroupA.dropdown ?? 'defaultDropdownValue',
  }) }}
  
  ```
  
  The field will need to store its data as JSON. To get that working…
  
  * Your `getContentColumnType()` method should return `text`:
  
    ```php
    public function getContentColumnType()
    {
        return \yii\db\Schema::TYPE_TEXT;
    }
  
    ```
  * Your `normalizeValue()` method should check if the value is a string, and if so, JSON-decode it:
  
    ```php
    public function normalizeValue($value, ElementInterface $element = null)
    {
        if (is_string($value)) {
            $value = \craft\helpers\Json::decode($value);
        }
        return $value;
    }
  
    ```
  
  > **Comments:**
  > **username_0**: Wow, thanks for the detailed write-up, BK! After having done my best to implement it and still not getting data saved, I have a few questions: 1) is `protected function inputHtml()` (I assume the `fieldHtml` in the code is a typo) the same as the `public function getInputHtml()` that pluginfactory.io started me out with, in `plugin-handle/src/fields/CustomFieldField.php`? 2) `value: value.subGroupA...` or `value: field.subGroupA...`? 3) `getContentColumnType()` was set to `TYPE_STRING`; is that different from `TYPE_TEXT`? Thanks again!!!!  
  > **username_1**: @username_0 1) Yeah, in general the protected one is recommended, but either one will work. I’ll PR pluginfactory. 2) `value` 3) yeah `string` will give you a `VARCHAR` column; `TEXT` is better for storing JSON.  
  > **username_1**: @username_0 Just updated the example with namespaced input IDs as well. Forgot you’ll want to do that to ensure you don’t get conflicts if the same field type is used multiple times within the same field layout.  
  > **username_0**: Ok, that got things working! I think the main thing left is to figure out how to get `{{ entry.fieldHandle.subGroupA` to custom-output the same as `{{ entry.fieldHandle.subGroupA.text ~ entry.fieldHandle.subGroupA.dropdown }}`. I haven't done any searching yet on the subject (although I'm not quite sure what to search _for_); could you point me in the right direction?  
  > **username_1**: @username_0 That would be a bit more complicated; you would need to start normalizing your field value into nested objects using custom classes in `normalizeValue()`. See Craft’s built-in Color field type + the ColorData model for a relatively simple example.  
  > **username_0**: Ok, that's certainly pointed me in the right direction! I have the basic "sub" functions working - `entry.fieldHandle.subGroupA` is spitting out what I want it to. It seems like that comes at the cost of being able to reference field values by their data structure, though (eg. `entry.fieldHandle.subGroupA.dropdown`). Is there any way to have my cake and eat it too? ;)  
  > **username_1**: @username_0 If the subgroups are represented via instantiated class(es), you can add a `__toString()` method to it to determine what should be output when the object itself is output “as a string”, e.g. `entry.fieldHandle.subGroupA`.  
  > **username_0**: Ahh, so however I would want multiple "levels" of output variables, I'd need everything down to the next-to-last level to be a class (assuming the straight-up data hierarchy didn't suffice because things needed massaging before output)?  
  
  ---
  
  