Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#5610] Add turbo frames chapter #154

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

gogi1805
Copy link

Task: #5610

Aim

Add chapter for turbo frames.

Solution

Explained a general idea of turbo frames, installation and setup steps, demonstrated the functionalities through code examples.

@gogi1805
Copy link
Author

I didn't get the time to properly dive into this topic on Eager loading frames.

Should I also add that once I return from the exams?

@nikajukic nikajukic requested a review from lovro-bikic May 3, 2023 07:36
@nikajukic
Copy link
Contributor

Adding @lovro-bikic as well to the list of reviewers because he's familiar with the topic.


With Turbo Frames, you can treat a subset of the page as its own component, where links and form submissions replace only that part.

The way it works is: whenever the link inside a frame is clicked or form inside a frame is submitted, the frame content will automatically be updated after receiving a response.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The way it works is: whenever the link inside a frame is clicked or form inside a frame is submitted, the frame content will automatically be updated after receiving a response.
## How it works
Whenever the link inside a frame is clicked or form inside a frame is submitted, the frame content will automatically be updated after receiving a response.

Development practices/Turbo frames.md Show resolved Hide resolved
h2 = 'Authors'

- @authors.each do |author|
= turbo_frame_tag "edit_author_#{author.id}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
= turbo_frame_tag "edit_author_#{author.id}"
= turbo_frame_tag [:edit, dom_id(author)]


The only required change was adding this line for each author:
```ruby
= turbo_frame_tag "edit_author_#{author.id}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
= turbo_frame_tag "edit_author_#{author.id}"
= turbo_frame_tag [:edit, dom_id(author)]


The view for the #edit action may look like this:
```ruby
= turbo_frame_tag "edit_author_#{@author.id}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if we want to use render 'form'? In that case, it wouldn't work because new and edit would have a different frame id.

Please try with the following and add it as a section in this chapter if it works (eg: Working with partials for forms):

= turbo_frame_tag [action_name, dom_id(@author)]

Copy link
Author

@gogi1805 gogi1805 Jun 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not work because the dom_id(@author) for the #new action is new_author so
= turbo_frame_tag [action_name, dom_id(@author)] would generate the turbo frame with the new_new_author id.

Also if there are some validation errors while editing, the action_name would be update instead of edit.

So we click on the link to add a new author, it acts as if it was clicked inside of a `'new_author'` frame because of the `data-turbo-frame` attribute. The response provided by the #new action will have its turbo-frame segment extracted and the content will replace the frame from on the index page.

### target
By default, navigation within a frame will target just that frame. But it can also drive another named frame by setting the `target` to the ID of that frame.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please elaborate a bit more for this section cause I find it hard to understand/visualize what's happening.

@cilim
Copy link
Member

cilim commented May 4, 2023

I didn't get the time to properly dive into this topic on Eager loading frames.

Should I also add that once I return from the exams?

I think we should cover them if they provide us with concrete value 🙂

@gogi1805 gogi1805 requested a review from cilim June 16, 2023 14:00
@cilim
Copy link
Member

cilim commented Jun 26, 2023

@gogi1805 please include the repo link to you dummy application somewhere as the last section. I see you included it in the useful links section, but I'd like to make a mention to the reader that they can clone the repo and how to set it up.

Speaking of setting it up, after I ran:

  • bin/setup
  • rails server

I was getting an error telling me the application.css isn't found. I had to run bundle exec rake assets:precompile before I could start the server and open the /authors page. Please add this command to bin/setup too.

The rest works fine and looks ok to me as a chapter👌

Let's wait for a couple more approvals before we merge this in 🙂

@nikajukic
Copy link
Contributor

@lovro-bikic @uncoverd @vr4b4c please review this new Handbook chapter.

@vr4b4c
Copy link
Member

vr4b4c commented Jul 4, 2023

I'm going to skip this one, I'm not familiar with this technology

</turbo-frame>
```

However, the second part (*Or it can drive another named frame by setting the target to the ID of that frame.*) doesn't seem to work in development without using turbo streams ([discussion](https://discuss.hotwired.dev/t/target-attribute-on-turbo-frame-tags/1959)) and `data-turbo-frame` attribute can be used to achieve the required functionality.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an example on Atlas using target: that works, you just have to put the target on both turbo frames.
Your previous example could be rewritten as:

# index.html.slim

= turbo_frame_tag 'main'
  h2 = 'Authors'
  = link_to 'Add author', new_author_path, data: { 'turbo-frame': dom_id(Author.new) }

  = turbo_frame_tag Author.new, target: 'main' # <--
  - @authors.each do |author|
    = turbo_frame_tag "edit_author_#{author.id}"
      .row
        .col
          = author.first_name
        .col
          = author.last_name
        .col
          = link_to 'Edit', edit_author_path(author.id)

# new.html.slim

= turbo_frame_tag Author.new, target: 'main' # <--
  = simple_form_for @author do |f|
    .row
      .col
        = f.input :first_name
      .col
        = f.input :last_name
      .col
        = f.button :submit

Comment on lines +43 to +50
The gem is automatically configured for applications made with Rails 7+ unless --skip-hotwire was passed to the generator.
Any new project that uses the [default-rails-template](https://github.com/infinum/default_rails_template) should have Turbo set up and ready to go.

It can also be installed manually:
1. Add the turbo-rails gem to your Gemfile: `gem 'turbo-rails'`
2. Run `./bin/bundle install`
3. Run `./bin/rails turbo:install`
4. Add `import '@hotwired/turbo-rails'` to the Javascript entrypoint file, which is usually located in `app/javascript/application.js` or `app/webpack/packs/application.js` for apps using webpacker if it hasn't been automatically added in the previous step.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See turbo-rails documentation for detailed installation instructions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe my intention wasn't clear, I would remove c/p of installation instructions and leave the reference to the official docs.

end
```

NOTE: If you are using slim, make sure to name the views like `'index.html.slim'` instead of just `'index.slim'` because otherwise the form submissions won't work correctly!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NOTE: If you are using slim, make sure to name the views like `'index.html.slim'` instead of just `'index.slim'` because otherwise the form submissions won't work correctly!
NOTE: If you are using slim, make sure to name the views like `'index.html.slim'` instead of just `'index.slim'` because otherwise the [form submissions won't work correctly](https://github.com/hotwired/turbo-rails/issues/168)!

This time we don't want to replace anything that's visible on the index page.
We actually want to "append" the form for creating a new author to the page.

If we used the same approach as for the inline editing, we would replace the link for creating a new user with the form. Maybe we don't want to do this because we don't want the form to appear at the same position where the button is, but somewhere else on the page. Luckily it is also done very easily with turbo frames.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If we used the same approach as for the inline editing, we would replace the link for creating a new user with the form. Maybe we don't want to do this because we don't want the form to appear at the same position where the button is, but somewhere else on the page. Luckily it is also done very easily with turbo frames.
If we used the same approach as for the inline editing, we would replace the link for creating a new user with the form. Maybe we don't want to do this because we don't want the form to appear at the same position where the button is, but somewhere else on the page. Luckily it is also done very easily with frame targeting.


The first change to our index page is the link to the new_author_path. This link is different to the one for the edit_author_path, because `data: { 'turbo-frame': dom_id(Author.new) }` was added to it.

The `data-turbo-frame` attribute can be added on non-frame elements to control from which frame was the link clicked or the form submitted.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `data-turbo-frame` attribute can be added on non-frame elements to control from which frame was the link clicked or the form submitted.
The `data-turbo-frame` attribute can be added on non-frame elements to control which part of the page should be updated by user interaction with the element.

end
```

So we click on the link to add a new author, it acts as if it was clicked inside of a frame with the `new_author` id because of the `data-turbo-frame` attribute. The response provided by the #new action will have its turbo-frame segment extracted and the form for creating a new author will replace the empty frame with the same id from the index page.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
So we click on the link to add a new author, it acts as if it was clicked inside of a frame with the `new_author` id because of the `data-turbo-frame` attribute. The response provided by the #new action will have its turbo-frame segment extracted and the form for creating a new author will replace the empty frame with the same id from the index page.
When the user clicks on the link to add a new author, the button sets the turbo frame target to `new_author`. Upon receiving the response, provided by the #new action, the `new_author` turbo-frame segment is extracted and the empty frame placeholder is replaced with the form content.

@gogi1805 gogi1805 requested review from cilim and vr4b4c August 16, 2023 09:18
Comment on lines +43 to +50
The gem is automatically configured for applications made with Rails 7+ unless --skip-hotwire was passed to the generator.
Any new project that uses the [default-rails-template](https://github.com/infinum/default_rails_template) should have Turbo set up and ready to go.

It can also be installed manually:
1. Add the turbo-rails gem to your Gemfile: `gem 'turbo-rails'`
2. Run `./bin/bundle install`
3. Run `./bin/rails turbo:install`
4. Add `import '@hotwired/turbo-rails'` to the Javascript entrypoint file, which is usually located in `app/javascript/application.js` or `app/webpack/packs/application.js` for apps using webpacker if it hasn't been automatically added in the previous step.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe my intention wasn't clear, I would remove c/p of installation instructions and leave the reference to the official docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants