å¼ç¤¾ãµã¼ãã¹ã§ãã savanna.io ã¯ãã£ã¨Turbolinksã¨Stimulusã§éçºãã¦ããã®ã§ããããã®åº¦ Hotwireããªãªã¼ã¹ããã*1ã®ã§Turbolinksé¨åãTurboã«ç½®ãæãã¦ã¿ã¾ããããã®éã®ãã£ããã¨ãããã£ããã¨ã®ã¡ã¢ãæ®ãã¦ããã¾ããã¡ã¢æ¸ããªã®ã§éãªã®ã¯ã容赦ãã ããã
åæ
webpackerã使ã£ã¦ã¾ããassets pipelineæ´¾ãç´ ã®webpackãå©ç¨ãã¦ãã人ã¯é©å®èªã¿æ¿ãã¦ãã ãã
Turbolinksã®ã¢ã³ã¤ã³ã¹ãã¼ã«ã¨Turboã®ã¤ã³ã¹ãã¼ã«
- turbo-railsãGemfileã«è¿½å ãã¦bundle install
./bin/rails turbo:install
ããã
ã§åé¡ãªããããã®ã§ããã°ããã§ãturbo:installã失æãããturbo-rails/turbo_with_webpacker.rb ã¨åçã®ãã¨ãæåã§ããã°OKã§ããsavanna.io ã§ã¯TypeScriptãå©ç¨ãã¦ãããããã¾ãããã¾ããã§ãã(turbo:install ã¯app/javascript/application.jsããããã¨ãåæã¨ãã¦ãã)ã
turbolinksãturboã«ç½®æãã
document.addEventListener('turbolinks:load', ...
ã®ããã«ãã¦ããç®æãdocument.addEventListener('turbo:load', ...
ã«ç½®æãã¾ãã
turbolinks-cache-controlãªã©ã¯ãTurboã®ããã¥ã¡ã³ãã«ã¯æ¸ãã¦ããã¾ãããTurboã®ã³ã¼ããã¿ããturbolinksã®ç®æãturboã«æ¸ãæããã ãã§åé¡ãªãããã ã£ãã®ã§ãã®ããã«ãããåä½ãã¾ãããå¤åturbolinksã§ã§ãããã¨ã¯turboã®ããã¥ã¡ã³ãã«æ¸ãã¦ãªãã¦ãã§ããããããªãããªâ¦(æªç¢ºèª)ã
OAuthãã°ã¤ã³ã®å¯¾å¿
savanna.ioã¯OAuthã«ãããã°ã¤ã³ã«å¯¾å¿ãã¦ãã¾ããomniauthã®CSRFèå¼±æ§ã«å¯¾å¿ã§ããããã«ããã°ããåãã次ã®ããã«POSTãå©ç¨ããããã«ãã¦ãã¾ããã
= link_to 'twitterã«ãã°ã¤ã³', '/auth/twitter', method: :post
ãã®ã³ã¼ãã§çæãããªã³ã¯ã«ã¢ã¯ã»ã¹ããã¨ãã¾ãé·ç§»ã§ãã¾ãããããã¯ãTurboããã¯formãéåæ対象ã«ãªã£ãã®ãåå ã§ããtwitterã®ãã°ã¤ã³ç¨URLãfetchãããã¨ãã¦preflightãªã¯ã¨ã¹ãã§ã¨ã©ã¼ã«ãªã£ã¦ãã¾ããã
Turboã¯data-turbo=false
å±æ§ãã¤ãããã¨ã§ç¹å®ã®ãªã³ã¯ãformãTurboé対å¿ã«ãããã¨ãã§ãã¾ãã
ããããä¸è¨ã®link_toã«methodãªãã·ã§ã³ãã¤ãããã®ã«ã¤ãã¦ã¯ãã¾ãdata-turbo=false
å±æ§ãã¤ãããã¨ãã§ãã¾ãããmethodãªãã·ã§ã³ãã¤ãã¦ãããªã³ã¯ãã¯ãªãã¯ããã¨rails-ujsãèªåçã«formãä½ãsubmitãããã¨ããå®è£
ã«ãªã£ã¦ãã¾ããã¤ã¾ãèªåã§çæãããformã«å¯¾ãã¦data-turbo=false
ãä»ããå¿
è¦ãããã®ã§ããããããè¡ãããã®APIãããã¾ããã
ãªã®ã§link_toãbutton_toã«å¤æ´ãã¦å¯¾å¿ãã¾ããã
= button_to('/auth/twitter') { 'Twitterã§ãã°ã¤ã³' }
ããªãã¼ã·ã§ã³ã¨ã©ã¼æã®å¯¾å¿
Turboã¯Turbolinksã§ã¯æªå¯¾å¿ã ã£ãformã®submitã«å¯¾å¿ãã¦ãã¾ããdata-remote=true
ãªformã§ãªãã¨ãèªåã§fetchã使ãéåæã§POSTãã¾ãã
ããªãã¼ã·ã§ã³ã¨ã©ã¼æã«422ãªã©ã®ã¹ãã¼ã¿ã¹ã³ã¼ããè¿ãã¨ãURLã¯ãã®ã¾ã¾ã®ç¶æ ã§ç»é¢ã®å·®ãæ¿ããè¡ããã¾ããã¤ã¾ãæ®éã«(scaffoldã§çæãããããªã¢ã¯ã·ã§ã³ã®å½¢ã§)ããªãã¼ã·ã§ã³ã¨ã©ã¼ã®ã¡ãã»ã¼ã¸ãå«ãç»é¢ãæç»ã§ããããã«ãªãã¾ããã
Turboæ¹å¼ã¨Turbolinksæ¹å¼ãæ··å¨ããã
savanna.io ã§ã¯ ajax_error_rendererã¨ããgemã使ãSJRã§ã¨ã©ã¼ã¡ãã»ã¼ã¸ãå·®ãè¾¼ããã¨ãããã¨ããã£ã¦ããã®ã§ãããæ®éã®ã¨ã©ã¼ã¡ãã»ã¼ã¸ç¨ã®HTMLãè¿ãããã«å¤æ´ããå¿ è¦ãããã¾ãã
ãã¹ã¦ã®ã¢ã¯ã·ã§ã³ãä¸æ°ã«å¤æ´ããã®ã¯å¤§å¤ãªã®ã§ãå¾ã ã«åãæ¿ããããããã«ãTurboæ¹å¼ã¨Turbolinksæ¹å¼ãæ··å¨ããããããããªä»çµã¿ãä½ãã¾ããã
Turbolinksã¯rails-ujsã«ããajaxã§POSTãããæ³å®ã§ãããã®ã¨ãredirectããããTurbolinks.visit()ãå®è¡ããããã«ãµã¼ãããã¬ã¹ãã³ã¹ãè¿ãã¾ãããããturbolinks-railsã¨ããgemãå®ç¾ãã¦ãã¾ããã次ã®ã³ã¼ãã¯ãturbolinks-railsã¨åçã®ãã¨ãTurboã§å®ç¾ããããã«ãã¦ãã¾ãããã®ããã«æ¸ãã¨ãdata-remote=true
ãªformããPOSTããã¨ãã¯turbolinksã®æåã§ããã以å¤ã¯Turboã®æåã«ãªãã¾ãã
# ApplicationController def redirect_to(...) super.tap do visit_location_with_turbo(location) if request.xhr? && !request.get? && !request.format.turbo_stream? end end def visit_location_with_turbo(location) visit_options = { action: 'replace' } script = [] script << 'Turbo.clearCache()' script << "Turbo.visit(#{location.to_json}, #{visit_options.to_json})" self.status = 200 self.response_body = script.join("\n") response.content_type = 'text/javascript' response.headers['X-Xhr-Redirect'] = location end
request.format.turbo_stream?
ãtrueã®ã¨ãã¯Turboããã®ãªã¯ã¨ã¹ãçµç±ã®ãªãã¤ã¬ã¯ããªã®ã§ãªã«ãããªã(é常ã®redirect_to)ããã«ãã¦ãã¾ãã
// application.ts import { Turbo } from '@hotwired/turbo-rails' window.Turbo = Turbo
Turboã¯webpackç°å¢ã§ã¯globalã§ã¯ãªãã®ã§æ示çã«globalå¤æ°ã¨ãã¦æ±ããããã«ããå¿ è¦ãããã¾ãã
Turboã§ã¯ããªãã¼ã·ã§ã³ã¨ã©ã¼æã«turbo:loadãçºç«ããªã
ããã§ãã¨ã¯é å¼µã£ã¦ããªãã¼ã·ã§ã³ã¨ã©ã¼æã®å¦çãç½®ãæãã¦ããã ãâ¦ã¨æããããããªãã¨ã¯ããã¾ããï¼ï¼ããªãã¼ã·ã§ã³ã¨ã©ã¼æã«turbo:load
ãçºç«ããªãã¨ããç¾è±¡ã«ééãã¾ãããhttps://github.com/hotwired/turbo/issues/85 ãè¦ãéãããã¯ä»æ§ã¨ã®ãã¨ã§ãâ¦ã
savanna.ioã§ã¯turbo:loadããã¹ã¦ã®ãã¼ã¸è¡¨ç¤ºã®ã¿ã¤ãã³ã°ã§çºç«ããã®ãæå¾ ãã¦ããã³ã¼ãããã£ãã®ã§ãæ¤è¨ããçµæãä¸è¨ã®ãTurboæ¹å¼ã¨Turbolinksæ¹å¼ãæ··å¨ããããã§æ¸ããããæ¹ã¯ä¸æ¡ç¨ã«ãã¾ãã*2ã
çµå±ãturbo-streamãå©ç¨ããã¨ajax_error_rendererã¨ä½¿ç¨æãã»ã¼åãã«ãªãã®ã§turbo-streamã§ã¨ã©ã¼ã¡ãã»ã¼ã¸ãå·®ãè¾¼ãããã«ãã¾ããã次ã®ããã«ãã¦render_errors_by_turbo_stream
ãå¼ã³åºãã¨<div id="erorr"></div>
ã«ã¨ã©ã¼ã¡ãã»ã¼ã¸ãå·®ãè¾¼ã¾ãã¾ãã
def render_errors_by_turbo_stream(model:) render turbo_stream: turbo_stream.update(:error, partial: 'errors', locals: { model: model }), status: :unprocessable_entity end
- unless model.errors.empty? #error_explanation.alert.alert-danger.error-messages %ul - model.errors.full_messages.each do |msg| %li= msg
[ã³ã©ã ]ããªãã¼ã·ã§ã³ã¨ã©ã¼æã®ãªãã¼ãã®å¦ç
ããªãã¼ã·ã§ã³ã¨ã©ã¼æã«ãã©ã¦ã¶ããªãã¼ãããã¨ãã®æåã¯Turbo使ç¨æã¨ããã§ãªãã¨ãã¨ã§éããããã¾ããTurbo使ç¨æã¯formã®ãã¼ã¸ããªãã¼ãããã¾ããTurboæªä½¿ç¨æã¯ããã©ã¼ã ã®å 容ãåéãã¾ããï¼ãã¨ãã£ããã¤ã¢ãã°ã表示ããã¾ãã
ããã¯Histroy APIãå©ç¨ãã¦ãã以ä¸ä»æ¹ããªã*3ã§ãããTurbolinksã®ã¨ãã¨æ¯ã¹ãã¨ã ãã¶æ¹åãããæããããã¾ããTurbolinkså©ç¨æãã¤data-remote=trueãªããªãã©ã¼ã ã®ã¨ãã¯ãnewã¢ã¯ã·ã§ã³ã®formã«ããã¯ãããªãã¼ãããã¨indexã¢ã¯ã·ã§ã³ã«ã¢ã¯ã»ã¹ãã¦ãã¾ããã¨ããäºãèµ·ãã¦ãã¾ããã
data-disable-withã«å¯¾å¿ãã
turbo-streamã使ãã¨åçã«ã¨ã©ã¼ã¡ãã»ã¼ã¸ãå·®ãè¾¼ããã¨ãã§ãã¦ãã§ãããã§ããããã¨æããããããªãã¼ã·ã§ã³ã¨ã©ã¼æã«submitãã¿ã³ãdisableã®ã¾ã¾ãã¨ããç¶æ³ã«ééãã¾ããã
form_with
ãªã©ã§ajaxãå©ç¨ãã¦POSTããã¨ããajaxçµäºæã«èªåã§disableç¶æ
ã解é¤ããããã«rails-ujsã§å®ç¾©ããã¦ãã¾ããturboã¯rails-ujsãå©ç¨ãã¦ããªãã®ã§ãç¬èªã§å¯¾å¿ããå¿
è¦ãããã¾ãã次ã®ããã«ããã©ã¼ã submitå¾ã®ããªãã¼ã·ã§ã³ã¨ã©ã¼ã§ãrails-ujsã®disableç¶æ
ã解é¤ããããã®ã¡ã½ãããå¼ã³åºãããã«ãã¾ããã
document.addEventListener('turbo:submit-end', (event: Event) => { if (!event.detail.success) { Rails.enableElement(event.detail.formSubmission.formElement) } })
assets precompileã®å¯¾è±¡ããå¤ã
turbo-railsã¯ä»ã®ã¨ããassets precompileã§ãããªããturboããã®å¯¾è±¡ã¨ãã¦èªåã§æ¿å ¥ããã¨ããä»æ§ã«ãªã£ã¦ãã¾ããsavanna.io ã¯åºæ¬webpackerãªã®ã§ããassetsã使ã£ã¦ããgem(administrate)ãããã®ã§assets pipelineã¯ãªãã«ãã¦ãã¾ãããããã¦turbo-railsãæä¾ãã¦ããjsã¯ES6ã®æ¸ãæ¹ãªã®ã§ãæ¬çªãããã¤æã«uglifierã«ããå§ç¸®å¦çã§å¤±æãã¾ãããããããassetsã¨ãã¦æä¾ããã¦ããjsã¯ä½¿ããªãã®ã§ã次ã®ããã«ãã¦å¯¾å¦ãã¾ããã
# config/application.rb config.after_initialize do config.assets.precompile -= ['turbo'] end
ãã®ä»turbolinksãrails-ujsã«ä¾åãã¦ããå¦çãæ¸ãæãã
ãã¨ã¯ç´°ããå¤æ´ãªã®ã§æ¦è¦ã ãæ¸ãã¦ããã¾ãã
- turbolinksåã§google-analyticsã使ãããã« google-analytics-turbolinkã¨ããgemã使ã£ã¦ããã®ãããã
- ãä¿åããã¦ããªãå¤æ´ãããã¾ãã移åãã¦ããããã§ããï¼ããå®è£ ããããã«rails-ujsã®ã¤ãã³ããhookãã¦ããã®ãturboã®ã¤ãã³ãã«ããã¯ããããã«ãã
ã¾ã¨ã
- ããã¾ã§è¦ã¦ããããã«ãTurbolinksããTurboã®ç§»è¡ã«é¢ãã¦ã¯ãããªãã«ãããã©ãããããã¾ã
- ãã æããã«Turbolinksæ代ããæ¹åãé²ãã§ãã¦ä½é¨ãè¯ããªã£ã¦ããã®ã§ãæ°ã«ãªã£ã¦ãã人ã¯ä¸åº¦è©¦ãã¦ã¿ãã®ããããããã¾ã