One place for hosting & domains

      How To Add Stimulus to a Ruby on Rails Application


      Introduction

      If you are working with a Ruby on Rails project, your requirements may include some interactivity with the HTML generated by your view templates. If so, you have a few choices for how to implement this interactivity.

      For example, you could implement a JavaScript framework like React or Ember. If your requirements include handling state on the client side, or if you are concerned about performance issues associated with frequent queries to the server, then choosing one of these frameworks may make sense. Many Single Page Applications (SPAs) take this approach.

      However, there are several considerations to keep in mind when implementing a framework that manages state and frequent updates on the client side:

      1. It’s possible for loading and conversion requirements — things like parsing JavaScript, and fetching and converting JSON to HTML — to limit performance.
      2. Commitment to a framework may involve writing more code than your particular use case requires, particularly if you are looking for small-scale JavaScript enhancements.
      3. State managed on both the client and server side can lead to a duplication of efforts, and increases the surface area for errors.

      As an alternative, the team at Basecamp (the same team that wrote Rails) has created Stimulus.js, which they describe as “a modest JavaScript framework for the HTML you already have.” Stimulus is meant to enhance a modern Rails application by working with server-side generated HTML. State lives in the Document Object Model (DOM), and the framework offers standard ways of interacting with elements and events in the DOM. It works side by side with Turbolinks (included in Rails 5+ by default) to improve performance and load times with code that is limited and scoped to a clearly defined purpose.

      In this tutorial, you will install and use Stimulus to build on an existing Rails application that offers readers information about sharks. The application already has a model for handling shark data, but you will add a nested resource for posts about individual sharks, allowing users to build out a body of thoughts and opinions about sharks. This piece runs roughly parallel to How To Create Nested Resources for a Ruby on Rails Application, except that we will be using JavaScript to manipulate the position and appearance of posts on the page. We will also take a slightly different approach to building out the post model itself.

      Prerequisites

      To follow this tutorial, you will need:

      • A local machine or development server running Ubuntu 18.04. Your development machine should have a non-root user with administrative privileges and a firewall configured with ufw. For instructions on how to set this up, see our Initial Server Setup with Ubuntu 18.04 tutorial.
      • Node.js and npm installed on your local machine or development server. This tutorial uses Node.js version 10.16.3 and npm version 6.9.0. For guidance on installing Node.js and npm on Ubuntu 18.04, follow the instructions in the “Installing Using a PPA” section of How To Install Node.js on Ubuntu 18.04.
      • Ruby, rbenv, and Rails installed on your local machine or development server, following Steps 1-4 in How To Install Ruby on Rails with rbenv on Ubuntu 18.04. This tutorial uses Ruby 2.5.1, rbenv 1.1.2, and Rails 5.2.3.
      • SQLite installed, and a basic shark information application created, following the directions in How To Build a Ruby on Rails Application.

      Step 1 — Creating a Nested Model

      Our first step will be to create a nested Post model, which we will associate with our existing Shark model. We will do this by creating Active Record associations between our models: posts will belong to particular sharks, and each shark can have multiple posts.

      To get started, navigate to the sharkapp directory that you created for your Rails project in the prerequisites:

      To create our Post model, we’ll use the rails generate command with the model generator. Type the following command to create the model:

      • rails generate model Post body:text shark:references

      With body:text, we’re telling Rails to include a body field in the posts database table — the table that maps to the Post model. We’re also including the :references keyword, which sets up an association between the Shark and Post models. Specifically, this will ensure that a foreign key representing each shark entry in the sharks database is added to the posts database.

      Once you have run the command, you will see output confirming the resources that Rails has generated for the application. Before moving on, you can check your database migration file to look at the relationship that now exists between your models and database tables. Use the following command to look at the contents of the file, making sure to substitute the timestamp on your own migration file for what’s shown here:

      • cat db/migrate/20190805132506_create_posts.rb

      You will see the following output:

      Output

      class CreatePosts < ActiveRecord::Migration[5.2] def change create_table :posts do |t| t.text :body t.references :shark, foreign_key: true t.timestamps end end end

      As you can see, the table includes a column for a shark foreign key. This key will take the form of model_name_id — in our case, shark_id.

      Rails has established the relationship between the models elsewhere as well. Take a look at the newly generated Post model with the following command:

      Output

      class Post < ApplicationRecord belongs_to :shark end

      The belongs_to association sets up a relationship between models in which a single instance of the declaring model belongs to a single instance of the named model. In the case of our application, this means that a single post belongs to a single shark.

      Though Rails has already set the belongs_to association in our Post model, we will need to specify a has_many association in our Shark model as well in order for that relationship to function properly.

      To add the has_many association to the Shark model, open app/models/shark.rb using nano or your favorite editor:

      Add the following line to the file to establish the relationship between sharks and posts:

      ~/sharkapp/app/models/shark.rb

      class Shark < ApplicationRecord
        has_many :posts
        validates :name, presence: true, uniqueness: true
        validates :facts, presence: true
      end
      

      One thing that is worth thinking about here is what happens to posts once a particular shark is deleted. We likely do not want the posts associated with a deleted shark persisting in the database. To ensure that any posts associated with a given shark are eliminated when that shark is deleted, we can include the dependent option with the association.

      Add the following code to the file to ensure that the destroy action on a given shark deletes any associated posts:

      ~/sharkapp/app/models/shark.rb

      class Shark < ApplicationRecord
        has_many :posts , dependent: :destroy
        validates :name, presence: true, uniqueness: true
        validates :facts, presence: true
      end
      

      Once you have finished making these changes, save and close the file. If you are working with nano, do this by pressing CTRL+X, Y, then ENTER.

      You now have a model generated for your posts, but you will also need a controller to coordinate between the data in your database and the HTML that’s generated and presented to users.

      Step 2 — Creating a Controller for a Nested Resource

      Creating a posts controller will involve setting a nested resource route in the application’s main routing file and creating the controller file itself to specify the methods we want associated with particular actions.

      To begin, open your config/routes.rb file to establish the relationship between your resourceful routes:

      Currently, the file looks like this:

      ~/sharkapp/config/routes.rb

      Rails.application.routes.draw do
        resources :sharks
      
        root 'sharks#index'
        # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
      end
      

      We want to create a dependent relationship relationship between shark and post resources. To do this, update your route declaration to make :sharks the parent of :posts. Update the code in the file to look like the following:

      ~/sharkapp/config/routes.rb

      Rails.application.routes.draw do
        resources :sharks do
          resources :posts
        end
        root 'sharks#index'
        # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
      end
      

      Save and close the file when you are finished editing.

      Next, create a new file called app/controllers/posts_controller.rb for the controller:

      • nano app/controllers/posts_controller.rb

      In this file, we’ll define the methods that we will use to create and destroy individual posts. However, because this is a nested model, we’ll also want to create a local instance variable, @shark, that we can use to associate particular posts with specific sharks.

      First, we can create the PostsController class itself, along with two private methods: get_shark, which will allow us to reference a particular shark, and post_params, which gives us access to user-submitted information by way of the params method.

      Add the following code to the file:

      ~/sharkapp/app/controllers/posts_controller.rb

      class PostsController < ApplicationController
        before_action :get_shark 
      
        private
      
        def get_shark
          @shark = Shark.find(params[:shark_id])
        end
      
        def post_params
          params.require(:post).permit(:body, :shark_id)
        end
      end
      

      You now have methods to get the particular shark instances with which your posts will be associated, using the :shark_id key, and the data that users are inputting to create posts. Both of these objects will now be available for the methods you will define to handle creating and destroying posts.

      Next, above the private methods, add the following code to the file to define your create and destroy methods:

      ~/sharkapp/app/controllers/posts_controller.rb

      . . .
        def create
          @post = @shark.posts.create(post_params)
        end
      
        def destroy
          @post = @shark.posts.find(params[:id])
          @post.destroy   
        end
      . . .
      

      These methods associate @post instances with particular @shark instances, and use the collection methods that became available to us when we created the has_many association between sharks and posts. Methods such as find and create allow us to target the collection of posts associated with a particular shark.

      The finished file will look like this:

      ~/sharkapp/app/controllers/posts_controller.rb

      class PostsController < ApplicationController
        before_action :get_shark 
      
        def create
          @post = @shark.posts.create(post_params)
        end
      
        def destroy
          @post = @shark.posts.find(params[:id])
          @post.destroy   
        end
      
        private
      
        def get_shark
          @shark = Shark.find(params[:shark_id])
        end
      
        def post_params
          params.require(:post).permit(:body, :shark_id)
        end
      end
      

      Save and close the file when you are finished editing.

      With your controller and model in place, you can begin thinking about your view templates and how you will organize your application’s generated HTML.

      Step 3 — Reorganizing Views with Partials

      You have created a Post model and controller, so the last thing to think about from a Rails perspective will be the views that present and allow users to input information about sharks. Views are also the place where you will have a chance to build out interactivity with Stimulus.

      In this step, you will map out your views and partials, which will be the starting point for your work with Stimulus.

      The view that will act as the base for posts and all partials associated with posts is the sharks/show view.

      Open the file:

      • nano app/views/sharks/show.html.erb

      Currently, the file looks like this:

      ~/sharkapp/app/views/sharks/show.html.erb

      <p id="notice"><%= notice %></p>
      
      <p>
        <strong>Name:</strong>
        <%= @shark.name %>
      </p>
      
      <p>
        <strong>Facts:</strong>
        <%= @shark.facts %>
      </p>
      
      <%= link_to 'Edit', edit_shark_path(@shark) %> |
      <%= link_to 'Back', sharks_path %>
      

      When we created our Post model, we opted not to generate views for our posts, since we will handle them through our sharks/show view. So in this view, the first thing we will address is how we will accept user input for new posts, and how we will present posts back to the user.

      Note: For an alternative to this approach, please see How To Create Nested Resources for a Ruby on Rails Application, which sets up post views using the full range of Create, Read, Update, Delete (CRUD) methods defined in the posts controller. For a discussion of these methods and how they work, please see Step 3 of How To Build a Ruby on Rails Application.

      Instead of building all of our functionality into this view, we will use partials — reusable templates that serve a particular function. We will create one partial for new posts, and another to control how posts are displayed back to the user. Throughout, we’ll be thinking about how and where we can use Stimulus to manipulate the appearance of posts on the page, since our goal is to control the presentation of posts with JavaScript.

      First, below shark facts, add an <h2> header for posts and a line to render a partial called sharks/posts:

      ~/sharkapp/app/views/sharks/show.html.erb

      . . . 
      <p>
        <strong>Facts:</strong>
        <%= @shark.facts %>
      </p>
      
      <h2>Posts</h2>
      <%= render 'sharks/posts' %>
      . . . 
      

      This will render the partial with the form builder for new post objects.

      Next, below the Edit and Back links, we will add a section to control the presentation of older posts on the page. Add the following lines to the file to render a partial called sharks/all:

      ~/sharkapp/app/views/sharks/show.html.erb

      <%= link_to 'Edit', edit_shark_path(@shark) %> |
      <%= link_to 'Back', sharks_path %>
      
      <div>
        <%= render 'sharks/all' %>
      </div>
      

      The <div> element will be useful when we start integrating Stimulus into this file.

      Once you are finished making these edits, save and close the file. With the changes you’ve made on the Rails side, you can now move on to installing and integrating Stimulus into your application.

      Step 4 — Installing Stimulus

      The first step in using Stimulus will be to install and configure our application to work with it. This will include making sure we have the correct dependencies, including the Yarn package manager and Webpacker, the gem that will allow us to work with the JavaScript pre-processor and bundler webpack. With these dependencies in place, we will be able to install Stimulus and use JavaScript to manipulate events and elements in the DOM.

      Let’s begin by installing Yarn. First, update your package list:

      Next, add the GPG key for the Debian Yarn repository:

      • curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -

      Add the repository to your APT sources:

      • echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list

      Update the package database with the newly added Yarn packages:

      And finally, install Yarn:

      With yarn installed, you can move on to adding the webpacker gem to your project.

      Open your project’s Gemfile, which lists the gem dependencies for your project:

      Inside the file, you will see Turbolinks enabled by default:

      ~/sharkapp/Gemfile

      . . . 
      # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
      gem 'turbolinks', '~> 5'
      . . . 
      

      Turbolinks is designed to improve performance by optimizing page loads: instead of having link clicks navigate to a new page, Turbolinks intercepts these click events and makes the page request using Asynchronous JavaScript and HTML (AJAX). It then replaces the body of the current page and merges the contents of the <head> sections, while the JavaScript window and document objects and the <html> element persist between renders. This addresses one of the main causes of slow page load times: the reloading of CSS and JavaScript resources.

      We get Turbolinks by default in our Gemfile, but we will need to add the webpacker gem so that we can install and use Stimulus. Below the turbolinks gem, add webpacker:

      ~/sharkapp/Gemfile

      . . . 
      # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
      gem 'turbolinks', '~> 5'
      gem 'webpacker', '~> 4.x'
      . . . 
      

      Save and close the file when you are finished.

      Next, add the gem to your project’s bundle with the bundle command:

      This will generate a new Gemfile.lock file — the definitive record of gems and versions for your project.

      Next, install the gem in the context of your bundle with the following bundle exec command:

      • bundle exec rails webpacker:install

      Once the installation is complete, we will need to make one small adjustment to our application’s content security file. This is due to the fact that we are working with Rails 5.2+, which is a Content Security Policy (CSP) restricted environment, meaning that the only scripts allowed in the application must be from trusted sources.

      Open config/initializers/content_security_policy.rb, which is the default file Rails gives us for defining application-wide security policies:

      • nano config/initializers/content_security_policy.rb

      Add the following lines to the bottom of the file to allow webpack-dev-server — the server that serves our application’s webpack bundle — as an allowed origin:

      ~/sharkapp/config/initializers/content_security_policy.rb

      . . . 
      Rails.application.config.content_security_policy do |policy|
        policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035' if Rails.env.development?
      end
      

      This will ensure that the webpacker-dev-server is recognized as a trusted asset source.

      Save and close the file when you are finished making this change.

      By installing webpacker, you created two new directories in your project’s app directory, the directory where your main application code is located. The new parent directory, app/javascript, will be where your project’s JavaScript code will live, and it will have the following structure:

      Output

      ├── javascript │ ├── controllers │ │ ├── hello_controller.js │ │ └── index.js │ └── packs │ └── application.js

      The app/javascript directory will contain two child directories: app/javascript/packs, which will have your webpack entry points, and app/javascript/controllers, where you will define your Stimulus controllers. The bundle exec command that we just used will create the app/javascript/packs directory, but we will need to install Stimulus for the app/javascript/controllers directory to be autogenerated.

      With webpacker installed, we can now install Stimulus with the following command:

      • bundle exec rails webpacker:install:stimulus

      You will see output like the following, indicating that the installation was successful:

      Output

      . . . success Saved lockfile. success Saved 5 new dependencies. info Direct dependencies └─ stimulus@1.1.1 info All dependencies ├─ @stimulus/core@1.1.1 ├─ @stimulus/multimap@1.1.1 ├─ @stimulus/mutation-observers@1.1.1 ├─ @stimulus/webpack-helpers@1.1.1 └─ stimulus@1.1.1 Done in 8.30s. Webpacker now supports Stimulus.js 🎉

      We now have Stimulus installed, and the main directories we need to work with it in place. Before moving on to writing any code, we’ll need to make a few application-level adjustments to complete the installation process.

      First, we’ll need to make an adjustment to app/views/layouts/application.html.erb to ensure that our JavaScript code is available and that the code defined in our main webpacker entry point, app/javascript/packs/application.js, runs each time a page is loaded.

      Open that file:

      • nano app/views/layouts/application.html.erb

      Change the following javascript_include_tag tag to javascript_pack_tag to load app/javascript/packs/application.js:

      ~/sharkapp/app/views/layouts/application.html.erb

      . . .
          <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
          <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
      . . . 
      

      Save and close the file when you have made this change.

      Next, open app/javascript/packs/application.js:

      • nano app/javascript/packs/application.js

      Initially, the file will look like this:

      ~/sharkapp/app/javascript/packs/application.js

      . . . 
      console.log('Hello World from Webpacker')
      
      import "controllers"
      

      Delete the boilerplate code that’s there, and add the following code to load your Stimulus controller files and boot the application instance:

      ~/sharkapp/app/javascript/packs/application.js

      . . . 
      import { Application } from "stimulus"
      import { definitionsFromContext } from "stimulus/webpack-helpers"
      
      const application = Application.start()
      const context = require.context("../controllers", true, /.js$/)
      application.load(definitionsFromContext(context))
      

      This code uses webpack helper methods to require the controllers in the app/javascript/controllers directory and load this context for use in your application.

      Save and close the file when you are finished editing.

      You now have Stimulus installed and ready to use in your application. Next, we’ll build out the partials that we referenced in our sharks show view — sharks/posts and sharks/all — using Stimulus controllers, targets, and actions.

      Step 5 — Using Stimulus in Rails Partials

      Our sharks/posts partial will use the form_with form helper to create a new post object. It will also make use of Stimulus’s three core concepts: controllers, targets, and actions. These concepts work as follows:

      • Controllers are JavaScript classes that are defined in JavaScript modules and exported as the module’s default object. Through controllers, you have access to particular HTML elements and the Stimulus Application instance defined in app/javascript/packs/application.js.
      • Targets allow you to reference particular HTML elements by name, and are associated with particular controllers.
      • Actions control how DOM events are handled by controllers, and are also associated with particular controllers. They create a connection between the HTML element associated with the controller, the methods defined in the controller, and a DOM event listener.

      In our partial, we’re first going to build a form as we normally would using Rails. We will then add a Stimulus controller, action, and targets to the form in order to use JavaScript to control how new posts get added to the page.

      First, create a new file for the partial:

      • nano app/views/sharks/_posts.html.erb

      Inside the file, add the following code to create a new post object using the form_with helper:

      ~/sharkapp/app/views/sharks/_posts.html.erb

              <%= form_with model: [@shark, @shark.posts.build] do |form| %>
                      <%= form.text_area :body, placeholder: "Your post here" %>
                      <br>
                      <%= form.submit %>
              <% end %>
      

      So far, this form behaves like a typical Rails form, using the form_with helper to build a post object with the fields defined for the Post model. Thus, the form has a field for the post :body, to which we’ve added a placeholder with a prompt for filling in a post.

      Additionally, the form is scoped to take advantage of the collection methods that come with the associations between the Shark and Post models. In this case, the new post object that’s created from user-submitted data will belong to the collection of posts associated with the shark we’re currently viewing.

      Our goal now is to add some Stimulus controllers, events, and actions to control how the post data gets displayed on the page. The user will ultimately submit post data and see it posted to the page thanks to a Stimulus action.

      First, we’ll add a controller to the form called posts in a <div> element:

      ~/sharkapp/app/views/sharks/_posts.html.erb

      <div data-controller="posts">
              <%= form_with model: [@shark, @shark.posts.build] do |form| %>
                       <%= form.text_area :body, placeholder: "Your post here" %>
                       <br>
                       <%= form.submit %>
              <% end %>
      </div>
      

      Make sure you add the closing <div> tag to scope the controller properly.

      Next, we’ll attach an action to the form that will be triggered by the form submit event. This action will control how user input is displayed on the page. It will reference an addPost method that we will define in the posts Stimulus controller:

      ~/sharkapp/app/views/sharks/_posts.html.erb

      <div data-controller="posts">
              <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody" } do |form| %>
              . . . 
                       <%= form.submit %>
              <% end %>
      </div>
      

      We use the :data option with form_with to submit the Stimulus action as an additional HTML data attribute. The action itself has a value called an action descriptor made up of the following:

      • The DOM event to listen for. Here, we are using the default event associated with form elements, submit, so we do not need to specify the event in the descriptor itself. For more information about common element/event pairs, see the Stimulus documentation.
      • The controller identifier, in our case posts.
      • The method that the event should invoke. In our case, this is the addBody method that we will define in the controller.

      Next, we’ll attach a data target to the user input defined in the :body <textarea> element, since we will use this inputted value in the addBody method.

      Add the following :data option to the :body <textarea> element:

      ~/sharkapp/app/views/sharks/_posts.html.erb

      <div data-controller="posts">
              <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody" } do |form| %>
                      <%= form.text_area :body, placeholder: "Your post here", data: { target: "posts.body" } %>
      . . .
      

      Much like action descriptors, Stimulus targets have target descriptors, which include the controller identifier and the target name. In this case, posts is our controller, and body is the target itself.

      As a last step, we’ll add a data target for the inputted body values so that users will be able to see their posts as soon as they are submitted.

      Add the following <ul> element with an add target below the form and above the closing <div>:

      ~/sharkapp/app/views/sharks/_posts.html.erb

      . . .
              <% end %>
        <ul data-target="posts.add">
        </ul>
      
      </div>
      

      As with the body target, our target descriptor includes both the name of the controller and the target — in this case, add.

      The finished partial will look like this:

      ~/sharkapp/app/views/sharks/_posts.html.erb

      <div data-controller="posts">
              <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody"} do |form| %>
                      <%= form.text_area :body, placeholder: "Your post here", data: { target: "posts.body" } %>
                      <br>
                      <%= form.submit %>
              <% end %>
        <ul data-target="posts.add">
        </ul>
      
      </div>
      

      Once you have made these changes, you can save and close the file.

      You have now created one of the two partials you added to the sharks/show view template. Next, you’ll create the second, sharks/all, which will show all of the older posts from the database.

      Create a new file named _all.html.erb in the app/views/sharks/ directory:

      • nano app/views/sharks/_all.html.erb

      Add the following code to the file to iterate through the collection of posts associated with the selected shark:

      ~/sharkapp/app/views/sharks/_all.html.erb

      <% for post in @shark.posts  %>
          <ul>
      
              <li class="post">
                  <%= post.body %>
              </li>
      
          </ul>
          <% end %>
      

      This code uses a for loop to iterate through each post instance in the collection of post objects associated with a particular shark.

      We can now add some Stimulus actions to this partial to control the appearance of posts on the page. Specifically, we will add actions that will control upvotes and whether or not posts are visible on the page

      Before we do that, however, we will need to add a gem to our project so that we can work with Font Awesome icons, which we’ll use to register upvotes. Open a second terminal window, and navigate to your sharkapp project directory.

      Open your Gemfile:

      Below your webpacker gem, add the following line to include the font-awesome-rails gem in the project:

      ~/sharkapp/Gemfile

      . . . 
      gem 'webpacker', '~> 4.x'
      gem 'font-awesome-rails', '~>4.x'
      . . . 
      

      Save and close the file.

      Next, install the gem:

      Finally, open your application’s main stylesheet, app/assets/stylesheets/application.css:

      • nano app/assets/stylesheets/application.css

      Add the following line to include Font Awesome’s styles in your project:

      ~/sharkapp/app/assets/stylesheets/application.css

      . . . 
      *
       *= require_tree .
       *= require_self
       *= require font-awesome
       */
      

      Save and close the file. You can now close your second terminal window.

      Back in your app/views/sharks/_all.html.erb partial, you can now add two button_tags with associated Stimulus actions, which will be triggered on click events. One button will give users the option to upvote a post and the other will give them the option to remove it from the page view.

      Add the following code to app/views/sharks/_all.html.erb:

      ~/sharkapp/app/views/sharks/_all.html.erb

      <% for post in @shark.posts  %>
          <ul>
      
              <li class="post">
                  <%= post.body %>
                  <%= button_tag "Remove Post", data: { controller: "posts", action: "posts#remove" } %>
                  <%= button_tag "Upvote Post", data: { controller: "posts", action: "posts#upvote" } %>
              </li>
      
          </ul>
          <% end %>
      

      Button tags also take a :data option, so we’ve added our posts Stimulus controller and two actions: remove and upvote. Once again, in the action descriptors, we only need to define our controller and method, since the default event associated with button elements is click. Clicking on each of these buttons will trigger the respective remove and upvote methods defined in our controller.

      Save and close the file when you have finished editing.

      The final change we will make before moving on to defining our controller is to set a data target and action to control how and when the sharks/all partial will be displayed.

      Open the show template again, where the initial call to render sharks/all is currently defined:

      • nano app/views/sharks/show.html.erb

      At the bottom of the file, we have a <div> element that currently looks like this:

      ~/sharkapp/app/views/sharks/show.html.erb

      . . . 
      <div>
        <%= render 'sharks/all' %>
      </div>
      

      First, add a controller to this <div> element to scope actions and targets:

      ~/sharkapp/app/views/sharks/show.html.erb

      . . . 
      <div data-controller="posts">
        <%= render 'sharks/all' %>
      </div>
      

      Next, add a button to control the appearance of the partial on the page. This button will trigger a showAll method in our posts controller.

      Add the button below the <div> element and above the render statement:

      ~/sharkapp/app/views/sharks/show.html.erb

      . . . 
      <div data-controller="posts">
      
      <button data-action="posts#showAll">Show Older Posts</button>
      
        <%= render 'sharks/all' %>
      

      Again, we only need to identify our posts controller and showAll method here — the action will be triggered by a click event.

      Next, we will add a data target. The goal of setting this target is to control the appearance of the partial on the page. Ultimately, we want users to see older posts only if they have opted into doing so by clicking on the Show Older Posts button.

      We will therefore attach a data target called show to the sharks/all partial, and set its default style to visibility:hidden. This will hide the partial unless users opt in to seeing it by clicking on the button.

      Add the following <div> element with the show target and style definition below the button and above the partial render statement:

      ~/sharkapp/app/views/sharks/show.html.erb

      . . . 
      <div data-controller="posts">
      
      <button data-action="posts#showAll">Show Older Posts</button>
      
      <div data-target="posts.show" style="visibility:hidden">
        <%= render 'sharks/all' %>
      </div>
      

      Be sure to add the closing </div> tag.

      The finished show template will look like this:

      ~/sharkapp/app/views/sharks/show.html.erb

      <p id="notice"><%= notice %></p>
      
      <p>
        <strong>Name:</strong>
        <%= @shark.name %>
      </p>
      
      <p>
        <strong>Facts:</strong>
        <%= @shark.facts %>
      </p>
      
      <h2>Posts</h2>
      
      <%= render 'sharks/posts' %>
      
      <%= link_to 'Edit', edit_shark_path(@shark) %> |
      <%= link_to 'Back', sharks_path %>
      
      <div data-controller="posts">
      
      <button data-action="posts#showAll">Show Older Posts</button>
      
      <div data-target="posts.show" style="visibility:hidden">
        <%= render 'sharks/all' %>
      </div>
      </div>
      

      Save and close the file when you are finished editing.

      With this template and its associated partials finished, you can move on to creating the controller with the methods you’ve referenced in these files.

      Step 6 — Creating the Stimulus Controller

      Installing Stimulus created the app/javascript/controllers directory, which is where webpack is loading our application context from, so we will create our posts controller in this directory. This controller will include each of the methods we referenced in the previous step:

      • addBody(), to add new posts.
      • showAll(), to show older posts.
      • remove(), to remove posts from the current view.
      • upvote(), to attach an upvote icon to posts.

      Create a file called posts_controller.js in the app/javascript/controllers directory:

      • nano app/javascript/controllers/posts_controller.js

      First, at the top of the file, extend Stimulus’s built-in Controller class:

      ~/sharkapp/app/javascript/controllers/posts_controller.js

      import { Controller } from "stimulus"
      
      export default class extends Controller {
      }
      

      Next, add the following target definitions to the file:

      ~/sharkapp/app/javascript/controllers/posts_controller.js

      . . .
      export default class extends Controller {
          static targets = ["body", "add", "show"]
      }
      

      Defining targets in this way will allow us to access them in our methods with the this.target-nameTarget property, which gives us the first matching target element. So, for example, to match the body data target defined in our targets array, we would use this.bodyTarget. This property allows us to manipulate things like input values or css styles.

      Next, we can define the addBody method, which will control the appearance of new posts on the page. Add the following code below the target definitions to define this method:

      ~/sharkapp/app/javascript/controllers/posts_controller.js

      . . .
      export default class extends Controller {
          static targets = [ "body", "add", "show"]
      
          addBody() {
              let content = this.bodyTarget.value;
              this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
          }
      }
      

      This method defines a content variable with the let keyword and sets it equal to the post input string that users entered into the posts form. It does this by virtue of the body data target that we attached to the <textarea> element in our form. Using this.bodyTarget to match this element, we can then use the value property that is associated with that element to set the value of content as the post input users have entered.

      Next, the method adds this post input to the add target we added to the <ul> element below the form builder in the sharks/posts partial. It does this using the Element.insertAdjacentHTML() method, which will insert the content of the new post, set in the content variable, before the add target element. We’ve also enclosed the new post in an <li> element, so that new posts appear as bulleted list items.

      Next, below the addBody method, we can add the showAll method, which will control the appearance of older posts on the page:

      ~/sharkapp/app/javascript/controllers/posts_controller.js

      . . . 
      export default class extends Controller {
      . . .
          addBody() {
              let content = this.bodyTarget.value;
              this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
          }
      
          showAll() {
              this.showTarget.style.visibility = "visible";
          }
      
      }
      

      Here, we again use the this.target-nameTarget property to match our show target, which is attached to the <div> element with the sharks/all partial. We gave it a default style, "visibility:hidden", so in this method, we simply change the style to "visible". This will show the partial to users who have opted into seeing older posts.

      Below showAll, we’ll next add an upvote method, to allow users to “upvote” posts on the page by attaching the free Font Awesome check-circle icon to a particular post.

      Add the following code to define this method:

      ~/sharkapp/app/javascript/controllers/posts_controller.js

      . . . 
      export default class extends Controller {
      . . . 
      
          showAll() {
              this.showTarget.style.visibility = "visible";
          }
      
          upvote() {
              let post = event.target.closest(".post");
              post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
          }
      
      }
      

      Here, we’re creating a post variable that will target the closest <li> element with the class post — the class we attached to each <li> element in our loop iteration in sharks/all. This will target the closest post and add the check-circle icon just inside <li> element, after its last child.

      Next, we’ll use a similar method to hide posts on the page. Add the following code below the upvote method to define a remove method:

      ~/sharkapp/app/javascript/controllers/posts_controller.js

      . . . 
      export default class extends Controller {
      . . . 
      
          upvote() {
              let post = event.target.closest(".post");
              post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
          }
      
          remove() {
              let post = event.target.closest(".post");
              post.style.visibility = "hidden";
          }
      }
      

      Once again, our post variable will target the closest <li> element with the class post. It will then set the visibility property to "hidden" to hide the post on the page.

      The finished controller file will now look like this:

      ~/sharkapp/app/javascript/controllers/posts_controller.js

      import { Controller } from "stimulus"
      
      export default class extends Controller {
      
          static targets = ["body", "add", "show"]
      
          addBody() {
              let content = this.bodyTarget.value;
              this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
          }
      
          showAll() {
              this.showTarget.style.visibility = "visible";
          }
      
          upvote() {
              let post = event.target.closest(".post");
              post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
          }
      
          remove() {
              let post = event.target.closest(".post");
              post.style.visibility = "hidden";
          }
      } 
      

      Save and close the file when you are finished editing.

      With your Stimulus controller in place, you can move on to making some final changes to the index view and testing your application.

      Step 7 — Modifying the Index View and Testing the Application

      With one final change to the sharks index view you will be ready to test out your application. The index view is the root of the application, which you set in Step 4 of How To Build a Ruby on Rails Application.

      Open the file:

      • nano app/views/sharks/index.html.erb

      In place of the link_to helpers that were autogenerated for us to display and destroy sharks, we’ll use button_to helpers. This will help us work with generated HTML code instead of the default Rails JavaScript assets, which we specified we would no longer use in Step 1, when we changed javascript_include_tag to javascript_pack_tag in app/views/layouts/application.html.erb.

      Replace the existing link_to helpers in the file with the following button_to helpers:

      ~/sharkapp/app/views/sharks/index.html.erb

      . . . 
        <tbody>
          <% @sharks.each do |shark| %>
            <tr>
              <td><%= shark.name %></td>
              <td><%= shark.facts %></td>
              <td><%= button_to 'Show', shark_path(:id => shark.id), :method => :get %></td>
              <td><%= button_to 'Edit', edit_shark_path(:id => shark.id), :method => :get %></td>
              <td><%= button_to 'Destroy', shark_path(:id => shark.id), :method => :delete %></td>
            </tr>
          <% end %>
        </tbody>
      . . . 
      

      These helpers accomplish much the same things as their link_to counterparts, but the Destroy helper now relies on generated HTML rather than Rails’s default JavaScript.

      Save and close the file when you are finished editing.

      You are now ready to test your application.

      First, run your database migrations:

      Next, start your server. If you are working locally, you can do this with the following command:

      If you are working on a development server, you can start the application with:

      • rails s --binding=your_server_ip

      Navigate to the application landing page in your browser. If you are working locally, this will be localhost:3000, or http://your_server_ip:3000 if you are working on a server.

      You will see the following landing page:

      Application Landing Page

      Clicking on Show will take you to the show view for this shark. Here you will see a form to fill out a post:

      Shark Show Page

      In the post form, type “These sharks are scary!”:

      Filled in Post

      Click on Create Post. You will now see the new post on the page:

      New Post Added to Page

      You can add another new post, if you would like. This time, type “These sharks are often misrepresented in films” and click Create Post:

      Second Post Added to Page

      In order to test the functionality of the Show Older Posts feature, we will need to leave this page, since our Great White does not currently have any posts that are older than the ones we’ve just added.

      Click Back to get to the main page, and then Show to return to the Great White landing page:

      Shark Show Page

      Clicking on Show Older Posts will now show you the posts you created:

      Show Older Posts

      You can now upvote a post by clicking on the Upvote Post button:

      Upvote a Post

      Similarly, clicking Remove Post will hide the post:

      Remove a Post

      You have now confirmed that you have a working Rails application that uses Stimulus to control how nested post resources are displayed on individual shark pages. You can use this as the jumping off point for future development and experimentation with Stimulus.

      Conclusion

      Stimulus represents a possible alternative to working with rails-ujs, JQuery, and frameworks like React and Vue.

      As discussed in the introduction, Stimulus makes the most sense when you need to work directly with HTML generated by the server. It is lightweight, and aims to make code – particularly HTML – self-explanatory to the highest degree possible. If you don’t need to manage state on the client side, then Stimulus may be a good choice.

      If you are interested in how to create nested resources without a Stimulus integration, you can consult How To Create Nested Resources for a Ruby on Rails Application.

      For more information on how you would integrate React with a Rails application, see How To Set Up a Ruby on Rails Project with a React Frontend.



      Source link

      How to Add and Delete Users on Ubuntu 18.04


      Introduction

      Adding and removing users on a Linux system is one of the most important system administration tasks to familiarize yourself with. When you create a new system, you are often only given access to the root account by default.

      While running as the root user gives you complete control over a system and its users, it is also dangerous and can be destructive. For common system administration tasks, it is a better idea to add an unprivileged user and carry out those tasks without root privileges. You can also create additional unprivileged accounts for any other users you may have on your system. Each user on a system should have their own separate account.

      For tasks that require administrator privileges, there is a tool installed on Ubuntu systems called sudo. Briefly, sudo allows you to run a command as another user, including users with administrative privileges. In this guide we will cover how to create user accounts, assign sudo privileges, and delete users.

      Prerequisites

      To follow along with this guide, you will need:

      Adding a User

      If you are signed in as the root user, you can create a new user at any time by typing:

      If you are signed in as a non-root user who has been given sudo privileges, you can add a new user by typing:

      Either way, you will be asked a series of questions. The procedure will be:

      • Assign and confirm a password for the new user
      • Enter any additional information about the new user. This is entirely optional and can be skipped by hitting ENTER if you don’t wish to utilize these fields.
      • Finally, you’ll be asked to confirm that the information you provided was correct. Enter Y to continue.

      Your new user is now ready for use. You can now log in using the password that you entered.

      If you need your new user to have access to administrative functionality, continue on to the next section.

      Granting a User Sudo Privileges

      If your new user should have the ability to execute commands with root (administrative) privileges, you will need to give the new user access to sudo. Let’s examine two approaches to this problem: adding the user to a pre-defined sudo user group, and specifying privileges on a per-user basis in sudo’s configuration.

      Adding the New User to the Sudo Group

      By default, sudo on Ubuntu 18.04 systems is configured to extend full privileges to any user in the sudo group.

      You can see what groups your new user is in with the groups command:

      Output

      newuser : newuser

      By default, a new user is only in their own group which adduser creates along with the user profile. A user and its own group share the same name. In order to add the user to a new group, we can use the usermod command:

      The -aG option here tells usermod to add the user to the listed groups.

      Specifying Explicit User Privileges in /etc/sudoers

      As an alternative to putting your user in the sudo group, you can use the visudo command, which opens a configuration file called /etc/sudoers in the system’s default editor, and explicitly specify privileges on a per-user basis.

      Using visudo is the only recommended way to make changes to /etc/sudoers, because it locks the file against multiple simultaneous edits and performs a sanity check on its contents before overwriting the file. This helps to prevent a situation where you misconfigure sudo and are prevented from fixing the problem because you have lost sudo privileges.

      If you are currently signed in as root, type:

      If you are signed in as a non-root user with sudo privileges, type:

      Traditionally, visudo opened /etc/sudoers in the vi editor, which can be confusing for inexperienced users. By default on new Ubuntu installations, visudo will instead use nano, which provides a more convenient and accessible text editing experience. Use the arrow keys to move the cursor, and search for the line that looks like this:

      /etc/sudoers

      root    ALL=(ALL:ALL) ALL
      

      Below this line, add the following highlighted line. Be sure to change newuser to the name of the user profile that you would like to grant sudo privileges:

      /etc/sudoers

      root    ALL=(ALL:ALL) ALL
      newuser ALL=(ALL:ALL) ALL
      

      Add a new line like this for each user that should be given full sudo privileges. When you are finished, you can save and close the file by hitting CTRL+X, followed by Y, and then ENTER to confirm.

      Testing Your User’s Sudo Privileges

      Now, your new user is able to execute commands with administrative privileges.

      When signed in as the new user, you can execute commands as your regular user by typing commands as normal:

      You can execute the same command with administrative privileges by typing sudo ahead of the command:

      You will be prompted to enter the password of the regular user account you are signed in as.

      Deleting a User

      In the event that you no longer need a user, it is best to delete the old account.

      You can delete the user itself, without deleting any of their files, by typing the following command as root:

      If you are signed in as another non-root user with sudo privileges, you could instead type:

      If, instead, you want to delete the user’s home directory when the user is deleted, you can issue the following command as root:

      • deluser --remove-home newuser

      If you’re running this as a non-root user with sudo privileges, you would instead type:

      • sudo deluser --remove-home newuser

      If you had previously configured sudo privileges for the user you deleted, you may want to remove the relevant line again by typing:

      Or use this if you are a non-root user with sudo privileges:

      root    ALL=(ALL:ALL) ALL
      newuser ALL=(ALL:ALL) ALL   # DELETE THIS LINE
      

      This will prevent a new user created with the same name from being accidentally given sudo privileges.

      Conclusion

      You should now have a fairly good handle on how to add and remove users from your Ubuntu 18.04 system. Effective user management will allow you to separate users and give them only the access that they are required to do their job.

      For more information about how to configure sudo, check out our guide on how to edit the sudoers file here.



      Source link

      How to Add a Quick View Option to Your WooCommerce Products


      WooCommerce is an excellent tool for creating and managing an online store. However, there are some features it doesn’t include out of the box, such as a quick view option. Considering the importance of product displays for landing sales, this is a crucial missed opportunity.

      The good news is that, like many such issues in WordPress, you can solve this problem without too much trouble. Using the right plugin, you can easily add a quick view option to all of your products. This makes it easier for customers to examine and purchase items – and increase your conversion rate to earn more revenue!

      In this post, we’ll explain the many benefits of adding a quick view display option to each of your WooCommerce products. Then we’ll share two simple steps for doing so using WooCommerce Quick View Pro. Let’s get right to it!

      Your Store Deserves WooCommerce Hosting

      Sell anything, anywhere, anytime on the world’s biggest eCommerce platform.

      The Benefits of Including a Quick View Option for Your WooCommerce Products

      In online retail, a quick view display is a popup box that shows a product’s key details. This information might include one or several images, pricing, variations, and an Add to Cart button.

      An example of a WooCommerce quick view popup.

      Quick view displays are typically accessed by clicking a button or hovering over a product in a catalog or list view. For example, you might include quick views on your product category pages, so customers can see more information about individual products without having to navigate to their product pages.

      Quick view buttons on a product list page.

      This feature provides several benefits to your users. For instance, since they don’t have to navigate back and forth between your product lists and individual product pages, browsing becomes a lot easier. Guests can simply open the quick view display to see a product’s details, and then continue looking through the other items on the page.

      Additionally, quick view displays are an ideal place to include photo galleries, zoom options, and information about product variations and add-ons. With these additions, customers can get an up-close look at the items they’re interested in from multiple angles. They can also see each of the colors or other variations an item comes in.

      Finally, quick view displays with an Add to Cart button simplify the purchasing process. Customers can add multiple items to their carts without having to leave your category page or product list. This enables them to continue browsing without interruption, increasing the chances that they’ll buy more products.

      Ultimately, adding a quick view option is beneficial to both you and your potential customers. Enhancing your product displays with this handy feature is a simple way to make your e-commerce website easier and more enjoyable to use.

      How to Add a Quick View Option to Your WooCommerce Products (In 2 Steps)

      With the WooCommerce Quick View Pro plugin, adding quick view popup boxes for each of your WooCommerce products is fast and easy. Let’s look at how to configure and use this solution in just two steps.

      Step 1: Download, Install, and Activate WooCommerce Quick View Pro

      The first thing you’ll need to do is acquire WooCommerce Quick View Pro, and add it to your WooCommerce site. It’s important to note that you must already have WooCommerce installed and activated for this quick view plugin to work.

      To get started, head to the developer’s website and navigate to Plugins > WooCommerce Quick View Pro.

      Navigating to the plugins page on the Barn2 website.

      Here you’ll find information and pricing for the plugin; at this time, there isn’t a free version of this particular tool. Once you’ve purchased a license, you can download the WooCommerce Quick View Pro .zip file. You should also receive an email containing your license key. Make sure to take note of this, as you’ll need it to finish setting up the plugin.

      Next, make your way to your WordPress admin dashboard and navigate to Plugins > Add New. Click on Upload Plugin at the top of the screen.

      Uploading a new plugin.

      You can then select or drag-and-drop the .zip file containing the plugin, and hit Install Now. After the installation is complete, select the Activate button as well.

      Finally, with your license key in hand, access WooCommerce Quick View Pro’s settings by navigating to WooCommerce > Settings > Products > Quick View. The first field available should be the one for your license key.

      The License Key field.

      Add your license key here, then scroll down and click on the Save changes button. You’re now ready to start using the plugin.

      Step 2: Configure the Plugin’s Settings to Meet Your Needs

      Once you’ve installed and activated WooCommerce Quick View Pro, the plugin will automatically add a quick view display option to each of your products. However, you can also customize these displays to include the information you need by visiting the plugin’s settings.

      First, you’ll want to decide how your customers will open the quick view displays. You can use a button, enable the quick view to open when a customer clicks on the product image or name , or both.

      The quick view open settings.

      Leaving both of these options unchecked will disable the quick view displays entirely.

      The two fields below the Opening the Quick View check boxes will help you customize your Quick View button with your own text. You can also choose to add or remove the button icon. Next, you’ll need to decide what information you want to include in your popups. You have the option of an image, product details, or both.

      The quick view contents options.

      If you include images, you can choose to enable a gallery-style view and zoom functionality. Quick View Pro works great as a standalone WooCommerce gallery lightbox plugin. Both of these options are useful for customers who want to see variations on a product or get a closer look at fine details such as stitching.

      Using the zoom feature in a quick view display.

      If you choose to add product details to your quick view displays, you’ll also need to check the box for each item you wish to include. Your options are:

      • Reviews: These can provide social proof for your merchandise.
      • Price: An important detail for customers who are debating a purchase.
      • Short description: It helps to highlight features that could make an item more desirable.
      • Add to Cart button: A button makes purchasing fast and easy.
      • Meta information: This includes extra product information such as categories, tags, and SKU codes.

      Once you’ve selected all the information you wish to incorporate, your quick view displays will be ready to go. You can always come back here to change these settings, and your quick view displays will be updated automatically.

      A completed quick view display created with WooCommerce Quick View Pro.

      By default, the plugin adds your quick view displays to your category pages and other areas where customers may be browsing through several items. However, you can also incorporate them into product pages, too.

      Plus, all your quick view displays will be fully responsive for mobile shoppers. Quick view lightboxes like the ones you can create with WooCommerce Quick View Pro are especially helpful for giving customers a better look at your products on smaller screens.

      Design Your User Experience

      Detailed, easy-to-view product displays are essential to the success of your online store. With quick view displays, you can point out the best qualities of each of your products, simplify browsing, and speed up the purchase process. In some cases, this might even lead to an increase in sales.

      Are you ready to up the ante on your WooCommerce store? Consider DreamPress, our managed WordPress hosting solution. With automatic updates and strong security defenses, DreamPress takes server management off your hands so you can focus on what you do best: selling products. Learn more about plan options today.



      Source link