|
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