Can't reach vue controllers from clean Rails7 + importmaps - javascript

In the past got used to Stimulus with webpacker, and thought it would be similar. I have included VUE3 via importmaps and intent to link a basic controller for a 'Hello World'. What am I doing wrong? I am not proficient in the JS part.
config/importmap.rb
pin "application", preload: true
pin "#hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "#hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "#hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "vue", to: "https://ga.jspm.io/npm:vue#3.2.29/dist/vue.runtime.esm-bundler.js"
pin "#vue/reactivity", to: "https://ga.jspm.io/npm:#vue/reactivity#3.2.29/dist/reactivity.esm-bundler.js"
pin "#vue/runtime-core", to: "https://ga.jspm.io/npm:#vue/runtime-core#3.2.29/dist/runtime-core.esm-bundler.js"
pin "#vue/runtime-dom", to: "https://ga.jspm.io/npm:#vue/runtime-dom#3.2.29/dist/runtime-dom.esm-bundler.js"
pin "#vue/shared", to: "https://ga.jspm.io/npm:#vue/shared#3.2.29/dist/shared.esm-bundler.js"
javascript/application.js
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "#hotwired/turbo-rails"
import "controllers"
javascript/controllers/application.js
import { Application } from "#hotwired/stimulus"
const application = Application.start()
// Configure Stimulus development experience
application.debug = false
window.Stimulus = application
export { application }
javascript/controllers/hello_vue.js
import Vue from 'vue/dist/vue.esm'
document.addEventListener('DOMContentLoaded', () => {
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
})
views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Vuetube</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" %>
<%= javascript_include_tag "hello_vue" %> <!-- This one does not work and fails-->
<%= javascript_importmap_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
index.html.erb
<h1>Pages#index</h1>
<p>Find me in app/views/pages/index.html.erb</p>
<h1>Pages#welcome</h1>
<div id="app">
{{ message }}
</div>

Related

errorMessage is not defined

How do I use errorMessage object from routes in a partial.I tried this
Route:-
const express = require("express");
const router = express.Router();
const Character = require("../models/character");
// All Character
router.get("/", (req, res) => {
res.render("characters/index");
});
// New Character
router.get("/new", (req, res) => {
res.render("characters/new", { character: new Character() });
});
// Creat
router.post("/", (req, res) => {
const character = new Character({
name: req.body.name,
});
character.save((err, newCharacter) => {
if (err) {
res.render("characters/new", {
character: character,
errorMessage: "Error Creating",
});
} else {
// res.redirect(`characters/${newCharacter.id}`)
res.redirect("characters");
}
});
});
module.exports = router;
layout:-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Test</title>
</head>
<body>
<%- include("../views/partials/header.ejs") %>
<%- include("../views/partials/errorMessage.ejs") %>
<br />
<%- body %>
<br />
</body>
</html>
partial :-
<%= errorMessage %>
it gives me this error:-
ReferenceError: D:\Web_Development\LGS\layouts\layout.ejs:10
8|
9| <body>
>> 10| <%- include("../views/partials/header.ejs") %> <%-
11| include("../views/partials/errorMessage.ejs") %>
12| <br />
13| <%- body %>
D:\Web_Development\LGS\views\partials\errorMessage.ejs:1
>> 1| <%= errorMessage %>
2|
errorMessage is not defined
Maybe should you try to include specifix variables :
<%- include("../views/partials/errorMessage.ejs", {errorMessage}) %>
Or just check the avaibality of your variable as it's in the main layout...
<% if (errorMessage !== undefined) { %>
<%= errorMessage %>
<% } %>
You are passing data from sever to "characters/new.ejs" this file
and now in new.ejs file you have used layouts such as header and errorMessage by using <%- include() %> statement
and to pass data from new.ejs file to this layouts you need to provide second argument to <%- include() %> statement and object of data that you want to pass
so in your example to pass errorMessage to
"../views/partials/errorMessage.ejs" you need to provide
<%- include("../views/partials/errorMessage.ejs", {errorMessage}) %>
then you can use this passed data to your layout like <%= errorMessage %>
if you want to pass more then one data you can do this
<%- include("../views/partials/errorMessage.ejs", {data1, data2, ...}) %>
try this it might be helpful it worked for me !!!!!
<% if(locals.errorMessage != null) {%>
<%= errorMessage %>
<%}%>
I had the same problem... and I solved it by using this:
<%= locals.errorMessage %>

Ruby on rails not loading Javascript

I had this working, but must have made a minor change somewhere that's stopped it working.
I have a call to Javascript when clicking on a button, but when I click the button nothing loads. I've gone over all parts of the code multiple times but cannot see where the failure is happening and there's no feedback in the logs so I have nothing to go on.
As I said it was working previously, but then stopped.
This is my button:
<%= button_tag t(".change"), class: "btn let-admin-change",
data: { title: t(".confirm_switch"),
cancel: t(".cancel"),
cta: t(".confirm_cta"),
plot: plot.id } %>
And my javascript file:
(function (document, $) {
'use strict'
$(document).on('click', '.let-admin-change', function (event) {
var dataIn = $(this).data()
var $changeContainer = $('.change-admin-letting-form')
$('body').append($changeContainer)
var $form = $('.edit_plot')
$changeContainer.dialog({
show: 'show',
modal: true,
width: 700,
title: dataIn.title,
buttons: [
{
text: dataIn.cancel,
class: 'btn',
click: function () {
$(this).dialog('destroy')
}
},
{
text: dataIn.cta,
class: 'btn-send btn',
id: 'btn_submit',
click: function () {
$.ajax({
url: '/plots/' + dataIn.plot,
data: $form.serialize(),
type: 'PUT'
})
$(this).dialog('destroy')
$changeContainer.hide()
}
}]
}).prev().find('.ui-dialog-titlebar-close').hide() // Hide the standard close button
})
})(document, window.jQuery)
And the form it's calling:
<div class="change-admin-letting-form">
<%= simple_form_for plot, url: phase_lettings_path(#phase), remote: true do |f| %>
<div>
<%= f.hidden_field :letter_type, value: :homeowner %>
<%= f.hidden_field :letable_type, value: :planet_rent %>
</div>
<div>
<h3><%= t(".are_you_sure", plot: plot) %></h3>
<p><%= t(".confirm_admin_swap").html_safe %></p>
</div>
<% end %>
</div>
Can anyone see where this is going wrong to stop the javascript from loading?
EDIT
If I add
<%= render "change_admin_letting_form", plot: plot %>
underneath the button then the form loads (but it loads multiple forms - one form for each instance of the button / each plot on the page), so I assume the issue is that the plot information is not being passed to the form? I'm not sure how to pass this information across.
I've managed to get this working by doing the following to get the correct information to the form:
I've changed the class of the form to take a variable:
<div class="hidden <%= plot_id %>">
And send the variable to the form by rendering the form from inside the block that loads each plot:
<% plots.each do |plot| %>
// some omitted code
<%= button_tag t(".change"), class: "btn let-admin-change",
data: { title: t(".confirm_switch"),
cancel: t(".cancel"),
cta: t(".confirm_cta"),
plot: plot.id } %>
<%= render "change_letter_type_form", plot_id: "change_letting_plot_#{plot.id}", plot: plot %>
<% end %>
And changed the part of the javascript that loads the form to search for the new plot-based class:
var $changeContainer = $('.change_letting_plot_' + dataIn.plot)
So now only the one form relevant to the selected plot loads, and the form works as required.
I have next to zero experience with javascript, so this probably isn't the 'proper' way to get this to work, but it works so I'll take it as it is.

Rails how to render two different modals for destroy action

I want to add confirmation modal when bank manager has to delete bank_employee without clients bank_employee.users = nil. If bank_employee has clients I want to render different modal - destroy_confirmation_modal. How to do it in a proper way? Where should I put if condition?
code snipped of
edit.html.erb
<div class="row">
<div class="col-sm-12 col-md-12 col-xs-12 text-center bank-employee__button-wrapper bank-employees-users-registration__registrations-submit--wrapper">
<%= t('.delete') %>
<%= f.submit t('.submit'), id: "formSubmit", class: "bank-employee__button bank-employee__button-submit"%>
</div>
</div>
<% end %> // this `end` comes from `form_for`
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<%= link_to bank_employees_path do %>
<span class="bank-employee__back-button">
<%= image_tag "icon_back.svg", alt: "Back icon", class: ""%>
<%= t('.back') %>
</span>
<% end %>
</div>
</div>
</div>
<%= render "destroy_confirmation_modal" %>
I don't think I should update my controller method but maybe I'm wrong?
controller.rb
def destroy
authorize current_bank_employee
#bank_employee = find_bank_employee(params[:id])
if #bank_employee.users.any? && associated_bank_employees.present?
reassign_users_and_notify_bank_employee
elsif #bank_employee.users.any?
render :edit
else
#bank_employee.destroy
render :destroy_notice, locals: { old_bank_employee: #bank_employee, assigned: false }
end
end
EDIT
my routes.rb
resources :bank_employees, except: [:show], concerns: [:with_datatable] do
member do
get :confirm
end
end
rails routes showed me this path as confirm_bank_employee so I've changed if condition as follow
<% if #bank_employee.users.empty? %>
<%= button_to "", confirm_bank_employee_path(#bank_employee), form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy", remote: true %>
<% else %>
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<% end %>
You need a different approach.
<% if #bank_employee.clients.empty? %>
<%= button_to "", bank_employee_confirm_path(#bank_employee), form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy", remote: true %>
<% else %>
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<% end %>
now you need two things:
inside routes.rb create a collection for your blank_employee_confirm_path:
whatever setup you have, i assume you have some kind of blank_employees resources path:
resources :blank_employees do
member do
get :confirm
end
end
now, inside your controller you need to add the method confirm:
def confirm
## do whatever you want do to here
render :js
end
this will then head over to confirm.js
create a confirm.js.erb file inside your blank_employees view folder. To confirm this is working, you can add a console.log('it works') in it.
Once you have confirmed that it is working you can add the javascript code to the confirm.js.erb file:
$('#modal-body').html('<%= escape_javascript(render :partial => 'shared/your_confirmation_modal'%>');
with this setup, you need in your edit.html.erb file a <div id="modal-body"></div> that will take the modal. Also notice that in my example the confirmation modal is stored in "views/shared/_your_confirmation_modal.html". Change the path to your setup or create the exact path in order to make this work!
Notice that the "confirm" path is for the blank_employees that have no clients. The button will be only rendered when there is no client. All the other logic you had before for the blank_employees with clients stay the same. You don't need to change there anything. If you had any logic inside there for blank_employees without any clients, move the code to the confirm method.
One more thing: Make sure to add to your destroy method a render :js as well, and inside destroy.js.erb add the same kind of logic like inside confirm.js.erb, beside that you want to render the modal for blank_employees with clients. Your destroy.js.erb file should look something like this:
$('#modal-destroy').html('<%= escape_javascript(render :partial => 'shared/your_destroy_modal'%>');
Very important: Just like with the first modal, add a <div id="modal-destroy"></div> to your edit.html.erb file, otherwise it wont render the modal.
If something is unclear, let me know!
Greetings!

Cannot render a modal when button is clicked in Rails 4

I'm a beginner to Rails and I'm following a tutorial to build a Todo App.
When I try to click the button in the browser (in the application.html.haml code below) nothing happens. It is as if it is unresponsive. I see when I hover over the button it is supposed to direct me to localhost:3000/tasks/new.
Here's a snapshot of how the page looks as of now:
In the tutorial, there is a modal that is used to render the new todo task. The code for the modal is in new.js.erb in view > tasks:
m = $('#modal');
m.html('<%= j(render partial: 'task_form', locals: {task: #task}) %>');
m.modal('show');
The partial task_form.html.haml code is:
.modal-header
%h1 New Task
= simple_form_for task, class: 'clearfix' do |f|
.modal-body
= f.input :title
= f.input :note
= f.input :completed
.modal-footer
= f.submit 'Save', class: 'btn btn-primary'
The code for home.html.haml is:
.container
- if #tasks.empty?
%span.text-warning There are no tasks!
- else
%table.table.table-hover.table-bordered
%thead
%tr
%th Title
%th Created at
%th Completed
%tbody
- #tasks.each do |task|
%tr
%td
%strong= task.title
%td.text-info= task.created_at
%td.text-success= task.completed
#modal.modal.fade
And for the application.html.haml is:
!!!
%html
%head
%title Todo
= csrf_meta_tags
= csp_meta_tag
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
= javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
%body
.container
.jumbotron.text-center
%h1
ToDo
%p
Welcome to the tutorial's ToDo application
= link_to 'New task', new_task_path, class: 'btn btn-primary', remote: true
= yield
I tried looking for a solution everywhere but I couldn't find it. I thought I'd post it here for help from any good samaritans..Could you let me know why I cannot see the modal?
Thanks a lot for any help.
I finally got it. I forgot to install the gem 'jquery-rails' and make the necessary additions to the application.js and application.scss. Restarting the server after that resulted in a modal. Thanks!

Forms with Select2 are duplicated when clicking back button on browser in Rails 5

_header.html.erb (for forms part)
<%= form_for home_path, class: 'home', role: 'search', method: :get do |f| %>
<div class="form-group" style="display:inline;">
<div class="input-group input-group-md">
<%= text_field_tag :q, params[:q], placeholder: ... ,class: 'form-control hideOverflow', type: "search" %>
<%= select_tag "category", options_from_collection_for_select(...),include_blank: true, class: 'form-control hideOverflow', type: "search" %>
<%if logged_in? %>
<%= select_tag "location", options_for_select([...], ...),class: 'form-control hideOverflow', type: "search" %>
<% else %>
<%= select_tag "location", options_for_select([...], ...),class: 'form-control hideOverflow', include_blank: true, type: "search" %>
<% end %>
<span class="input-group-addon"><%= submit_tag "Search", class: "btn-transparent"%></span>
</div>
</div>
<% end %>
JS codes
<script>
$( document ).on('turbolinks:load', function() {
$('select#category').select2({
width: '60%',
dropdownAutoWidth : true,
placeholder: "Choose a category",
maximumSelectionLength: 3
});
$('select#location').select2({
width: '40%',
dropdownAutoWidth : true,
minimumResultsForSearch: Infinity
});
});
</script>
Glitch or rendering issues (click links to view the images)
normal
after I click back button from the browser
Can someone help me out why? Plus, my search forms are in the navigation bar in header partial file.
If I get rid of $(...).select in the script, everything works fine... I think there is a problem with select.js
Answered here:
https://stackoverflow.com/a/41915129/5758027
I have use this solution in my own code:
$(document).on('turbolinks:before-cache', function() { // this approach corrects the select 2 to be duplicated when clicking the back button.
$('.select-select2').select2('destroy');
$('.select-search-select2').select2('destroy');
} );
and an observer:
$(document).ready( ready ); //... once document ready
$(document).ajaxComplete( ready ); //... once ajax is complete
$(document).on('turbolinks:load', ready ); //... once a link is clicked
function ready() {
$(".select-search-select2").select2({
theme: "bootstrap",
language: 'es',
allowClear: true
});
$(".select-select2").select2({
theme: "bootstrap",
language: 'es',
minimumResultsForSearch: Infinity,
allowClear: true
});
};
isn't clearing the cache all the time makes using turbolinks practically pointless?
how about something like this instead?
$(document).on('turbolinks:before-cache', function(e) {
return $('.form-control.select2').each(function() {
return $(this).select2('destroy');
});
});
I wasn't able to solve this rendering issue (still waiting for the correct answer!) but if anyone has a similar problem like me, try thinking outside of box. Here is my hack: I added a back button in my applicaiton.
Get the full url path
# get the previous url
def save_previous_page
session[:return_to] = request.fullpath
end
Show the back button only if the page is NOT home page or search page
<% if session[:return_to] != request.fullpath%>
<%= link_to session.delete(:return_to) || request.fullpath, class: 'back-button' do%>
<i class="fa fa-arrow-circle-left" aria-hidden="true"></i>
<%end%>
<% end %>
Meanwhile, I am still waiting and trying to fix rendering issue...
FIXED THE ISSUE
Simply add this code in your .js file
Turbolinks.clearCache();
This is most likely to be some asset inconsistency, you should check your app\views\layouts folder for files with duplicated declarations of wither jQuery, jQuery UJS or Turbolinks. Check all <script> tags of your pages, and if you are declaring both on layout folder and in internal views the same scripts. If that is not the case, check for render, yield or build calls
Simple solution, don't run the select2 builder on stuff you would rather it not run on.
$("select#category:not(.select2-container):not(.select2-hidden-accessible)").select2();

Categories

Resources