Skip to content

Commit

Permalink
NestedFormController
Browse files Browse the repository at this point in the history
  • Loading branch information
Sub-Xaero committed Jan 24, 2021
1 parent c6eef27 commit 3058ebf
Show file tree
Hide file tree
Showing 37 changed files with 288 additions and 28 deletions.
1 change: 1 addition & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export { EnableInputsController } from "./enable_inputs_controller";
export { FallbackImageController } from "./fallback_image_controller";
export { LazyBlockController } from "./lazy_block_controller";
export { LimitedSelectionCheckboxesController } from "./limited_selection_checkboxes_controller";
export { NestedFormController } from "./nested_form_controller";
export { PasswordConfirmController } from "./password_confirm_controller";
export { PasswordPeekController } from "./password_peek_controller";
export { ResponsiveIframeBodyController, ResponsiveIframeWrapperController } from "./responsive_iframe_controller";
Expand Down
2 changes: 1 addition & 1 deletion dist/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions dist/nested_form_controller.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { BaseController } from "./base_controller";
export declare class NestedFormController extends BaseController {
static targets: string[];
static values: {
insertMode: StringConstructor;
wrapperClass: StringConstructor;
};
readonly targetTarget: HTMLElement;
readonly templateTarget: HTMLTemplateElement | HTMLScriptElement;
readonly wrapperClassValue: string;
readonly hasWrapperSelectorValue: boolean;
readonly insertModeValue: InsertPosition;
readonly hasInsertModeValue: boolean;
get wrapperClass(): string;
get insertMode(): InsertPosition;
connect(): void;
add(event?: Event): void;
remove(event: Event): void;
generateID(): string;
private checkStructure;
}
//# sourceMappingURL=nested_form_controller.d.ts.map
1 change: 1 addition & 0 deletions dist/nested_form_controller.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/stimulus-library.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.modern.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.modern.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.module.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.module.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/stimulus-library.umd.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/_partials/no-values.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `-` | - | - | - |
1 change: 1 addition & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
** [FallbackImageController](./controllers/fallback_image_controller.md "Stimulus Library - FallbackImageController")
** [LazyBlockController](./controllers/lazy_block_controller.md "Stimulus Library - LazyBlockController")
** [LimitedSelectionCheckboxesController](./controllers/limited_selection_checkboxes_controller.md "Stimulus Library - LimitedSelectionCheckboxesController")
** [NestedFormController](./controllers/nested_form_controller.md "Stimulus Library - NestedFormController")
** [PasswordConfirmController](./controllers/password_confirm_controller.md "Stimulus Library - PasswordConfirmController")
** [PasswordPeekController](./controllers/password_peek_controller.md "Stimulus Library - PasswordPeekController")
** [ResponsiveIFrameController](controllers/responsive_iframe_controller.md "Stimulus Library - ResponsiveIFrame")
Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/async_block_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ AJAX load heavy content after the initial page load, while showing a placeholder

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `endpoint` | String | The URL to fetch content from | - |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/char_count_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Add a visual count of the number of characters in a textarea/string input, with

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `min` | Number | The minimum number of character. This will add an `error` class (See: Classes) to the input if the min character are not met | 0 |
| `max` | Number | The maximum number of character. This will add an `error` class (See: Classes) to the input if the max character are exceeded | 99,999 |
Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/clipboard_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ You can also wire up the controller to have a copy button, and an optional fallb

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `removeUnusedValue` | Boolean | If `copy` is not supported by the browser, remove the `copy` target from the DOM. Otherwise remove the `fallback` target | false |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/confirm_navigation_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Alert the user when they try to navigate away from a page that might have unsave

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `message` | String | The message to show to the user (if supported by the browser) | The native browser `are you sure?` dialogue |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/disable_inputs_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Disable other inputs if a checkbox is ticked.

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `clear` | Boolean | Whether the controller should clear the targeted inputs when they become disabled | false |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/empty_dom_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ You can then style the controller, or show a nice placeholder when the container

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `scopeSelector` (Optional) | String | A CSS selector to pass to `querySelectorAll` to limit what elements are included in the count of empty/not-empty | All child elements of controller element |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/enable_inputs_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Enable other inputs if a checkbox is ticked.

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `clear` | Boolean | Whether the controller should clear the targeted inputs when they become disabled | false |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/lazy_block_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ AJAX load content, only when it comes into view, while showing a placeholder.

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `endpoint` | String | The URL to fetch content from | - |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ To only allow a user to select a limited number of checkboxes

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `max` | Number | The maximum number of checkboxes that can be checked | - |
| `message` (Optional) | String | The error message to show when the user selects too many checkboxes | - |
Expand Down
79 changes: 79 additions & 0 deletions docs/controllers/nested_form_controller.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# NestedFormController

## Purpose

Primarily for Rails `accepts_nested_attributes_for` associations, enabling a form interface that allows a user to added and remove sub-records.

<!-- tabs:start -->

## ** Actions **

#### [Actions](https://stimulus.hotwire.dev/reference/actions)

| Action | Purpose |
| --- | --- |
| `add` | Add a new record to the form using the given template |
| `remove` | Remove the nearest parent record from the form. If the record is a new record, just removes it from the DOm, otherwise looks for a hidden input with `name="_destroy"` sets it to `1` (true) and hides the record using `display: none`|

## ** Targets **

#### [Targets](https://stimulus.hotwire.dev/reference/targets)

| Target | Purpose | Default |
| --- | --- | --- |
| `template` | The `<template>` or `<script type="text/x-template">` tag containing the HTML for a sub record. | - |
| `target` | Where to add the created sub-records. | - |

## ** Classes **

#### [Classes](https://stimulus.hotwire.dev/reference/classes)

[no-classes](../_partials/no-classes.md ':include')

## ** Values **

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `wrapperClass` | String | The class of the `<div>` that wraps each sub-record. | `nested-fields` |
| `insert` | String | How to insert records around `target`. Accepts any one of the parameters accepted by [`insertAdjacentHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) | `beforeend` (append) |

## ** Events **

#### Events

[no-events](../_partials/no-events.md ':include')

## ** Side Effects **

None

<!-- tabs:end -->

# How to Use

This controller requires a bit of structured markup.

There should be a `<template data-nested-form-target="template">` or a `<script data-nested-form-target="template" type="text/x-template>` tag that holds the markup that should be added every time the user adds a new record. If you want to use multiple levels of nested forms you should
use `<script data-nested-form-target="template" type="text/x-template>`, as most browsers down allow `<template>` tags to be nested.

Ideally you should have a reusable partial for the nested-record's fields so that you can render it for the `template` that the controller uses to create new records, and for each of the existing records in the form.

Each distinct record in the form should be wrapped in a div with a class of either `nested-fields` or the value specified in `data-nested-form-wrapper-class-value`- this enables the controller to perform the operations it needs to do on all of the nested records.

When generating the markup for the `template`, the inputs should be named using the magic value `NEW_RECORD` where you want a unique identifier to be placed when the controller inserts a new record. i.e. `lists[task_attributes][NEW_RECORD][id]`. Using Rails `fields_for` helper, this is as easy as
specifying `f.simple_fields_for ..., child_index: 'NEW_RECORD' `



<!-- tabs:start -->

## ** HTML **

[example](../examples/nested_form_controller.erb ':include :type=code')

## ** HAML **

[example](../examples/nested_form_controller.haml ':include :type=code')
<!-- tabs:end -->
2 changes: 1 addition & 1 deletion docs/controllers/scroll_into_focus_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ See [scrollIntoView documentation](https://developer.mozilla.org/en-US/docs/Web/

**Note**: If the user's browser does not support smooth scrolling, the browser will ignore these values and scroll using the browser default.

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `behaviour` | String | One of `auto`, `smooth` | `auto` |
| `block` | String | One of `center`, `end`, `nearest`, `start` | `start` |
Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/scroll_to_bottom_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Enable buttons/links to smooth scroll to the bottom of the document, or the near

**Note**: If the user's browser does not support smooth scrolling, the browser will ignore these values and scroll using the browser default.

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `mode` | String | Should the controller scroll the `document` or the `nearest` scrollable element | `document` |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/scroll_to_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ See [scrollIntoView documentation](https://developer.mozilla.org/en-US/docs/Web/

**Note**: If the user's browser does not support smooth scrolling, the browser will ignore these values and scroll using the browser default.

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `target` | String | A CSS selector to the element you wish to scroll to | - |
| `behaviour` | String | One of `auto`, `smooth` | `auto` |
Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/scroll_to_top_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Enable buttons/links to smooth scroll to the top of the document, or the nearest

**Note**: If the user's browser does not support smooth scrolling, the browser will ignore these values and scroll using the browser default.

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `mode` | String | Should the controller scroll the `document` or the `nearest` scrollable element | `document` |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/self_destruct_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Remove elements a set amount of time after loading - primarily for flash notific
#### [Values](https://stimulus.hotwire.dev/reference/values)


| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `secondsValue` | Number | The number of seconds before the element removes itself from the DOM | - |

Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/teleport_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Transport an element from one place in the DOM, to another.

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `immediate` | Boolean | Set to `true` to immediately teleport the element on controller `connect` | `false` |
| `target` | String | The CSS selector corresponding to the desired position in the DOM | - |
Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/toggle_class_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ To toggle a class on an element, based on various optional interactions i.e. On

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `class` | String | The space separated list of classes to toggle | `-` |
| `mouseEnter` (Optional) | String | One of `on`, `off`, or `toggle`, what to do when the user's mouse enters the controller scope | `-` |
Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/turbo_frame_rc_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ out the frame widget, or selectively load/unload it.

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `frameId` | String | The ID of the frame to drive | - |
| `src` (Optional) | String | The URL set the remote frame's `src` to | If the controller root element is an `<a>`, the `href` of the element. Otherwise, throws an error. |
Expand Down
2 changes: 1 addition & 1 deletion docs/controllers/word_count_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Add a visual count of the number of words in a textarea/string input, with a opt

#### [Values](https://stimulus.hotwire.dev/reference/values)

| Value | Type | Purpose | Default |
| Value | Type | Description | Default |
| --- | --- | --- | --- |
| `min` | Number | The minimum number of words. This will add an `error` class (See: Classes) to the input if the min words are not met | 0 |
| `max` | Number | The maximum number of words. This will add an `error` class (See: Classes) to the input if the max words are exceeded | 99,999 |
Expand Down
45 changes: 45 additions & 0 deletions docs/examples/nested_form_controller.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!-- FILE: tasks/form-->

<!--
.....
Parent association fields
.....
-->

<h4> Tasks</h4>
<div data-controller="nested-form">
<script type="text/x-template" data-nested-form-target="template">
<%= f.simple_fields_for :tasks, Task.new, child_index: 'NEW_RECORD' do |task| %>
<%= render "task_fields", f: task %>
<% end %>
</script>

<div data-nested-form-target="target">
<%= f.simple_fields_for :tasks do |task| %>
<%= render "task_fields", f: task %>
<% end %>
</div>

<%= link_to "Add Task", "#", class: "button green", data: { action: "click->nested-form#add" } %>
</div>
<!--
.....
Parent association fields
.....
-->







<!-- FILE: tasks/_task_fields -->
<div class="nested-fields" data-new-record="<%= f.object.new_record? %>">
<div class="form-group">
<%= f.label :description %>
<%= f.text_field :description, class: 'form-control' %>
</div>
<small><%= link_to "Remove", "#", data: { action: "click->nested-form#remove" } %></small>
<%= f.hidden_field :_destroy %>
</div>
40 changes: 40 additions & 0 deletions docs/examples/nested_form_controller.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-#<!-- FILE: tasks/form -->
-#<!--
-#.....
-#Parent association fields
-#.....
-#-->
%h4 Tasks
%div{ data: { controller: "nested-form" } }
%script{ type: "text/x-template", data: { nested_form_target: "template" } }
= f.simple_fields_for :tasks, Task.new, child_index: 'NEW_RECORD' do |task|
= render "task_fields", f: task
%div{ data: { nested_form_target: "target"} }
= f.simple_fields_for :tasks do |task|
= render "task_fields", f: task
= link_to "Add Task", "#", class: "button green", data: { action: "click->nested-form#add" }

-#<!--
-#.....
-#Parent association fields
-#.....
-#-->







-#<!-- FILE: tasks/_task_fields -->
= content_tag :div, class: "nested-fields", data: { new_record: f.object.new_record? } do
.form-group
= f.label :description
= f.text_field :description, class: 'form-control'

%small= link_to "Remove", "#", data: { action: "click->nested-form#remove" }
= f.hidden_field :_destroy
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export {EnableInputsController} from "./enable_inputs_controller";
export {FallbackImageController} from "./fallback_image_controller";
export {LazyBlockController} from "./lazy_block_controller";
export {LimitedSelectionCheckboxesController} from "./limited_selection_checkboxes_controller";
export {NestedFormController} from "./nested_form_controller";
export {PasswordConfirmController} from "./password_confirm_controller";
export {PasswordPeekController} from "./password_peek_controller";
export {ResponsiveIframeBodyController, ResponsiveIframeWrapperController} from "./responsive_iframe_controller";
Expand Down
Loading

0 comments on commit 3058ebf

Please sign in to comment.