Having the following code in an ERB view:
<%= content_tag(:div, id: 'stat', data: {_var_: '_foo_'}) %>
generates the following HTML:
<div id="stat" data--var-="_foo_">
</div>
My intention is to obtain
<div id="stat" data-_var_="_foo_">
</div>
i.e. I do not want
data--var-
but instead
data-_var_
How can I achieve this, please ?
As pointed in the ActionView::Helpers::TagHelper docs:
To play nicely with JavaScript conventions sub-attributes are
dasherized. For example, a key user_id would render as data-user-id
and thus accessed as dataset.userId.
To illustrate, you can check in the Rails source code (tag_helper.rb) prefix_tag_option invoking key.to_s.dasherize:
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
#...#
content_tag_string(name, content_or_options_with_block, options, escape)
#...#
end
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
#...#
end
def tag_options(options, escape = true)
# ...
# TAG_PREFIXES = ['aria', 'data', :aria, :data].to_set
# invoke prefix_tag_option only if it's a data- sub-attributes
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
#...#
output << prefix_tag_option(key, k, v, escape)
end
#...#
end
def prefix_tag_option(prefix, key, value, escape)
key = "#{prefix}-#{key.to_s.dasherize}"
#...#
end
If you don't want to dasherize your keys, a possible "workaround" is to set the data-attribute directly in the options hash, like this:
<%= content_tag(:div, "test", { id: 'stat', 'data-_var_': '_foo_' }) %>
This way, Rails will render:
<div id="stat" data-_var_="_foo_">test</div>
Update: I have posted this to github jquery-datatables-rails as it appears to be an issue with that gem or, more likely, my use of it. I am posting the updated copy here as well with more of a description to ask for help. Note that, while the respond_to block is executed twice, the datatable code is only executed on the second execution of the block.
I am trying to pass a variable named my_view to the Items Controller index method so that I can selectively display the results. The current code works, sort of... In the end, the view is not maintained and any action in the resultant table, such as scrolling, causes the table to revert to showing all items instead of the desired selection.
The basic problem is that the index method is always executed twice any time a button is clicked. The first time, the my_view variable is correctly set. The second time, it is always nil. The jquery-datatables-rails gem is executed on both passes and is probably involved. It almost seems like I get two different transactions. I've even tried instance variables and they are also nil during the second pass. I don't understand how or why this happens.
To "fix" this problem, I set session[:my_view] during the first pass and then do not set it again on the second pass. During the ItemsDatatable.new execution, I then must clear session[:my_view] or it would be retained for the next transaction giving incorrect results. However, this results in the problem that I stated in that any change to the view, such as scrolling, causes a reversion to showing all items instead of the desired selection.
Curiously, I don't seem to have any other controller methods with this sort of anomaly, or at least haven't noticed it. Can someone tell me why the index method is executed twice, and how I might resolve this issue? Thank you!
EDIT UPDATE: I have one other controller in the admin namespace that uses respond_to with HTML/JSON options. It also is executed twice, so it seems that may (?) be related. Still seems odd behavior to lose all variables and execute it the second time, but I don't know. Is it related and how might I resolve it? Thanks...
Here are my buttons:
<%= link_to 'My Items', items_path(my_view: current_associate.id), class: 'btn btn-primary kc-wide' %>
<%= link_to 'All Items', items_path(my_view: "all"), class: 'btn btn-primary kc-wide' %>
And the Items Controller with the index method:
class ItemsController < ApplicationController
def index
session[:my_view] ||= params[:my_view]
respond_to do |format|
format.html
format.json { render json: ItemsDatatable.new(view_context) }
end
end
end
Related routes, just in case?
POST /items_index(.:format) items#index
items GET /items(.:format) items#index
POST /items(.:format) items#create
new_item GET /items/new(.:format) items#new
edit_item GET /items/:id/edit(.:format) items#edit
item GET /items/:id(.:format) items#show
PATCH /items/:id(.:format) items#update
PUT /items/:id(.:format) items#update
DELETE /items/:id(.:format) items#destroy
The Rails datatable code is:
class ItemsDatatable < ApplicationController
before_action :check_if_associate
delegate :params, :h, :link_to, :edit_item_path, :new_item_path, :location, to: :#view
def check_params(params)
# When using the .json suffix in URI, need to stub params so that I can see that JSON information
params[:draw] = 1 if params[:draw].blank?
params[:columns] = Array.new(1, {data: 0, name: '', searchable: true, orderable: true, search: {value: '', regex: false}}) if params[:columns].blank?
params[:order] = Array.new(1, {column: 0, dir: 'asc'}) if params[:order].blank?
params[:start] = 0 if params[:start].blank?
params[:length] = 10 if params[:length].blank?
params[:search] = {value: '', regex: false} if params[:search].blank?
end
def initialize(view)
#view = view
check_params(params)
#view
end
def as_json(options = {})
{
draw: params[:draw].to_i,
recordsTotal: Item.count,
recordsFiltered: items.total_entries,
data: data
}
end
private
def data
todays_date = Time.zone.now.to_date
items.map do |item|
status = item.status
# If current associate has it, show where it is.
if item.checkedout?(#view.current_associate)
status = "#{item.status}-#{item.lastloc}"
end
# If it's checked out but someone else has it, show who has it.
if item.not_available? and !item.checkedout?(#view.current_associate)
status = "#{item.status}-#{item.location}"
end
# Override the above if in return status, just showing that.
status = item.status == "Rtn" ? "Rtn" : status
odometer = item.odometer.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, "\\1,")
msrp = item.msrp.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, "\\1,")
age_date = item.age_date.nil? ? todays_date : item.age_date.to_date
[
link_to(item.stock_number, edit_item_path(item)),
ERB::Util.h(item.year),
ERB::Util.h(item.make),
ERB::Util.h(item.model),
ERB::Util.h(item.color),
ERB::Util.h(status),
ERB::Util.h(odometer),
ERB::Util.h(msrp),
ERB::Util.h((todays_date - age_date).to_i)
]
end
end
def items
#items ||= fetch_items
end
def fetch_items
items = Item.order("#{sort_column} #{sort_direction}")
items = items.includes(:item_location, item_location: [:locator])
items = items.includes(:key, key: [:key_location])
items = items.page(page).per_page(per_page)
unless #view.session[:my_view].blank? || #view.session[:my_view] == "all"
associate = Associate.find(#view.session[:my_view]).name
associate = associate.gsub(/'/, "''") # .gsub(/'/, "\\\\\'")
items = items.where("clshadow = \'#{associate}\'")
end
#view.session[:my_view] = nil
if params[:search][:value].present?
items = items.where("stock_number ilike :search or yrshadow ilike :search or mkshadow ilike :search or mdshadow ilike :search or coshadow ilike :search or status ilike :search", search: "%#{params[:search][:value]}%")
end
items
end
def page
params[:start].to_i/per_page + 1
end
def per_page
params[:length].to_i > 0 ? params[:length].to_i : 10
end
def sort_column
columns = %w[stock_number yrshadow mkshadow mdshadow coshadow status odometer msrp age_date]
columns[params[:order][0][:column].to_i]
end
def sort_direction
params[:order][0][:dir] == "desc" ? "desc" : "asc"
end
end
Related JavaScript code:
var itemstable = $('#itemstable').DataTable({
responsive: true,
autoWidth: false,
pagingType: 'full',
jQueryUI: true,
processing: true,
serverSide: true,
ajax: {
url: 'items_index.json',
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: function(d) {
return JSON.stringify(d);
}
},
columns: [null, null, null, null, null, null,
{className: 'dt-right'},
{className: 'dt-right'},
{className: 'dt-right'}
]
});
And the view:
<div class="span9">
<p>
<table id="itemstable" class="display dt-responsive no-wrap table-striped" width="80%">
<thead>
<tr>
<th class="all">Stock No.</th>
<th class="all">Year</th>
<th class="all">Make</th>
<th class="min-tablet">Model</th>
<th class="min-tablet">Color</th>
<th class="all">Status</th>
<th class="desktop">Mileage</th>
<th class="desktop">MSRP</th>
<th class="desktop">Aged</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<%= link_to 'My Items', items_path(my_view: current_associate.id),
class: 'btn btn-primary kc-wide' %>
<%= link_to 'All Items', items_path(my_view: "all"),
class: 'btn btn-primary kc-wide' %>
<%= link_to 'Recent Items', x_logs_path,
class: 'btn btn-primary kc-wide' %>
<%= link_to 'Home', '/', class: 'btn btn-primary kc-wide' %>
</div>
Try this:-
def index
session[:my_view] = params[:my_view] unless request.xhr?
respond_to do |format|
format.html
format.json { render json: ItemsDatatable.new(view_context) }
end
end
It appears that the gem executes independently, without transferring control to the supplied code, during the first pass in order to set up the parameter list with all the desired variables. It then appears to force a reload to initiate the process with the parameter list as desired. I am assuming this from reviewing the variables during the first and second passes. During this process, it forgets any other variables, though I don't think that it should do that.
In any case, I have to use session variables to maintain status during this reload. I changed the controller index as follows, and then eliminated the clearing of session[:my_view] in my datatables controller.
def index
unless params[:my_view].blank?
session[:requested_view] = params[:my_view]
end
session[:my_view] = session[:requested_view]
respond_to do |format|
format.html
format.json { render json: CarsDatatable.new(view_context) }
end
end
In my navigation, I need '.active' to tell user if they are in the current page. So I defined a helper to generate li.active dynamically.
def nav_link(link_text, link_path
class_name = current_page?(link_path) ? 'active' : nil
content_tag(:li, :class => class_name) do
link_to link_text, link_path
end
end
so it can generate
<li class="active">
Resources
</li>
However, for one specific link, I want to add <strong> to the <a> inside <li> to make it stands out from others. How can I do it?
<li class="active">
<strong>Resources</strong>
</li>
UPDATE:
From this
To this
Every nav link(e.g. new,resources, videos) all uses the nav_link helper. What I need, is a way to insert some html(or CSS) into the resouces link. To make it stands out.
Could you not make the change in css?
.active {
font-weight: bold;
}
You can certainly nest content_tag to add strong node:
def nav_link(link_text, link_path, additional_class = nil)
class_name = current_page?(link_path) ? 'active' : nil
content_tag(:li, :class => class_name) do
content_tag(:strong) do
link_to link_text, link_path
end
end
end
Update:
You could check for "Resources" as follows to add the strong tag:
def nav_link(link_text, link_path, additional_class = nil)
class_name = current_page?(link_path) ? 'active' : nil
content_tag(:li, :class => class_name) do
link = link_to(link_text, link_path)
if link_text == 'Resources'
content_tag(:strong) { link }
else
link
end
end
end
I'm using Kaminari on my site with a 'load more' button to show another six items when clicked. It works great but when I try to add a sorting order it's not passing the params to the link_to_next_page def although I can see it in the html...
The other question asked on this said to pass the params to the link_to_next_page but it doesn't make a difference.
Example: When I try to sort by lowest price > highest price the first six items are sorted but on 'load more' the sorting order is random.
Can anyone advise here??
Thanks.
Some code...
index.html.erb
<div id="offers">
<%= render :partial => #television_offers %>
</div>
<%= link_to_next_page #television_offers, 'Load More', :remote => true, :id=>"load_more_link", :params => params %> </div>
index.js.erb
$('#offers').append("<%= escape_javascript(render :partial => #television_offers)%>");
$('#load_more_link').replaceWith("<%= escape_javascript(link_to_next_page(#television_offers, 'Load More', :remote => true, :id=>'load_more_link', :params => params))%>");
application_helper.rb
def link_to_next_page(scope, name, options = {}, &block)
param_name = options.delete(:param_name) || Kaminari.config.param_name
link_to_unless scope.last_page?, name, {param_name => (scope.current_page + 1)}, options.merge(:rel => 'next') do
block.call if block
end
end
television_offers_controller.rb
def index
#television_offers = TelevisionOffer.page(params[:page]).per(6)
if params[:filter] == "large_screens"
#television_offers = #television_offers.large_size
elsif params[:filter] == "small_screens"
#television_offers = #television_offers.small_size
elsif params[:filter] == "price"
if params[:order] == "asc"
#television_offers = #television_offers.asc(:offer_price)
else
#television_offers = #television_offers.desc(:offer_price)
end
else
#television_offers = #television_offers.best
end
end
For anyone experiencing the same problem this was solved by simply updating kaminari to the latest version
I am doing this for a school project and it is not working. :(
How do I get out the selected[petowner_id] from the view and make it usable in a ruby controller?
How do I make the #selected_pet = params([petowner_id]) in the controller that comes in from the view to function? Currently it renders an error message when I try to set it. :(
I am getting very tired of it not working.
The controller from Pets controller
class PetsController < ApplicationController
# GET /pets
# GET /pets.json
def monsters
#Finds the current user
if current_user
#user = current_user
#pets_kept = [] #why?
##petowner = Petowner.find(params[:petowner][:id])
#if(params[:commit])
#end
#monster = "Eeeep"
#mypets=[]
#all_my_pets = #user.petowners
#options value = 2
#params { selected_petowner[petowner_id]}
#selectpet = params{[selected][petowner_id]}
#petowner = Petowner.find_by_id(params[:id])
#pet = Pet.find_by_id(params[:pet_id])
#Find the petowners that the user currently has
##mypets = #user.petowners
#This is my way of doing things in a C++ fashion, I don't get all ruby things
#user.petowners.each do |pet|
##selected_pet = pet.find(params[:selected])
# if pet.hp != 0
# #pets_kept << pet #Dont recall seeing the << before in ruby but for C++ statement used for cout statements
#if pet.select
# #selected_pet = pet.select
#end
end
##selected_pet = Petowner.find(params[:petowner][:selected])
#end
#selected_pet = 1 ##user.petowners.find(params[:id])
#mypets = current_user.petowners.select{|pet| pet.hp !=0}
#raise "I am here"
##selected_pet = #mypets.find(params[:id][:selected])
##mypets = #pets_kept
end
The code from the view that doesn't go back to the controller variable and set it. :(
<select id="petowner_id" name="selected[petowner_id]">
<%= #all_my_pets.each do |pet| %>
<option value="<%= pet.id %>"><%= pet.pet_name %></option>
<% end %>
</select>
Previous step from pets/monsters view that doesn't work at all from previous collection. :(
<%= form_for :petowner, :url => petowner_fights_path(#selected_pet, #pet) do |f| %>
<p>Select a pet <%#= f.collection_select(:petowner, :petowner_id, #user.petowners, :petowner_id, :pet_name) %></p>
<%= render 'monsterinfo' %>
<div class="outer"></div>
<%= f.submit "Fight Selected Monster" %>
<% end %>
You probably want params[:petowner][:petowner_id]. Definitely don't construct the select with html in a view.
By the way, it's really helpful to see all of the params passed in to a controller action. I tend to raise params.to_yaml when I need to do that.