DEV Community

Shivashankar
Shivashankar

Posted on

Prompting user confirmation in stimulus-reflex

Introduction

StimulusReflex along with cable_ready is a wonderful option for creating reactive application.

One issue I face is how to prompt a user before deleting a record. This post is about how to deal this one.

Alt Text


Assume we have tasks list as follows and we need to prompt the user to confirm when he/she tries to remove a record.

Title Status Remove
Fix homepage css issue Mark as resolved Remove
Add new banner Mark as resolved Remove
Iphone support Resolved Remove

Step: 1: create scaffold and migrate

$ rails g scaffold tasks title status
$ rake db:migrate

Step: 2 add stimulus_reflex and cable ready gems in Gemfile

gem 'cable_ready'
gem "stimulus_reflex", "~> 3.2"
 $ bundle install

Step: 3 update app/channels/application_cable/connection.rb

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private
      def find_verified_user
        if verified_user = User.find_by(id: cookies.encrypted[:user_id])
          verified_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

Step: 4 create tasks channel

$ rails g channel Tasks 

Step: 5 update app/channels/tasks_channel.rb

class TasksChannel < ApplicationCable::Channel
  def subscribed
    stream_from "tasks"
  end
end

Step: 6 update app/javascript/channels/tasks_channel.js

import cableReady from 'cable_ready'
import consumer from "./consumer"

consumer.subscriptions.create("TasksChannel", {
  received(data) {
    if (data.cableReady) cableReady.perform(data.operations)
  }
});

Step: 7 generate tasks reflex

rails g stimulus_reflex Tasks

Step: 8 Update app/reflexes/tasks_reflex.rb

class TasksReflex < ApplicationReflex
  include CableReady::Broadcaster

  def update
    id = element.dataset[:id]
    task = Task.find(id)
    task.update(status: "resolved")

    cable_ready["tasks"].text_content(
      selector: "status-col-#{id}",
      text: 'Resolved'
    )
    cable_ready.broadcast
  end

  def remove(id)
    task = Task.find(id)
    task.destroy 

    # in views we have set id for each row
    # cable_ready["tasks"]- here `tasks` denote channel name

    cable_ready["tasks"].remove(
      selector: "row-#{id}"
    )
    cable_ready.broadcast
  end
end

Step: 9 Update app/javascript/controllers/tasks_controller.js

import ApplicationController from './application_controller'
export default class extends ApplicationController {
  remove(event) {
    event.preventDefault();
    // This is where we are prompting the user for confirmation
    const ok = confirm("Are you sure to mark the task as 'resolved'?")
    if(!ok){
      return false;
    }else{
      const el = event.currentTarget
      const id = el.getAttribute("data-id");
      this.stimulate("TasksReflex#delete", id)
    }
  }
}

Step: 9 Update app/controllers/tasks_controller.rb

   class TasksController < ApplicationController
     include CableReady::Broadcaster
   end

app/views/users/index.html.erb

<table>
  <thead>
   <tr> 
      <th>Title</th>
      <th>Status</th> 
      <th>Remove</th> 
   </tr>
  </thead> 
  <tbody>
    <% @tasks.each do |task| %>
      <!-- unique row id  -->
      <tr id="row-<%= task.id %>">
        <td><%= task.title %></td>
        <td id="status-col-<%= task.id %>">
          <!-- directly call StimulusReflex -->
          <%= link_to 'Mark as resolved', '#',  data:{reflex: 'click->TasksReflex#update', id: task.id} %>
        </td>
        <td data-controller="tasks">
          <!-- Call controller instead of Reflex to show confirmation --> 
          <%= link_to 'Remove', '#', data:{action: "click->tasks#remove", id: task.id} %> 
        </td>
      </tr>
    <% end %>
  </tbody> 
</table>

Note: We need to use controller i.e data-action instead of data-reflex in links to show confirmation.

Top comments (1)

Collapse
 
techpeace profile image
Matt Buck

Thanks for the post! I arrived at a slightly different solution.