|
11 | 11 |
|
12 | 12 | A Stimulus controller to create new fields on the fly to populate your Rails relationship with `accepts_nested_attributes_for`.
|
13 | 13 |
|
14 |
| -[Nested attributes](https://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods) allow you to save attributes on associated records through the parent. |
| 14 | +## 📚 Documentation |
15 | 15 |
|
16 |
| -## Installation |
| 16 | +See [stimulus-rails-nested-form documentation](https://stimulus-components.netlify.app/docs/components/stimulus-rails-nested-form/). |
17 | 17 |
|
18 |
| -```bash |
19 |
| -$ yarn add stimulus-rails-nested-form |
20 |
| -``` |
21 |
| - |
22 |
| -And use it in your JS file: |
23 |
| -```js |
24 |
| -import { Application } from "stimulus" |
25 |
| -import NestedForm from "stimulus-rails-nested-form" |
26 |
| - |
27 |
| -const application = Application.start() |
28 |
| -application.register("nested-form", NestedForm) |
29 |
| -``` |
30 |
| - |
31 |
| -## Usage |
32 |
| - |
33 |
| -In your models: |
34 |
| -```ruby |
35 |
| -class User < ApplicationRecord |
36 |
| - has_many :todos |
37 |
| - accepts_nested_attributes_for :todos, reject_if: :all_blank, allow_destroy: true |
38 |
| -end |
39 |
| - |
40 |
| -class Todo < ApplicationRecord |
41 |
| - belongs_to :user |
42 |
| -end |
43 |
| -``` |
44 |
| - |
45 |
| -In your controller: |
46 |
| -```ruby |
47 |
| -class UsersController < ApplicationController |
48 |
| - def update |
49 |
| - if user.update(user_params) |
50 |
| - redirect_to users_path |
51 |
| - else |
52 |
| - render :edit |
53 |
| - end |
54 |
| - end |
55 |
| - |
56 |
| - private |
57 |
| - |
58 |
| - def user_params |
59 |
| - params |
60 |
| - .require(:user) |
61 |
| - .permit( |
62 |
| - todos_attributes: [:id, :_destroy, :description] |
63 |
| - ) |
64 |
| - end |
65 |
| -end |
66 |
| -``` |
67 |
| - |
68 |
| -To DRY up the code, we extract the fields in a partial called `todo_form` to use it in the [template](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template) with a new `Todo` and in the default `fields_for`. |
69 |
| - |
70 |
| -In your view: |
71 |
| -```html |
72 |
| -<%= form_with model: @user, data: { controller: 'nested-form', nested_form_wrapper_selector: '.nested-form-wrapper' } do |f| %> |
73 |
| - <template data-target="nested-form.template"> |
74 |
| - <%= f.fields_for :todos, Todo.new, child_index: 'NEW_RECORD' do |todo_fields| %> |
75 |
| - <%= render "todo_form", f: todo_fields %> |
76 |
| - <% end %> |
77 |
| - </template> |
78 |
| - |
79 |
| - <%= f.fields_for :todos do |todo_fields| %> |
80 |
| - <%= render "todo_form", f: todo_fields %> |
81 |
| - <% end %> |
82 |
| - |
83 |
| - <!-- Inserted elements will be injected before that target. --> |
84 |
| - <div data-target="nested-form.target"></div> |
85 |
| - |
86 |
| - <button type="button" data-action="nested-form#add"> |
87 |
| - Add todo |
88 |
| - </button> |
89 |
| - |
90 |
| - <%= f.submit 'Save todos' %> |
91 |
| -<% end %> |
92 |
| -``` |
93 |
| - |
94 |
| -In the `_todo_form.html.erb` partial: |
95 |
| -```html |
96 |
| -<div class="nested-form-wrapper" data-new-record="<%= f.object.new_record? %>"> |
97 |
| - <%= f.label :description %> |
98 |
| - <%= f.text_field :description %> |
99 |
| - |
100 |
| - <button type="button" data-action="nested-form#remove"> |
101 |
| - Remove todo |
102 |
| - </button> |
103 |
| - |
104 |
| - <%= f.hidden_field :_destroy %> |
105 |
| -</div> |
106 |
| -``` |
107 |
| - |
108 |
| -As explained in the [documentation](https://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for), we need to specify the `child_index` and replace its value in JavaScript because the index needs to be unique for each fields. |
109 |
| - |
110 |
| -## Configuration |
111 |
| - |
112 |
| -| Attribute | Default | Description | Optional | |
113 |
| -| --------- | ------- | ----------- | -------- | |
114 |
| -| `data-nested-form-wrapper-selector` | `.nested-form-wrapper` | Selector to find the wrapper. | ✅ | |
115 |
| - |
116 |
| -The remove feature is completely optional. |
117 |
| - |
118 |
| -## Extending Controller |
119 |
| - |
120 |
| -You can use inheritance to extend the functionality of any Stimulus component: |
121 |
| - |
122 |
| -```js |
123 |
| -import NestedForm from "stimulus-rails-nested-form" |
124 |
| - |
125 |
| -export default class extends NestedForm { |
126 |
| - connect() { |
127 |
| - super.connect() |
128 |
| - console.log("Do what you want here.") |
129 |
| - } |
130 |
| -} |
131 |
| -``` |
132 |
| - |
133 |
| -These controllers will automatically have access to targets defined in the parent class. |
134 |
| - |
135 |
| -If you override the `connect`, `disconnect` or any other methods from the parent, you'll want to call `super.method()` to make sure the parent functionality is executed. |
136 |
| - |
137 |
| -## Development |
138 |
| - |
139 |
| -### Project setup |
140 |
| -```bash |
141 |
| -$ yarn install |
142 |
| -$ yarn dev |
143 |
| -``` |
144 |
| - |
145 |
| -### Tests |
146 |
| - |
147 |
| -[Jest](https://jestjs.io/) and [Puppeteer](https://github.com/puppeteer/puppeteer) are responsible to test this component: |
148 |
| -```bash |
149 |
| -$ yarn test |
150 |
| -``` |
151 |
| - |
152 |
| -### Linter |
153 |
| -[Prettier](https://prettier.io/) and [ESLint](https://eslint.org/) are responsible to lint and format this component: |
154 |
| -```bash |
155 |
| -$ yarn lint |
156 |
| -$ yarn format |
157 |
| -``` |
158 |
| - |
159 |
| -## Contributing |
| 18 | +## 👷♂️ Contributing |
160 | 19 |
|
161 | 20 | Do not hesitate to contribute to the project by adapting or adding features ! Bug reports or pull requests are welcome.
|
162 | 21 |
|
163 |
| -## License |
| 22 | +## 📝 License |
164 | 23 |
|
165 | 24 | This project is released under the [MIT](http://opensource.org/licenses/MIT) license.
|
0 commit comments