Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

render_async lets you include pages asynchronously with AJAX

License

NotificationsYou must be signed in to change notification settings

nikolalsvk/render_async

Repository files navigation

render_async

👋 Welcome to render_async

Let's make your Rails pages fast again 🐎


DonateDownloadsAll contributorsGem Version
Discord ServerCode Climate MaintainablityTest CoverageLicenseHelp Contribute to Open Source

render_async is here to make your pages show faster to users.

Pages become faster seamlessly by rendering partials to your views.

Partials renderasynchronously and let users see your pagefasterthan using regular rendering.

It works with Rails and its tools out of the box.

✨ A quick overview of howrender_async does its magic:

  1. user visits a page
  2. render_async makes an AJAX request on the controller action
  3. controller renders a partial
  4. partial renders in the place where you putrender_async view helper

JavaScript is injected straight into<%= content_for :render_async %> so you choosewhere to put it.

📣 P.S. Join ourDiscord channel for help and discussion, and let's makerender_async even better!

📦 Installation

Add this line to your application's Gemfile:

gem'render_async'

And then execute:

$ bundle install

🔨 Usage

  1. Includerender_async view helper somewhere in your views (e.g.app/views/comments/show.html.erb):

    <%= render_async comment_stats_path%>
  2. Then create a route for itconfig/routes.rb:

    get:comment_stats,controller::comments
  3. Fill in the logic in your controller (e.g.app/controllers/comments_controller.rb):

    defcomment_stats@stats=Comment.get_statsrenderpartial:"comment_stats"end
  4. Create a partial that will render (e.g.app/views/comments/_comment_stats.html.erb):

    <divclass="col-md-6"><%=@stats%></div>
  5. Addcontent_for in your base view file in the body part (e.g.app/views/layouts/application.html.erb):

    <%= content_for :render_async%>

🛠️ Advanced usage

Advanced usage includes information on different options, such as:

Passing in a container ID

render_async renders an element that gets replaced with the contentof your request response. In order to have more control over the elementthat renders first (before the request), you can set the ID of that element.

To set ID of the container element, you can do the following:

<%= render_async users_path, container_id: 'users-container'%>

Rendered code in the view:

<divid="users-container"></div>...

Passing in a container class name

render_async renders an element that gets replaced with the content of yourrequest response. If you want to style that element, you can set the class nameon it.

<%= render_async users_path, container_class: 'users-container-class'%>

Rendered code in the view:

<divid="render_async_18b8a6cd161499117471"class="users-container-class"></div>...

Passing in HTML options

render_async can accepthtml_options as a hash.html_options is an optional hash that gets passed to a Rails'javascript_tag, to drop HTML tags into thescript element.

Example of utilizinghtml_options with anonce:

<%= render_async users_path, html_options: { nonce: true }%>

Rendered code in the view:

<scriptnonce="2x012CYGxKgM8qAApxRHxA==">//<![CDATA[  ...//]]></script>...<divid="render_async_18b8a6cd161499117471"class=""></div>

💡 You can enablenonce to be set everywhere by usingconfiguration option render_async provides.

Passing in an HTML element name

render_async can take in an HTML element name, allowing you to controlwhat type of container gets rendered. This can be useful when you're usingrender_async inside a tableand you need it to render atr element before your request gets loaded, soyour content doesn't get pushed out of the table.

Example of using HTML element name:

<%= render_async users_path, html_element_name: 'tr'%>

Rendered code in the view:

<trid="render_async_04229e7abe1507987376"></tr>...

Passing in a placeholder

render_async can be called with a block that will act as a placeholder beforeyour AJAX call finishes.

Example of passing in a block:

<%= render_async users_path do%><h1>Users are loading...</h1><%end%>

Rendered code in the view:

<divid="render_async_14d7ac165d1505993721"><h1>Users are loading...</h1></div><script>//<![CDATA[  ...//]]></script>

After AJAX is finished, placeholder will be replaced with the request'sresponse.

Passing in an event name

render_async can receive:event_name option which will emit JavaScriptevent after it's done with fetching and rendering request content to HTML.

This can be useful to have if you want to add some JavaScript functionalityafter your partial is loaded throughrender_async.

You can also access the associated container (DOM node) in the event objectthat gets emitted.

Example of passing it torender_async:

<%= render_async users_path, event_name: "users-loaded"%>

Rendered code in view:

<divid="render_async_04229e7abe1507987376"></div><script>//<![CDATA[  ...document.dispatchEvent(newEvent("users-loaded"));  ...//]]></script>

Then, in your JavaScript code, you could do something like this:

document.addEventListener("users-loaded",function(event){console.log("Users have loaded!",event.container);// Access the container which loaded the users});

💡 Dispatching events is also supported for older browsers that don't support Event constructor.

Using default events

render_async will fire the eventrender_async_load when an async partialhas loaded and rendered on the page.

In case there is an error, the eventrender_async_error will fire instead.

This event will fire for allrender_async partials on the page. For everyevent, the associated container (DOM node) will be passed along.

This can be useful to apply JavaScript to content loaded after the page isready.

Example of using events:

// Vanilla javascriptdocument.addEventListener('render_async_load',function(event){console.log('Async partial loaded in this container:',event.container);});document.addEventListener('render_async_error',function(event){console.log('Async partial could not load in this container:',event.container);});// with jQuery$(document).on('render_async_load',function(event){console.log('Async partial loaded in this container:',event.container);});$(document).on('render_async_error',function(event){console.log('Async partial could not load in this container:',event.container);});

Refreshing the partial

render_async lets you refresh (reload) the partial by letting you dispatchthe 'refresh' event on therender_async's container. An example:

<%= render_async comments_path,                 container_id: 'refresh-me',                 replace_container: false%><buttonid="refresh-button">Refresh comments</button><script>varbutton=document.getElementById('refresh-button')varcontainer=document.getElementById('refresh-me');button.addEventListener('click',function(){varevent=newEvent('refresh');// Dispatch 'refresh' on the render_async containercontainer.dispatchEvent(event)})</script>

If you follow the example above, when you click "Refresh comments" button,render_async will trigger again and reload thecomments_path.

💡 Note that you need to passreplace_container: false so you can later dispatch an event on that container.

Retry on failure

render_async can retry your requests if they fail for some reason.

If you wantrender_async to retry a request for number of times, you can dothis:

<%= render_async users_path, retry_count: 5, error_message: "Couldn't fetch it"%>

Now render_async will retryusers_path for 5 times. If it succeeds inbetween, it will stop with dispatching requests. If it fails after 5 times,it will show anerror message which you need to specify.

This can show useful when you know your requests often fail, and you don't wantto refresh the whole page just to retry them.

Retry after some time

If you want to retry requests but with some delay in between the calls, you canpass aretry_delay option together withretry_count like so:

<%= render_async users_path,                 retry_count: 5,                 retry_delay: 2000%>

This will makerender_async wait for 2 seconds before retrying after eachfailure. In the end, if the request is still failing after 5th time, it willdispatch adefault error event.

🍬 If you are catching an event after an error, you can getretryCount fromthe event.retryCount will have the number of retries it took before the event was dispatched.

Here is an example on how to getretryCount:

<%= render_async users_path,                 retry_count: 5,                 retry_delay: 2000,                 error_event_name: 'it-failed-badly'%><script>document.addEventListener('it-failed-badly',function(event){console.log("Request failed after "+event.retryCount+" tries!")});</script>

If you need to pass retry count to the backend, you can passretry_count_header inrender_async's options:

<%= render_async users_path,                 retry_count: 5,                 retry_count_header: 'Retry-Count-Current'%>

And then in controller you can read the value from request headers.

request.headers['Retry-Count-Current']&.to_i

Toggle event

You can triggerrender_async loading by clicking or doing another event to acertain HTML element. You can do this by passing in a selector and an eventname which will triggerrender_async. If you don't specify an event name, thedefault event that will triggerrender_async will be 'click' event. You cando this by doing the following:

<ahref='#'id='comments-button'>Load comments</a><%=render_asynccomments_path,toggle:{selector:'#comments-button',event::click}%>

This will triggerrender_async to load thecomments_path when you click the#comments-button element.If you want to remove an event once it's triggered, you can passonce: true in the toggle options.Theonce option is false (nil) by default.

You can also pass in a placeholder before therender_async is triggered. Thatway, the element that startedrender_async logic will be removed after therequest has been completed. You can achieve this behaviour with something like this:

<%= render_async comments_path, toggle: { selector: '#comments-button', event: :click } do%><ahref='#'id='comments-button'>Load comments</a><%end%>

Control polling with a toggle

Also, you can mix interval and toggle features. This way, you can turn pollingon, and off by clicking the "Load comments" button. In order to do this, you need topasstoggle andinterval arguments torender_async call like this:

<ahref='#'id='comments-button'>Load comments</a><%=render_asynccomments_path,toggle:{selector:'#comments-button',event::click},interval:2000%>

If you wantrender_async to render the request on load, you can passstart: true. Passing thestart option inside thetoggle hash will triggerrender_async on page load. You can then toggle off polling by interactingwith the element you specified. An example:

<ahref='#'id='comments-button'>Toggle comments loading</a><%=render_asynccomments_path,toggle:{selector:'#comments-button',event::click,start:true},interval:2000%>

In the example above, the comments will load as soon as the page is rendered.Then, you can stop polling for comments by clicking the "Toggle commentsloading" button.

Polling

You can callrender_async with interval argument. This will make render_asynccall specified path at the specified interval.

By doing this:

<%= render_async comments_path, interval: 5000%>

You are tellingrender_async to fetch comments_path every 5 seconds.

This can be handy if you want to enable polling for a specific URL.

⚠️ By passing interval torender_async, the initial container elementwill remain in the HTML tree and it will not be replaced with request response.You can handle how that container element is rendered and its style bypassing in an HTML element name andHTML element class.

Controlled polling

You can controllerrender_asyncpolling in 2 manners.First one is pretty simple, and it involves using thetogglefeature. To do this, you can follow instructions in thecontrol polling with a toggle section.

The second option is more advanced and it involves emitting events to therender_async'scontainer element. From your code, you can emit the following events:

  • 'async-stop' - this will stop polling
  • 'async-start' - this will start polling.

💡 Please note that events need to be dispatched to a render_async container.

An example of how you can do this looks like this:

<%= render_async wave_render_async_path,                 container_id: 'controllable-interval', # set container_id so we can get it later easily                 interval: 3000%><buttonid='stop-polling'>Stop polling</button><buttonid='start-polling'>Start polling</button><script>varcontainer=document.getElementById('controllable-interval')varstopPolling=document.getElementById('stop-polling')varstartPolling=document.getElementById('start-polling')vartriggerEventOnContainer=function(eventName){varevent=newEvent(eventName);container.dispatchEvent(event)}stopPolling.addEventListener('click',function(){container.innerHTML='<p>Polling stopped</p>'triggerEventOnContainer('async-stop')})startPolling.addEventListener('click',function(){triggerEventOnContainer('async-start')})</script>

We are rendering two buttons - "Stop polling" and "Start polling". Then, weattach an event listener to catch any clicking on the buttons. When the buttonsare clicked, we either stop the polling or start the polling, depending on whichbutton a user clicks.

Handling errors

render_async lets you handle errors by allowing you to pass inerror_messageanderror_event_name.

  • error_message

    passing anerror_message will render a message if the AJAX requests fails forsome reason

    <%= render_async users_path,                 error_message: '<p>Sorry, users loading went wrong :(</p>'%>
  • error_event_name

    callingrender_async witherror_event_name will dispatch event in the caseof an error with your AJAX call.

    <%= render_asyc users_path, error_event_name: 'users-error-event'%>

    You can then catch the event in your code with:

    document.addEventListener('users-error-event',function(){// I'm on it})

Caching

render_async can utilize view fragment caching to avoid extra AJAX calls.

In your views (e.g.app/views/comments/show.html.erb):

# note 'render_async_cache' instead of standard 'render_async'<%= render_async_cache comment_stats_path%>

Then, in the partial (e.g.app/views/comments/_comment_stats.html.erb):

<% cache render_async_cache_key(request.path), skip_digest: true do%><divclass="col-md-6"><%=@stats%></div><%end%>
  • The first time the page renders, it will make the AJAX call.
  • Any other times (until the cache expires), it will render from cacheinstantly, without making the AJAX call.
  • You can expire cache simply by passing:expires_in in your view whereyou cache the partial

Doing non-GET requests

By default,render_async creates AJAX GET requests for the path you provide.If you want to change this behaviour, you can pass in amethod argument torender_async view helper.

<%= render_async users_path, method: 'POST'%>

You can also setbody andheaders of the request if you need them.

<%= render_async users_path,                 method: 'POST',                 data: { fresh: 'AF' },                 headers: { 'Content-Type': 'text' }%>

Using with Turbolinks

On Turbolinks applications, you may experience caching issues when navigatingaway from, and then back to, a page with arender_async call on it. This willlikely show up as an empty div.

If you're using Turbolinks 5 or higher, you can resolve this by setting Turbolinksconfiguration ofrender_async to true:

RenderAsync.configuredo |config|config.turbolinks=true# Enable this option if you are using Turbolinks 5+end

This way, you're not breaking Turbolinks flow of loading or reloading a page.It is more efficient than the next option below.

Another option:If you want, you can tell Turbolinks to reload yourrender_async call as follows:

<%= render_async events_path, html_options: { 'data-turbolinks-track': 'reload' }%>

This will reload the whole page with Turbolinks.

💡 If Turbolinks is misbehaving in some way, make sure to put<%= content_for :render_async %> in your base view file inthe<body> and not the<head>.

Using with Turbo

On Turbo applications, you may experience caching issues when navigatingaway from, and then back to, a page with arender_async call on it. This willlikely show up as an empty div.

If you're using Turbo, you can resolve this by setting Turboconfiguration ofrender_async to true:

RenderAsync.configuredo |config|config.turbo=true# Enable this option if you are using Turboend

This way, you're not breaking Turbos flow of loading or reloading a page.It is more efficient than the next option below.

Another option:If you want, you can tell Turbo to reload yourrender_async call as follows:

<%= render_async events_path, html_options: { 'data-turbo-track': 'reload' }%>

This will reload the whole page with Turbo.

💡 If Turbo is misbehaving in some way, make sure to put<%= content_for :render_async %> in your base view file inthe<body> and not the<head>.

Using with respond_to and JS format

If you need to restrict the action to only respond to AJAX requests, you'lllikely wrap it insiderespond_to/format.js blocks like this:

defcomment_statsrespond_todo |format|format.jsdo@stats=Comment.get_statsrenderpartial:"comment_stats"endendend

When you do this, Rails will sometimes set the response'sContent-Type headertotext/javascript. This causes the partial not to be rendered in the HTML.This usually happens when there's browser caching.

You can get around it by specifying the content type totext/html in therender call:

renderpartial:"comment_stats",content_type:'text/html'

Nested async renders

It is possible to nest async templates within other async templates. When doingso, anothercontent_for is required to ensure the JavaScript needed to loadnested templates is included.

For example:

<%# app/views/comments/show.html.erb%><%= render_async comment_stats_path%>
<%# app/views/comments/_comment_stats.html.erb%><divclass="col-md-6"><%=@stats%></div><divclass="col-md-6"><%=render_asynccomment_advanced_stats_path%></div><%=content_for:render_async%>

Customizing the content_for name

Thecontent_for name may be customized by passing thecontent_for_nameoption torender_async. This option is especially useful when doingnested asyncrenders to better control the location of the injected JavaScript.

For example:

<%= render_async comment_stats_path, content_for_name: :render_async_comment_stats%><%= content_for :render_async_comment_stats%>

Configuration options

render_async renders Vanilla JS (regular JavaScript, non-jQuery code)by default in order to fetch the request from the server.

If you wantrender_async to use jQuery code, you need to configure it to doso.

You can configure it by doing the following anywhere before you callrender_async:

RenderAsync.configuredo |config|config.jquery=true# This will render jQuery code, and skip Vanilla JS code. The default value is false.config.turbolinks=true# Enable this option if you are using Turbolinks 5+. The default value is false.config.turbo=true# Enable this option if you are using Turbo. The default value is false.config.replace_container=false# Set to false if you want to keep the placeholder div element from render_async. The default value is true.config.nonces=true# Set to true if you want render_async's javascript_tag always to receive nonce: true. The default value is false.end

Also, you can do it like this:

# This will render jQuery code, and skip Vanilla JS codeRenderAsync.configuration.jquery=true

Aside from configuring whether the gem relies on jQuery or VanillaJS, you canconfigure other options:

  • turbolinks option - If you are using Turbolinks 5+, you should enable this option since it supports Turbolinks way of loading data. The default value for this option is false.
  • turbo option - If you are using Turbo, you should enable this option since it supports Turbo way of loading data. The default value for this option is false.
  • replace_container option - If you want render_async to replace its container with the request response, turn this on. You can turn this on globally for all render_async calls, but if you use this option in a specific render_async call, it will override the global configuration. The default value is true.
  • nonces - If you need to pass innonce: true to thejavascript_tag in your application, it might make sense for you to turn this on globally for all render_async calls. To read more about nonces, check outRails' official guide on security. The default value is false.

⚒️ Development

After checking out the repo, runbin/setup to install dependencies. Then, runrake spec to run the tests. You can also runbin/console for an interactiveprompt that will allow you to experiment. To run integration tests, usebin/integration-tests. For more information, check outCONTRIBUTING file, please.

Got any questions or comments about development (or anything else)?Joinrender_async's Discord channeland let's makerender_async even better!

🙏 Contributing

Check outCONTRIBUTING file, please.

Got any issues or difficulties?Joinrender_async's Discord channeland let's makerender_async even better!

📝 License

The gem is available as open source under the terms of theMIT License.

Contributors

Thanks goes to these wonderful people (emoji key):


Nikola Đuza

💬💻📖👀

Colin

💻📖💡

Kasper Grubbe

💻

Sai Ram Kunala

📖

Josh Arnold

💻📖

Elad Shahar

💻💡

Sasha

💻📖

Ernest Surudo

💻

Kurtis Rainbolt-Greene

💻

Richard Schneeman

📖

Richard Venneman

📖

Filipe W. Lima

📖

Jesús Eduardo Clemens Chong

💻

René Klačan

💻📖

Gil Gomes

📖

Khoa Nguyen

💻📖

Preet Sethi

💻

fangxing

💻

Emmanuel Pire

💻📖

Maxim Geerinck

💻

Don

💻

villu164

📖

Mitchell Buckley

💻📖

yhirano55

💻📖

This project follows theall-contributors specification. Contributions of any kind welcome!


[8]ページ先頭

©2009-2025 Movatter.jp