In my development environment, everything is okay.
editor work so great.
but when app run on heroku, the WYSIWYG editor Redactor display twice.
one default style Redactor editor, and one custom style Redactor editor.
I try to delete custom setup,
not working,
it display two, two default style editor,
ban turbolinks or modify config, it's still not working.
wherever I do, I got two editor on the page where use Redactor.
on Heroku the page look like:
<div class="redactor-box">
<ul class="redactor-toolbar" id="redactor-toolbar-2">
</ul>
<div class="redactor-editor">
</div>
<div class="redactor-box">
<ul class="redactor-toolbar" id="redactor-toolbar-3">
</ul>
<div class="redactor-editor">
</div>
</div>
</div>
In my development env, it look like:
<div class="redactor-box">
<ul class="redactor-toolbar" id="redactor-toolbar-2">
</ul>
<div class="redactor-editor">
</div>
</div>
this is my code.
#app/views/articles/new.html.erb
<div class="col-md-8 col-xs-8 col-xs-offset-2 col-md-offset-2">
<%= form_for([current_user, #article]) do |f| %>
<%= render 'shared/error_messages', var: #article %>
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control', id: "article-title" %></br>
<%= f.label :content %>
<%= f.text_area :content, class: :redactor %></br>
<%= f.submit "Save", class: "btn btn-default btn-custom" %>
<% end %>
</div>
and this is my Redactor config.
#app/assets/javascripts/redactor-rails/config.js
window.init_redactor = function(){
var csrf_token = $('meta[name=csrf-token]').attr('content');
var csrf_param = $('meta[name=csrf-param]').attr('content');
var params;
if (csrf_param !== undefined && csrf_token !== undefined) {
params = csrf_param + "=" + encodeURIComponent(csrf_token);
}
$('.redactor').redactor({
"imageUpload":"/redactor_rails/pictures?" + params,
"imageGetJson":"/redactor_rails/pictures",
"fileUpload":"/redactor_rails/documents?" + params,
"fileGetJson":"/redactor_rails/documents",
"path":"/assets/redactor-rails",
"css":"style.css",
"minHeight" : 300
});
}
$(document).on( 'ready page:load', window.init_redactor );
rails '4.2.3'
redactor-rails '0.5.0'
Thanks for read.
update
when I delete all config file, not just change some setup,
I got right display.
but if I do that, I can't custom my editor.
finally, I use CSS solve the problem.
it's a stupid way,
The extra editor just be hidden, not disappear.
.fix-box {
margin-top: 110px;
.redactor-box {
#redactor-toolbar-2 {
display: none;
}
.redactor-box {
margin-top: -110px;
}
}
}
You should change
$(document).on( 'ready page:load', window.init_redactor );
to
$(document).ready(function(){window.init_redactor});
pageLoad may be called more than once in some circumstances, causing multiple Redactor instances in your case
Related
I am going through a software engineering book called Modern Front-End Development for Rails, Second Edition by Noel Rappin. In one section of the book, I am trying to create a search form with turbo and stimulus so that when I type in input, it will automatically submit through javascript and the page that I am on will show the desired search result that I want. I've followed the book and double checked my code but it seems that the search bar is not working for one reason or another.
What makes this feature difficult to troubleshoot is that I do not see any errors in the rails terminal, no output in the browser console tab, no error logging, etc. It simply does not do a thing when I type. Below is the following pieces of code that is supposed to make the search work.
app/views/schedules/_search_form.html.erb
<%= turbo_frame_tag("search-form") do%>
<div data-controller="search">
<%= form_with(
url: concerts_url,
method: "get",
data: {
"turbo-frame": "search-results",
"search-target": "form",
action: "input->search#submit"
}
) do%>
<div class="flex justify-center">
<div class="w-4/5">
<%= text_field_tag(
"query", "",
placeholder: "Search concerts",
type: "search",
id: "search_query",
"data-search-target": "input",
class: "w-full px-3 py-2
border border-gray-400 rounded-lg"
) %>
</div>
</div>
<% end %>
<%= turbo_frame_tag("search-results") %>
</div>
<%end%>
app/javascript/controllers/search_controller.ts
import { Controller } from "#hotwired/stimulus"
import "form-request-submit-polyfill"
export default class SearchController extends Controller {
static targets = ["form", "input"]
formTarget: HTMLFormElement
submit(): void {
this.formTarget.requestSubmit()
}
}
app/javascript/controllers/index.js
import { application } from "./application"
import SearchController from "./search_controller.ts"
application.register("search", SearchController)
app/controllers/concerts_controller.rb
def index
#query = params[:query]
#concerts = Concert.all
end
app/views/concerts/_search_result.html.erb
<article class="my-6 max-w-screen-lg">
<div>
<%= concert.start_time.by_example("Jan 2 #3:04PM") %>
<div class="font-bold text-xl">
<%= link_to(concert, data: {"turbo-frame": "_top"}) do %>
<%= highlight(concert.name, /#{query}/i) %>
<%end %>
<div class="float-right">
<% if concert.sold_out? %>
Sold out
<% else %>
<%= pluralize(concert.unsold_ticket_count, "Tickets") %>
Remaining
<%end%>
</div>
</div>
</div>
<div>
<%= highlight(concert.bands.map(&:name).join(", "), /#{query}/i) %>
</div>
<div><%= concert.genre_tags.split(",").to_sentence %></div>
<div><%= concert.venue.name %></div>
</article>
These 5 pieces should work together and should highlight on my page the search term that I want. But like I mentioned before, nothing is being submitted, going to the controller, or updating. It simply doesn't do anything with no errors reported. How can I troubleshoot this feature effectively? If there is something glaring that I am not seeing I would appreciate the help.
I've been testing Hotwire, using the demo that was put up by DHH. I have a default rails 6 setup & I know that it re-creates the javascript folder structure from the rails 5 < asset pipeline. The issue I am having is the form will not reset the text field after submission - despite setting up the stimulus controller for using this particular action. How can I reset the form for hotwire after the form is submitted by the user? My code is below
new.html.erb
<%= turbo_frame_tag 'new_conversation_comment', target: '_top' do %>
<%= form_with(model: [#conversation, #conversation_comment],
data: { controller: "reset_form", action: 'turbo:submit-end->reset_form#reset' }, html: {class: 'form-inline'}) do |form| %>
<%= form.submit 'Post', class: 'btn btn-primary mb-2', style: 'float: right;', 'data-reset_form-target': 'button' %>
<div style="overflow: hidden; padding-right: .5em;">
<%= form.text_field :content, class: 'form-control' %>
</div>
<% end %>
_conversation_comment.html.erb
<div class="p-1">
<%= conversation_comment.content %>
</div>
show.html.erb
<div class="p-2">
<%= turbo_stream_from #conversation %>
<%= turbo_frame_tag 'conversation' do %>
....
<% end %>
<div class="conversation-comments-container" id="conversation_comments">
<%= render #conversation.conversation_comments %>
</div>
<hr>
<%= turbo_frame_tag 'new_conversation_comment', src: new_conversation_conversation_comment_path(#conversation), target: :_top %>
</div>
assets/javascripts/controllers/reset_form_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
reset() {
this.element.reset()
}
}
Disclaimer: I'm using Webpack 4.0
Depending on setup underscore between controller name can be an issue. Replacing it with dash worked in my case.
turbo:submit-end->reset-form#reset
Disclaimer: I'm using Webpack 4.0
I ran into the same thing, also using Webpack.
The solution for me was:
Change the underscores to dashes, like Canbey said.
Move the stimulus controller from app/assets/javascripts/controllers/ to app/javascripts/controllers/ (somewhat per the stimulus docs so that webpack imported it properly — my application.js file was importing controllers from that directory rather than app/assets/javascripts.
A half SJR, half Hotwire approach would be this:
Working example here: https://rails-react-bootstrap.herokuapp.com/rooms
new.html.erb
<%= turbo_frame_tag 'new_note' do %>
<%= form_with model: [#room, #note] do |form| %>
<!-- Allow body validation error without message -->
<div class="form-group">
<%= form.rich_text_area :body, class: 'form-control comments', id: 'bodyText' %>
</div><!-- .form-group -->
<div class="form-group d-flex justify-content-center">
<%= form.submit 'Add Note', class: 'btn btn-lg btn-tayberry btn-block' %>
</div><!-- .form-group -->
<% end %>
<% end %>
create_js.erb
// clear note textarea after submit
var input = document.getElementById("bodyText");
input.value = '';
notes_controller
def create
#note = #room.notes.create!(note_params)
# #note.user = current_user
respond_to do |format|
if #note.save
format.turbo_stream
format.html { redirect_to #room }
format.js
else
format.html { render action: 'new' }
end
end
end
If you still need an answer, here is how you can achieve what you need:
In your controller respond to requests like that:
format.html { redirect_to conversations_url }
Note: redirect to the page where new_conversation_comment frame is defined (i.e same page from where you are submitting form). Redirect will happen, but Hotwire will substitute form for you with a new one and append comment where you specified.
It is a bit magic.
I suggest you to check this video:
https://gorails.com/episodes/hotwire-rails?autoplay=1
I have a view with bootstrap-tabs. The tabs are generated dynamically.
<%= form_with(:id => 'my-form', model: [:admin, #island], local: true) do |form| %>
<div class="tab-content bg-light" id="tabs-from-locales-content">
<%= available_locales.each_with_index do |locale, i| %>
<div
class="tab-pane fade <%= 'show active' if i == 0 %>"
id="<%= locale.downcase %>"
role="tabpanel"
aria-labelledby="<%= locale.downcase %>-tab">
<%= render partial: 'island_form', locals: {counter: i, locale: locale, f: form} %>
</div>
<% end %>
</div>
...
...
A tab represents each available localization of the app.
The model of the form contains two nested attributes. These attributes have 1 to many relationship with the model. So the user can add multiple of these from the form. Their fields can be generated dynamically:
(For simplicity I include in the question only one. This is a part of _island_form.html.erb partial.)
<div class="form-group ports-div">
<%= f.label :port %> </br>
<%= f.fields_for :ports do |builder| %>
<%= render 'port_fields', f: builder %>
<% end %>
<%= link_to_add_fields t('form.add_port'), f, :ports %>
</div>
<div class="form-group">
<%= f.label :airport %> </br>
<%= f.fields_for :airports do |builder| %>
<%= render 'airport_fields', f: builder %>
<% end %>
<%= link_to_add_fields t('form.add_airport'), f, :airports %>
</div>
And the port_fields partial:
<fieldset>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.hidden_field :_destroy %>
<%= link_to t('form.remove'), '#', class: 'remove_fields' %>
</fieldset>
The link_to_add_fields helper method:
def link_to_add_fields(name, f, association)
# Builds an instance of the association record.
new_object = f.object.send(association).klass.new
# Grabbing ruby's object id.
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + '_fields', f: builder)
end
link_to(name, '#', class: 'add_fields', data: { id: id, fields: fields.gsub('\n', '') })
end
What I want to achieve is to synchronize the addition and removal of these fields among the available tabs. So when the user clicks Add field on the first tab all the other tabs will follow this action. Same for field removal.
My relevant js file for the Add button looks like this. I have tried many combinations between the trigger, triggerHandler and stopPropagation, although most of the times I am getting StackOverflow exception and the fields are added only to the tab that I clicked the add button.
(Since I pass a class (.add_fields) in the selector isn't that supposed to be attached to all the elements with class .add_fields?)
form.on('click', '.add_fields', function (event, param) {
event.stopPropagation();
event.preventDefault();
var time = new Date().getTime();
var regexp = new RegExp($(this).data('id'), 'g');
$(this).before($(this).data('fields').replace(regexp, time));
$('.tab-pane').each(function (index) {
$('.add_fields').trigger("click", ["custom_click"]);
console.log('Tab - ' + index);
})
});
EDIT
I am getting somewhere with that code:
$('.ports-div').each(function (index) {
$(this).find('.add_fields').each(function () {
this.click();
})
});
Although, at all the tabs, instead of 1 field 6 fields are added. I added the ports-div to the div that wraps all the related elements.
Complete rewrite...
The problem as you have seen is to be that your click event handler is triggering a click on all tab-panes, including the one you are handling currently. You are also not just clicking the a single item in the loop, but all that match '.add_fields'. This then leads to the click being handled again, recursively.
To prevent this, I suggest calling a function to add your fields directly, rather than triggering a click. If triggering a click is just easiest in your situation, consider the following example that does roughly what you want without the recursive error.
https://jsfiddle.net/3ftf0j8e/1/
Dummy HTML
<a class="add_fields" id='1'>link 1</a>
<a class="add_fields" id='2'>link 2</a>
<a class="add_fields" id='3'>link 3</a>
<a class="add_fields" id='4'>link 4</a>
Sample Javascript
$(document).on('click', '.add_fields', function (event, param) {
event.preventDefault();
var clicked_id = $(this).attr('id');
console.log('clicked id:' + clicked_id);
$(this).addClass('done');
$(this).addClass('clicked');
// Just click items that have not been clicked
var els = $('.add_fields').not('.clicked');
console.log(els);
els.trigger("click");
setTimeout(function(){
if($('.add_fields').length == $('.add_fields.done').length)
$('.add_fields').removeClass('clicked');
})
});
CSS
a.clicked {
background-color: yellow;
}
a.done {
color: red;
}
As you can see now, each fires just once. The setTimeout at the end allows the DOM to update before clearing the clicked classes.
Running into a strange issue. I using jQuery's .append() to place a <div> containing a Rails form next to the paragraph tag a user selects in my Rails app.
First off, before adding the jQuery effect, I tested this by placing a the form on the page. It loaded, worked, and posted the user's text to my database correctly.
Second, I added this effect, and the <div> with the form does load, but weirdly, you cannot click into it. The text cursor will appear for a moment, disappear, and the user can not actually enter anything in the text_field.
Here's what I am running:
view.html.erb
<% #posts.each do |post| %>
<h2 id="title"><%= post.title %></h2>
<div id="paragraph"><%= markdown(post.content) %></div>
<% end %>
<div class="exampleToggle"> <!-- div that should append -->
<%= form_for :comments do |f| %>
<div class="form-group">
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="field">
<%= f.label :post %><br>
<%= f.text_area :post, class: "form-control" %>
</div>
</div>
<div class="actions">
<%= f.submit "Save", class: "btn btn-success-outline" %>
</div>
<% end %>
</div>
<script>
$(document).ready(function(){
var whatever = document.getElementsByClassName("exampleToggle");
$("p").click(function(){
$(this).append(whatever);
});
});
</script>
As I mentioned, the form does load and looks how it should after clicking on a paragraph element; however, the form will not accept text input.
Also, for clarity's sake, here is the CSS involved with the placing the <div> to the right of the clicked paragraph:
{
position: relative;
}
.exampleToggle {
position: absolute;
top: 0;
margin-left: 8%;
left: 100%;
width: 35%;
height: 100px;
}
Any idea why this is breaking the form/form helper?
EDIT 1: For clarity, the central issue here seems to be the Focus effect. To try and fix this, I updated the Javascript to allow for value entry when the .field class is activated, but it did not fix it. Here's what I tried.
<script>
$(document).ready(function(){
var whatever = document.getElementsByClassName("exampleToggle");
$("p").click(function(){
$(this).append(whatever);
});
$('.field').click(function () {
var val = $(this).val();
if (val == "") {
this.select();
}
});
});
</script>
You have given $("p"), but there is no p tag in your question.
Also you are looping the same id to different fields which is not correct, ID should be unique. Instead take the class instead of id.
You should take a class to the paragraphed_id field and should write the jquery functions based on the required classes.
Try this,
<% #posts.each do |post| %>
<h2 id="title"><%= post.title %></h2>
<div class="paragraph"><%= markdown(post.content) %></div>
<% end %>
<div class="exampleToggle"> <!-- div that should append -->
<%= form_for :comments do |f| %>
<div class="form-group">
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="field">
<%= f.label :post %><br>
<%= f.text_area :post, class: "form-control" %>
</div>
</div>
<div class="actions">
<%= f.submit "Save", class: "btn btn-success-outline" %>
</div>
<% end %>
</div>
<script>
$(document).ready(function(){
var whatever = document.getElementsByClassName("exampleToggle");
$(".paragraph").click(function(){
$(this).append(whatever);
});
});
</script>
I have following form
<div id="post-close-updates-form">
<%= form_for [#investment,#post_close_update], remote: true do |f| %>
<div class="form-group">
<%= f.label :content %>
<%= f.cktext_area :content %>
</div>
<%= f.submit "Update", class: "btn btn-primary" %>
<% end %>
</div>
and my jquery code is
$("#post-close-updates-form form")[0].reset();
but it is not clearing cktext_area content...while if i put normal html textarea then it works fine.
so how do i clear ckeditor cktext_area via js/jquery
ok here is the answer given by developer of ckeditor #galetahub
$("#post-close-updates-form form")[0].reset();
for (instance in CKEDITOR.instances){
CKEDITOR.instances[instance].updateElement();
}
but above one not worked for me so made some chages that works
for (instance in CKEDITOR.instances){
CKEDITOR.instances[instance].setData(" ");
}
setData() is to set the data is cktext_area
now if u want to get the data from cktext_area in js then use this
for (instance in CKEDITOR.instances){
CKEDITOR.instances[instance].getData();
}