There is a pretty bootstrap editor - Bootstrap WYSIWYG, which i want to use in my blog based on RoR 4.0. The problem is that Bootstrap WYSIWYG does not work with anything except DIV tag (as far as i know from a bit searching).
This code works fine:
<div id="editor" contenteditable="true">some editable content</div>
And this one doesnt:
<%= f.text_area :content, id: "editor", contenteditable: "true" %>
<textarea id="editor" contenteditable="true">
So, the question is - how to connect this two things together?
have you tried putting a hidden field, working with the div and when the editor have changes update the hidden field value? Hope this helps
In order to integrate Bootstrap-WYSIWYG in a Ruby on Rails app you should do the following:
(most of the times - you have more than one editor in a rails form, in this example I will show how to do it without errors)
I will use the editor in the admin namespace, therefore I have created a folder editor inside my views: "admin/shared/editor" to keep everything tide and content oriented.
Firstly, for every attribute of a model I would like to use the Bootstrap WYSIWYG editor I will render a partial with the field that has integrated the editor, so you might have smth like this:
/admin/posts/_form.html.haml:
= form_for #post do |f|
= render partial: 'admin/shared/editor/field', locals: { f: f, content: "summary" }
= f.button class: "form_with_editor"
were you pass as a local parameter the form and the attribute of the model you would like to apply the editor (in this case => summary). Note also that you should add a class to the submit button of the form: .form_with_editor, that will be used later for the button click listener.
Now inside admin/shared/editor/_field.html.haml:
.btn-toolbar.btn-editor{"data-role" => "editor-toolbar", "data-target" => "#editor_area_for_#{content}"}
= render partial: 'admin/shared/editor/toolbar'
.wysiwyg{id: "editor_area_for_#{content}", style: "overflow:auto; height:444px;max-height:444px", data: {"for-content" => "hidden_#{content}"}}
= f.hidden_field content.to_sym, id: "hidden_#{content}"
This editor works with a div and not with a textarea, therefore, we will be using a div with class .wysiwyg and a dynamic id which in this case evaluates to: #editor_area_for_summary
The class: .wysiwyg is used in order to select all the divs with this class when we initialize the editor in our doc ready function.
The toolbar partial contains all the markup for the custom toolbar of the editor, you can customize it as you wish and use it in all your fields.
In order to copy the contents of the div to the form input and post them to the server, you have to use a hidden input field:
= f.hidden_field content.to_sym, id: "hidden_#{content}"
Note: it also gets a dynamic id(which evaluates to: "hidden_summary") and a name -> :summary
Now in order to make all these to work together we need some javascript on your doc ready function:
/assets/javascripts/doc_ready.js:
$( document ).ready(function() {
// EDITOR stuff
$('.wysiwyg').wysiwyg();
// Handling the form submission
$('.form_with_editor').click(function(){
$('.wysiwyg').each(function() {
var editor_div_content = $(this).html();
var hidden_input = $(this).attr("data-for-content");
$("#" + hidden_input).val(editor_div_content);
});
});
// Fill the editor divs with content
$('.wysiwyg').each(function() {
var hidden_input = $(this).attr("data-for-content");
var editor_div_content = $("#" + hidden_input).val();
$(this).html(editor_div_content);
});
});
On the first part we apply the function wysiwyg() of the editor in every div that has this class.
On the second part we have an on click handler of the form button we get the html content of the div and we set it as the value of our hidden input field.
And on the last part we do the opposite, we get the value of the hidden field and place it on the editors div when the document is ready, with this one we display content on the editor that is already stored in the database.
I hope this one helps :)
Related
I have a home page with a list of software in a table.
With this link I open my softwares pages in modals:
<% = link_to software_path (software), remote: true do %> ... <% end%>
I created a script allowing me to open my modals when I click on td with class .clickable and not open modals when I click on a td with class .no-click.
$(".clickable").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
$('#software-modal').modal('show');
}
});
structure :
_software.html.erb open _software_modal.html.erb
Problem:
When I try to open my modals via the script, it's always the same modal that opens. That is, the modal of software 1 opens when I click on software 2, software 3, ... but no problem when I just use the link.
So I have a problem with id or something like that in my script ...
Can you help me ?
EDIT 1: With the response of VAD I tried to search the id with data-id.
_software.html.erb
<tr class="clickable" data-id="<%= software.id %>">
...
</tr>
<div id='software-content'></div>
_software_modal.html.erb
<div id="software-modal" class="modal fade" role="dialog">
...
</div>
show.js.erb
$('#software-content').html("<%= j render 'software_modal', software: #software %>");
$('#software-modal').modal('show');
this show.js.erb allows me to open my modals via example links:
<%= link_to software.name, software_path(software), remote: true,class:"no-click" %>
but not via a clickable table.
So I added a script in _software.html.erb that makes my table clickable:
$(".clickable[data-id]").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
window.location = '/softwares/' + $(this).attr('data-id');
}
});
I also tried with data-link, ... is there a way to open this window.location in a modal (like the link)? I searched for several hours yesterday but I did not find anything ..
EDIT 2
following the response of #VAD with render layout: false, we can put:
class ApplicationController < ActionController::Base
layout proc{|c| c.request.xhr? ? false : "application" }
end
which allows to have a false render for the js and true render for html. And so to keep my html pages with my starting layout.
Thx #dogenpunk
Render without layout when format is JS (needs drying)
You need to have different modals for every place you want to show the modal from. Like for software1 you need to show the modal with content relative to this particular object. For software2 you'll need to show the same model but with a brand new content relative to software2
Another option (and the better one) is that you may have one single modal for every Software object but by some event (for example actually showing the modal) you'll update the modal content with the data specific for this particular Software object. You may use some controller action for that
Additional
Try to pass software id as data parameters in your clickable elements and then to update your modal content with ajax and like that
<div class = 'clickable' data-id = "#{software.id}">
</div>
$(".clickable").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
$.get('/softwares/' + $(this).attr('data_id'), function(data) {
$('#software-modal body').html(data);
});
$('#software-modal').modal('show');
}
});
Of course I can't give precise code so you'll need to adjust it according to you current setup
Update
The idea is that you have show action in softwares_controller.rb which eventually renders the content for your modals:
def show
// some code you need
render layout: false
end
It will render show.html.erb view. In that view you should keep the html for your modal content. Notice that the action will render that view without layout. You need this because you will soon take this html and will put it into the modal, so you don't need any extra html like the layout.
So, you have an action which render the modal content for every software object by its id.
Now you need to put it into the modal.
You have clickable elements in your markup. You attach correspondent software ids to them as data-id. Then in your jquery code you use these ids to construct a url like this:
'/softwares/' + $(this).attr('data_id')
This url will lead to your show action in softwares_controller.rb. So by clicking on one of clickable elements you take the id, generate the url with it, send request to the url, get the response (the modal content) and then put it into the modal like this:
$.get('/softwares/' + $(this).attr('data_id'), function(data) {
$('#software-modal body').html(data);
});
Then you show the modal with already updated content:
$('#software-modal').modal('show');
The problem I'm running into deals with rendering a partial. I basically have a link which should get updated after the user puts some text in a text field. The first time it works fine. The user types in the text, the link gets updated and then when the link is clicked the partial is rendered with the correct information. However when the text is changed the link also gets update with the new parameters to pass to the action but when the link is clicked it still renders the old partial and no call to the corresponding action is made. Here is my view code:
label.control-label for="color_servers" Colors
.controls
input.input-xxlarge type="text" name="colors" id="project_colors" placeholder="comma separated colors" onblur="getColors(this)"
a data-toggle="modal" id="color_target" href="#" data-target="#color_modal" (Show colors)
.modal.hide#color_modal
.modal-header
button.close type="button" data-dismiss="modal" aria-hidden="true" ×
h3 Color list
.modal-body
.modal-footer
a.btn href="#" data-dismiss="modal" aria-hidden="true" Close
And this is my partial which I am rendering:
ul
- for color in #colors
li #{color}
I'm displaying the partial with the information in a lightbox type display. Here is my javascript code for the onBlur event of the text field:
function getColors(elem){
if(elem.value.trim()){
$.ajax({
url: "/project/check_colors",
type: "GET",
dataType: "json",
data:{
colors: elem.value
},
success: function(data){
$('#color_target').attr('href','/project/show_colors?colors=' + data.color_list)
console.log(document.getElementById("color_target").href)
console.log(data.input)
console.log(data.color_list)
}
});
}
}
So in the javascript code when I look at the output of the href attribute in the console, it shows the correct link. And finally here is my controller code:
def check_colors
colors = params[:colors].gsub(/\s+/, "").gsub(/,+/,",")
colors = colors.chomp(",")
color_list = Color.expand_colorset(colors).map(&:fullname)
render 'index', :json => {
:servers => color_list,
:input => colors
}
end
def show_colors
colors_string = params[:colors]
#colors = colors_string.split(",")
puts #colors
render partial: 'color_list'
end
The color_list variable is an array of colors which I send back in order to update the link with. I know that the show_colors action gets called called the first time because the #colors variable's value is printed in my terminal however when I update the text in the textfield and I click on the link again the action is not being called even though the link gets updated because nothing is printed in the terminal. It seems as if the partial is being cached, if that is the problem how can I prevent that. Also when I try to render my partial as a full fledged view rather than a partial, the action is called correctly every time and it renders the correct information even after changing the text field contents, but when I do that then my lightbox functionality does not work correctly. Any help will be appreciated, thank you.
hi guys for anyone who is interested I figured it out. It is kind of hacky but it works for me. I added this piece of code in my javascript outside of all functions, and it cleared the cached ajax.
$('body').on('hidden', '.modal', function(){$(this).removeData('modal');});
Hopefully it will save someone time in the future.
I am writing a form in Ruby on Rails and want to have a select box that calls a Javascript function. In the form file, this is the line I am using to try to add the select box:
<%= f.select :question_select, Question.all, :prompt => "New Question", :onchange => "displayQuestion(this)" %>
In my application.js file, I just have:
function displayQuestion(link) {
alert("Changed question");
}
I am going to be dynamically adding these form elements to a page, so I can't just use jQuery in the application.js file to add a function to a specific form element. Can anyone tell me what I'm doing wrong?
As you may know, Rails 3 strongly encourages UJS (unobtrusive JavaScript), which basically means that the JavaScript does the heavy lifting, and that you shouldn't tie your client-side interactivity in with your form generators. I would recommend doing something very similar here--just because you're adding elements dynamically doesn't mean you can't use jQuery to watch for changes on them.
In your template:
<%= f.select :question_select, Question.all, {prompt: "New Question"}, data: { question: true } %>
This creates something like the following:
<select id="..." data-question="true">
...
</select>
Then, in JavaScript, you can use event delegation to watch for change events on any element with data-question set on the entire document:
$(function() {
$(document).on('change', '[data-question]', function() {
// this == the element that fired the change event
alert('Changed question');
});
});
Note: Instead of using data-question, you could simply add a class to the element, and then modify your jQuery to use that class:
$(function() {
$(document).on('change', '.question', function() {
// this == the element that fired the change event
alert('Changed question');
});
});
I generally try to minimize the use of CSS selectors in my JavaScript so that designers are free to change them to whatever they want without breaking things, but it works just as well.
select_tag :variable, options_from_collection_for_select(:all, :id, :name), :onchange => 'your_onchange_handler()'
In a Rails app, I am loading a partial via an ajax call. (still using prototype)
The partial is a form that contains a textarea enriched with the yahoo yui_editor (similar to tinyMCE or FCKEditor)
<%= f.text_area :body, :class => 'rich_text_editor', :rows => "15", :style => "width : 90%;" %>
The yui_editor is not loaded and the textarea content is displayed as simple text when the form is loaded via an ajax call.
I tested that the yui_editor is active when the same partial is loaded directly without any ajax calls.
I know this has to do with the fact that the yui_editor javascript is not loaded but I have no idea how to solve this issue
Your help will be very much appreciated
Thanks
You need to start the YUI editor. Since the editor needs the id of the element, you need to specify a unique id in your partial.
See the YUI doc for more on the editor's parameters
Added
Are you adding the div via Ajax? In that case, you need to make the call to the YUI editor library after the div is added. Two ways to do that:
1) Your code which does the insert into the dom (with the results of the Ajax call) needs to explicitly call the YUI editor. Eg your Ajax results could include the element id of the text area, you could already know it in advance, etc.
2) You could include the script for calling the YUI editor in your Ajax results. But then you'll need to run the script(s) in the html after you've added them to the dom.
Setting innerHTML property of an element does NOT run any scripts in the html. But I have a script which does, see below.
The script is based on this SO Question
... do ajax call and get results in <body>
foo_el.innerHTML = body; // add results to the dom
exec_body_scripts(foo_el); // run any scripts in foo_el
//////////////////////////////////
function exec_body_scripts(body_el) {
// Finds and executes scripts in the dialog's body.
// Needed since innerHTML does not run scripts.
// NB: Only looks for scripts that are children or grandchildren of body_el.
// Doesn't look deeper.
function evalScript(elem) {
var data = (elem.text || elem.textContent || elem.innerHTML || "" ),
head = document.getElementsByTagName("head")[0] ||
document.documentElement,
script = document.createElement("script");
script.type = "text/javascript";
try {
script.appendChild(document.createTextNode(data)); // doesn't work on ie
} catch(e) {
// IE has funky script nodes
script.text = data;
}
head.insertBefore(script, head.firstChild);
head.removeChild(script);
};
// main section of function
var scripts = body_el.getElementsByTagName('SCRIPT'), i;
for (i = 0; scripts[i]; i++) {
evalScript(scripts[i]);
}
};
Partial example:
<% el_id = "rte_#{foo.id}"
# foo is the name of an object used by the partial. Using its id
# to ensure a unique id for the element on the page.
# Or use a simple counter "i". But in any case, the el_id must be unique
%>
<%= f.text_area :body, :class => 'rich_text_editor', :rows => "15",
:style => "width : 90%;", :id => el_id %>
<script>
(function() {
var myEditor = new YAHOO.widget.Editor('<%= el_id %>', {
height: '300px',
width: '522px',
dompath: true, //Turns on the bar at the bottom
animate: true //Animates the opening, closing and moving of Editor windows
});
myEditor.render();
})();
</script>
I am trying to write a rails application which lets you go to a certain page, say /person/:id. On this page it shows a set of available resources. I want each resource to have a button next to it, which reserves that resource to that person (by creating a new instance of an Allocation model.) As an extension, I'd like several buttons by each resource, that cancel reservations and do other things. I'd also like to input data alongside some of the buttons, e.g. to allocate some % of a resource.
My problem is I can't work out how to sensibly do this without repeating myself, or having a very hacky controller. How can I do this without matching on the value part of the submit buttons (the text on the buttons), or using any javascript?
Additionally, if you have two forms on a page, how do you set it up so changes on both forms are saved when any submit button is clicked?
im using jQuery, and this is what i did :
<script type="text/javascript">
$(document).ready(function() {
$('#bulk_print').click(function(){
var target = '<%= bulk_print_prepaid_vouchers_path(:format => :pdf) %>';
$('#prepaidvoucher_bulk_print').attr('action', target);
$('#prepaidvoucher_bulk_print').submit();
});
$('#bulk_destroy').click(function(){
var target = '<%= bulk_destroy_prepaid_vouchers_path %>';
$('#prepaidvoucher_bulk_print').attr('action', target);
$('#prepaidvoucher_bulk_print').submit();
});
});
</script>
<% form_tag '#', :method => :post, :id => 'prepaidvoucher_bulk_print' do %>
your form details
<button class="button" type="submit" id="bulk_print">
<%= image_tag("web-app-theme/printer.png", :alt => "Print Selected Vouchers") %> Print Selected Vouchers
</button>
<button class="button" type="submit" id="bulk_destroy">
<%= image_tag("web-app-theme/cross.png", :alt => "Delete Selected Vouchers") %> Delete Selected Vouchers
</button>
<% end %>
The idea is to change the form action on the fly, based on which button is clicked
Make each row in the list a form and put the info about the item in question there. Of course, you'll need to submit and reload the page with each action. The only way around this is to use checkboxes instead of buttons and make it one big form — or to use Javascript.
As for your second question, if you want to have a submit button affect two "forms," you should make them both part of the same form. You can have multiple submit buttons on the form if you need to. Otherwise, you could dynamically generate a third form with Javascript filled with the values from the original form — but that wouldn't work in all cases (e.g., file inputs).