Skip to content

Commit

Permalink
Turbo Frames (#18)
Browse files Browse the repository at this point in the history
Turbo Frames in Rails. Dynamic adding, editing and deleting records. Some features of these frames.
  • Loading branch information
anko20094 authored Nov 6, 2022
1 parent b95150b commit 1191aab
Show file tree
Hide file tree
Showing 14 changed files with 194 additions and 163 deletions.
8 changes: 4 additions & 4 deletions app/controllers/admin/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ def index
end
end

def edit; end

def create
if params[:archive].present?
UserBulkImportJob.perform_later create_blob, current_user
Expand All @@ -32,12 +30,14 @@ def create
redirect_to admin_users_path
end

def edit; end

def update
if @user.update user_params
flash[:success] = t '.success'
redirect_to admin_users_path
else
render :edit
render :edit, status: :unprocessable_entity
end
end

Expand Down Expand Up @@ -71,4 +71,4 @@ def authorize_user!
authorize(@user || User)
end
end
end
end
74 changes: 49 additions & 25 deletions app/controllers/questions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,69 @@ class QuestionsController < ApplicationController
before_action :authorize_question!
after_action :verify_authorized

def index
@tags = Tag.where(id: params[:tag_ids]) if params[:tag_ids]
@pagy, @questions = pagy Question.all_by_tags(@tags)
@questions = @questions.decorate
end

def show
load_question_answers
end

def new
@question = Question.new
end

def edit; end
def destroy
@question.destroy
respond_to do |format|
format.html do
flash[:success] = t('.success')
redirect_to questions_path, status: :see_other
end

def create
@question = current_user.questions.build question_params
if @question.save
flash[:success] = t('.success')
redirect_to questions_path
else
render :new, status: :unprocessable_entity
format.turbo_stream { flash.now[:success] = t('.success') }
end
end

def edit; end

def update
if @question.update question_params
flash[:success] = t('.success')
redirect_to questions_path
respond_to do |format|
format.html do
flash[:success] = t('.success')
redirect_to questions_path
end

format.turbo_stream do
@question = @question.decorate
flash.now[:success] = t('.success')
end
end
else
render :edit, status: :unprocessable_entity
end
end

def destroy
@question.destroy
flash[:success] = t('.success')
redirect_to questions_path, status: :see_other
def index
@tags = Tag.where(id: params[:tag_ids]) if params[:tag_ids]
@pagy, @questions = pagy Question.all_by_tags(@tags), link_extra: 'data-turbo-frame="pagination_pagy"'
@questions = @questions.decorate
end

def new
@question = Question.new
end

def create
@question = current_user.questions.build question_params
if @question.save
respond_to do |format|
format.html do
flash[:success] = t('.success')
redirect_to questions_path
end

format.turbo_stream do
@question = @question.decorate
flash.now[:success] = t('.success')
end
end
else
render :new
end
end

private
Expand All @@ -61,4 +85,4 @@ def set_question!
def authorize_question!
authorize(@question || Question)
end
end
end
4 changes: 4 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module ApplicationHelper
include Pagy::Frontend

def prepend_flash
turbo_stream.prepend 'flash', partial: 'shared/flash'
end

def pagination(obj)
# rubocop:disable Rails/OutputSafety
raw(pagy_bootstrap_nav(obj)) if obj.pages > 1
Expand Down
59 changes: 31 additions & 28 deletions app/javascript/scripts/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,44 @@ document.addEventListener("turbo:before-cache", function() {
const rerender = function() {
const i18n = Translations[document.querySelector('body').dataset.lang]

document.querySelectorAll('.js-multiple-select').forEach((element) => {
let opts = {
plugins: {
'remove_button': {
title: i18n['remove_button']
document.querySelectorAll('select.js-multiple-select').forEach((element) => {
if(!element.classList.contains('tomselected')) {
let opts = {
plugins: {
'remove_button': {
title: i18n['remove_button']
},
'no_backspace_delete': {},
'restore_on_backspace': {}
},
'no_backspace_delete': {},
'restore_on_backspace': {}
},
valueField: 'id',
labelField: 'title',
searchField: 'title',
create: false,
load: function(query, callback) {
const url = element.dataset.ajaxUrl + '.json?term=' + encodeURIComponent(query)
valueField: 'id',
labelField: 'title',
searchField: 'title',
create: false,
load: function(query, callback) {
const url = element.dataset.ajaxUrl + '.json?term=' + encodeURIComponent(query)

fetch(url)
.then(response => response.json())
.then (json => {
callback(json)
}).catch(() => {
callback()
})
},
render: {
no_results: function(_data, _escape){
return '<div class="no-results">' + i18n['no_results'] + '</div>';
fetch(url)
.then(response => response.json())
.then (json => {
callback(json)
}).catch(() => {
callback()
})
},
render: {
no_results: function(_data, _escape){
return '<div class="no-results">' + i18n['no_results'] + '</div>';
}
}
}
}

const el = new TomSelect(element, opts)
selects.push(el)
const el = new TomSelect(element, opts)
selects.push(el)
}
})
}

document.addEventListener("turbo:load", rerender)
document.addEventListener("turbo:frame-render", rerender)
document.addEventListener("turbo:render", rerender)
40 changes: 20 additions & 20 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
<!DOCTYPE html>
<html lang="<%= I18n.locale %>">
<head>
<meta charset="utf-8">
<title><%= full_title(yield(:page_title)) %></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", media: 'all' %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
</head>
<head>
<meta charset="utf-8">
<title><%= full_title(yield(:page_title)) %></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload", media: 'all' %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
</head>

<body data-lang="<%= I18n.locale %>">
<%= yield :main_menu %>

<main class="container mt-3">
<% flash.each do |k, v| %>
<%= tag.div v, class: "alert alert-#{k}", role: 'alert' %>
<% end %>
<body data-lang="<%= I18n.locale %>">
<%= yield :main_menu %>

<%= yield %>
</main>
</body>
</html>
<main class="container mt-3">
<div id="flash">
<%= render 'shared/flash' %>
</div>

<%= yield %>
</main>
</body>
</html>
52 changes: 27 additions & 25 deletions app/views/questions/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
<%= render 'shared/errors', object: question %>
<%= turbo_frame_tag question do %>
<%= form_with model: question do |f| %>
<%= render 'shared/errors', object: question %>

<%= form_with model: question do |f| %>
<div class="mb-3 row">
<div class="col-sm-2 col-form-label">
<%= f.label :title %>
</div>
<div class="mb-3 row">
<div class="col-sm-2 col-form-label">
<%= f.label :title %>
</div>

<div class="col-sm-10">
<%= f.text_field :title, class: 'form-control' %>
<div class="col-sm-10">
<%= f.text_field :title, class: 'form-control' %>
</div>
</div>
</div>

<div class="mb-3 row">
<div class="col-sm-2 col-form-label">
<%= f.label :body %>
</div>
<div class="mb-3 row">
<div class="col-sm-2 col-form-label">
<%= f.label :body %>
</div>

<div class="col-sm-10">
<%= f.text_area :body, class: 'form-control' %>
<div class="col-sm-10">
<%= f.text_area :body, class: 'form-control' %>
</div>
</div>
</div>

<div class="mb-3 row">
<div class="col-sm-2 col-form-label">
<%= f.label :tags %>
</div>
<div class="mb-3 row">
<div class="col-sm-2 col-form-label">
<%= f.label :tags %>
</div>

<div class="col-sm-10">
<%= f.collection_select :tag_ids, question.tags, :id, :title, {}, multiple: true,
class: 'js-multiple-select js-ajax-select w-100', data: {'ajax-url': '/api/tags'} %>
<div class="col-sm-10">
<%= f.collection_select :tag_ids, question.tags, :id, :title, {}, multiple: true,
class: 'js-multiple-select', data: {'ajax-url': '/api/tags'} %>
</div>
</div>
</div>

<%= f.submit t('global.button.submit'), class: 'btn btn-primary' %>
<%= f.submit t('global.button.submit'), class: 'btn btn-primary' %>
<% end %>
<% end %>
59 changes: 30 additions & 29 deletions app/views/questions/_question.html.erb
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
<article class="card my-3">
<section class="card-header">
<%= question.user.gravatar %>
<%= question.user.name_or_email %>
</section>
<%= turbo_frame_tag question do %>
<article class="card my-3">
<section class="card-header">
<%= question.user.gravatar %>
<%= question.user.name_or_email %>
</section>

<div class="card-body">
<h2><%= link_to question.title, question_path(question) %></h2>
<div class="card-body">
<h2><%= link_to question.title, question_path(question),
data: {turbo_frame: '_top'} %></h2>

<section class="card-text">
<%= tag.time datetime: question.formatted_created_at do %>
<small><%= question.formatted_created_at %></small>
<% end %>

<div class="my-2">
<%= render question.tags %>
</div>
<section class="card-text">
<%= tag.time datetime: question.formatted_created_at do %>
<small><%= question.formatted_created_at %></small>
<% end %>

<p class="my-2">
<%= truncate strip_tags(question.body), length: 150, omission: t('global.text.omission') %>
</p>
</section>
<div class="my-2">
<%= render question.tags %>
</div>

<%= link_to t('global.button.show'), question_path(question), class: 'btn btn-primary' %>
<p class="my-2">
<%= sanitize question.body %>
</p>
</section>

<% if policy(question).edit? %>
<%= link_to t('global.button.edit'), edit_question_path(question), class: 'btn btn-secondary' %>
<% end %>
<% if policy(question).destroy? %>
<%= link_to t('global.button.delete'), question_path(question), class: 'btn btn-danger',
data: {turbo_method: :delete, turbo_confirm: t('global.dialog.you_sure')} %>
<% end %>
</div>
</article>
<% if policy(question).edit? %>
<%= link_to t('global.button.edit'), edit_question_path(question), class: 'btn btn-secondary' %>
<% end %>
<% if policy(question).destroy? %>
<%= link_to t('global.button.delete'), question_path(question), class: 'btn btn-danger',
data: {turbo_method: :delete, turbo_confirm: t('global.dialog.you_sure')} %>
<% end %>
</div>
</article>
<% end %>
2 changes: 2 additions & 0 deletions app/views/questions/create.turbo_stream.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<%= turbo_stream.prepend 'questions', render(@question) %>
<%= prepend_flash %>
Loading

0 comments on commit 1191aab

Please sign in to comment.