I'm making resourceful routes for youtube videos. So, a person just pastes the youtube embed link in the form. In the controller I have a normal set of resourceful actions:
class VideosController < ApplicationController
def index
#videos = Video.all
end
def new
#video = Video.new
end
def create
Video.create(params[:video])
redirect_to :action => :index
end
def destroy
Video.destroy(params[:id])
redirect_to :action => :index
end
end
And in the view I'm just displaying it: (in Haml)
- #page_title = 'Video'
#videos
%ul
= list_of(#videos) do |video|
%h1= video.title
!= video.link
= link_to "Delete", video_path(video), :method => :delete
= link_to "Add new video", new_video_path
%p#top
= link_to 'Go to top ↑', '#'
For the one who don't use Haml, != escapes the string. video.link holds the YouTube embed code
The problem is that, when I create a new video, and when it redirects me back to the index page, the newly created video isn't displayed (the other ones are normally displayed). Only after I refresh the page, it's normally displayed.
I saw in the web inspector that the src attribute is missing from the iframe (so that's why the video isn't displayed). But when I look in the page source, everything is normal there. So, thinking it may be Javascript's fault, I tried disabling it. But nothing changed.
I don't think you want to escape it using haml... I think you want to call
video.link.html_safe
Note: if the user is pasting in the link, this is very unsafe.
Update --- If you have the javascript develop console open, you'll see this error pop up:
**Refused to execute a JavaScript script. Source code of script found within request.**
Check this answer for why it's refusing to due XSS Here's a method that is both safe and works. You'll paste in the youtube ID in the text field: ibWYROwadYs
index.erb
<% if session[:youtube].present? %>
<iframe width="480" height="360" src="http://www.youtube.com/embed/<%=session[:youtube]%>" frameborder="0" allowfullscreen></iframe>
<% end %>
<%= form_tag load_path do %>
<%= text_field_tag :youtube_id %>
<%= submit_tag "Submit" %>
<% end %>
<%= link_to "Clear", clear_path, :method => :delete %>
home_controller.rb
class HomeController < ApplicationController
def index
end
def smth
session[:youtube] = params[:youtube_id]
redirect_to :action => :index
end
def clear
session.clear
redirect_to :action => :index
end
end
Related
I want to make image_tag src dynamically changed via link_to.
So I make link_to. This link contain params. In controller my instance variable#selected_creator get this params successfully. And I confirm my instance variable is changed in view file, too. But my image_tagsrc is not changed. Did I must use with jquery? If then how can I render it?
creators/_row.html.erb
<% #creators.each do |creator| %>
<%= link_to 'select', creators_path(id: creator.id), :remote => true %>
<% end %>
creators_controller.rb
def index
#creators = Creator.all
if params[:id]
#selected_creator = Creator.find_by_id(params[:id])
else
#selected_creator = nil
end
end
creators/index.html.erb
<%= image_tag "#{#selected_creator.asset.url(:medium)}", :id => 'selected-creator' %>
You're using :remote => true which means that when you click the link, you're doing an ajax request. so you should create an erb template called index.js.erb which contains something like this:
$("#selected-creator").attr("src", <%= #selected_creator.asset.url(:medium) %>);
You'll also need to add respond_to :js to your controller so that it looks like this:
class SomeController < ApplicationController
respond_to :js
def index
#rest of code
end
end
I have a Rails app where I have a partial that I want a dropdown in so when a user is selected it will do a get method to the urls /timecards/:user_id which will be the controller's show method passing in the User's id field. I'm having a hard time with my paths in the form_tag and could use some help.
Here is my partial in the view:
<%= form_tag timecard_path, :method => :get do %>
<%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username) %>
<%= submit_tag "View Employee", class: "btn btn-primary" %>
<% end %>
From rake routes I get the following output:
timecards GET /timecards(.:format) timecards#index
POST /timecards(.:format) timecards#create
new_timecard GET /timecards/new(.:format) timecards#new
edit_timecard GET /timecards/:id/edit(.:format) timecards#edit
timecard GET /timecards/:id(.:format) timecards#show
PUT /timecards/:id(.:format) timecards#update
DELETE /timecards/:id(.:format) timecards#destroy
Here is my controller: timecards_controller.rb
class TimecardsController < ApplicationController
before_filter :disallow_clients, :disallow_medics, :disallow_employee, :disallow_supervisor
def index
#clock_events = ClockEvent.includes(:user).search(params[:search])
respond_to do |format|
format.html do
#clock_events = #clock_events.paginate(:per_page => params[:per_page] || 20, :page => params[:page]).order('users.username asc').order('clock_in desc')
end
format.csv { send_data ClockEvent.to_csv(#clock_events.order('users.username asc').order('clock_in desc')) }
end
end
def new
#clock_event = ClockEvent.new
end
def create
parse_times!
#clock_event = ClockEvent.new(params[:clock_event])
if #clock_event.save
redirect_to timecard_path(#clock_event.user.id), notice: "Entry added for #{#clock_event.user.username}".html_safe
else
render :new, notice: "Time Card Entry failed to Save".html_safe
end
end
def show
#user = User.find(params[:id])
#clock_events = #user.clock_events.search(params[:search])
respond_to do |format|
format.html do
#clock_events = #clock_events.paginate(:per_page => params[:per_page] || 5, :page => params[:page]).order('clock_in DESC')
end
format.csv { send_data ClockEvent.to_csv(#clock_events.order('clock_in desc')) }
format.pdf do
pdf = TimeCardPdf.new(#clock_events, #user)
send_data pdf.render, filename: "timecard-#{#user.username}",
type: "application/pdf",
disposition: "inline"
end
end
end
def edit
#user = User.find(params[:id])
#clock_events = #user.clock_events.search(params[:search]).order("clock_in ASC").paginate(:per_page => 10, :page => params[:page])
end
def update
parse_times!
#clock_event = ClockEvent.find(params[:clock_event][:id])
if #clock_event.update_attributes(params[:clock_event])
redirect_to edit_timecard_path(#clock_event.user.id), notice: "Updated Successfully".html_safe
else
redirect_to :back, notice: "Woops.".html_safe
end
end
private
def parse_times!
params[:clock_event].parse_time_select! :clock_in if params[:clock_event].has_key? 'clock_in(5i)'
params[:clock_event].parse_time_select! :clock_out if params[:clock_event].has_key? 'clock_out(5i)'
end
end
So I believe I'm calling the path properly in the form_tag but when I load the page I get the error: No route matches {:action=>"show", :controller=>"timecards"} Even though there is a show action in the timecards_controller.
I think something I have to set the form_tag for an explicit url and somehow pass in the :id of the user in the params. But I'm a bit stuck on how to do this.
So to summarize. When I have the dropdown, I select a user, click "View Employee", and that should go to the show action in timecards_controller.rb with the url of /timecards/3 (as an example). I've never used form_tag in this way before so passing a path or explicit url is a bit foreign to me.
A simple hotfix:
The simplest possible fix would be to change the form to a bunch of links.
<%= User.employee.order("username ASC").each |u| %>
<%= link_to u.username, timecard_path %>
<% end %>
Otherwise you could use Javascript to simply make the form redirect:
<%= form_tag timecodes_path, :method => :get, :id => 'timecode_employee' do %>
<%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username) %>
<%= submit_tag "View Employee", class: "btn btn-primary" %>
<% end %>
$("#timecode_employee").submit(function(e){
var form = $(this);
// redirect to timecards/:id
window.location = form.attr('action') + form.find('select').val();
e.preventDefault();
});
Tips for redesign
Your design could be radically improved by adding an underlying TimeCard model.
Here is one very common case which tells you why:
The client decides that they want to have managers sign off on time
cards every month.
Oh, drat. Now we need get all the ClockEvents in that scope and update a 'clock_events.state' on each.
But then the client also wants to know who has signed off the card. So you add a clock_events.signed_off_by_id and update all the clockevents. And then they want three managers to sign off, etc.
An alternative design
Note that this is a opinionated generic example.
class ClockEvent < ActiveRecord::Base
enum status: [:clocked_in, :clocked_out]
has_many :users
belongs_to :time_card
end
class TimeCard < ActiveRecord::Base
belongs_to :user
has_many :clock_events
accepts_nested_attributes_for :clock_events
end
class User < ActiveRecord::Base
has_many :time_cards
has_many :clock_events, through: :time_cards
end
A TimeCard might be automatically issued per month or if you never want to change than just stick with one TimeCard per user.
Lets go with some traditional routes here:
resources :time_cards
end
resources :clock_events do
end
resources :users, shallow: true do
resources :clock_events do
end
resources :time_cards do
end
end
Now imagine we had a classical punch card time clock.
We would punch in with:
POST /clock_events { user_id: 1, time_card_id: 5 }
And punch out with:
PATCH /clock_events/1 { status: :clocked_out }
Thats REST for you.
Nesto Presto
We have nested routes for time cards and clock_events per user:
GET /users/1/time_cards
GET /users/1/clock_events
From the we can choose to either build a dedicated UserTimeCardController or we can scope by the user id param in TimeCardsController.
class TimeCardsController
def index
#time_cards = TimeCard.all
#time_cards = #time_cards.where(user: params[:user_id]) if params[:user_id]
#users = scope.all
end
end
Filtering
But imagine if we want managers be be able filter the number of employees he sees on the index - a good architecture would look something like this:
class TimeCardsController
def index
#time_cards = TimeCard.all
#time_cards = #time_cards.where(user: params[:user_id]) if params[:user_id]
if params[:filters]
#time_cards = #time_cards.search(params[:search])
end
end
end
And on our index page we would add a form like this:
<%= form_tag(time_cards_path, method: :get) %>
<%= select_tag options_from_collection_for_select(User.employee.order("username ASC"), :id, :username), multiple: true %>
<%= submit_tag "Filter", class: "btn btn-primary" %>
<% end %>
The route that you have defined:
timecard GET /timecards/:id(.:format) timecards#show
It requires an id to show the correct timecard. But when you are invoking it in form_tag, you are just sending, timecard_path with no id. So you do need to send an id or a timecard object, and Rails will automatically extract the id out of it.
So, that should be:
form_tag #timecard do
# other code
end
#timecard must be instantiated in the action that renders your partial, and it must be a valid TimeCard object.
I'm definitely going to take #maxcal's advice and rewrite this thing, but since I had to ship fast I came up with an ugly UI hack that "works". I don't like it but it does exactly what I need it to do.
<ul class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<button class="btn btn-medium btn-primary">View Employees</button>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<% #users.each do |u| %>
<li><%= link_to "#{u.username}", timecard_path(u) %>
<% end %>
</ul>
</ul>
I don't like all the loading and this is really dirty, but in this instance since I was breaking all sorts of Rails conventions and had to ship this afternoon it'll work until I can come back and make it better.
Let the laughs begin. :)
I've built a fav icon that works like a Facebook like/unlike button, and I'm trying to update it via Ajax but can't seem to get it working. I think I'm missing something simple.
events_controller.rb
def fav
#event = Event.find(params[:id])
current_user.toggle_flag(#event, :fav) #events_helper.rb
respond_to do |format|
format.js
end
end
events_helper.rb
def toggle_fav(event, user)
if user_signed_in? #change icon from heart to empty heart and vice-versa
link_to user.flagged?(event, :fav) ? #if the event is flagged
content_tag(:span, " ", :class => "glyphicon glyphicon-heart") : #show full heart
content_tag(:span, " ", :class => "glyphicon glyphicon-heart-empty"), #else show empty heart
fav_event_path(event), #path that changes the state of the heart
:remote => true
else
link_to content_tag(:span, " ", :class => "glyphicon glyphicon-heart-empty"), fav_event_path(event) #prompt user to sign in
end
end
Events/index.html.erb
<% #events.each do |event| %>
<%= render 'each_event', :event => event %>
<% end %>
_each_event.html.erb (relevant info only)
<div class="row">
<div class="event-div">
<div class="event-details">
<ul><li class="fav-li"><%= render 'fav_li', :event => event %></li></ul>
</div>
</div>
</div>
_fav_li.html.erb
<%= toggle_fav(event, current_user) %>
fav.js.erb
$('.fav-li').html('<%=j render 'events/fav_li', :event => event, :layout => false %>');
I followed this tutorial to make the like button: http://www.youtube.com/watch?v=GG-kCSx0taU
And am using the make_flaggable gem.
Right now when I update the button it directs me to http://localhost:3000/events/23/fav but that template doesn't exist (and shouldn't). The events are displayed on the event index page after going through the index filters. There are multiple events on a single page.
I'd appreciate any help! Thanks.
Your controller is doing format.js in the respond_to block. It's expecting a js template to exist so it can run it as the response. Something like app/views/events/fav.js.erb. This file will contain some js code that will update your view with the results of your controller action e.g. update the icon from glyphicon-heart-empty to glyphicon-heart.
Here's another question/answer discussing js.erb templates: How does js.erb work
Hope that helps.
I'm trying to figure out how to allow a user to click on a link or button on the index page to clear out all of the objects from the app's database, and then redirect to a newly cleared index page. So, with the example model Article, I expect it should have something to do with an Article.destroy_all method, and I'm expecting it would be a simple solution, but I've tried some variations and am just not sure of how to actually implement it.
So it would be another action in your controller. If we're dealing with Articles then the controller would be:
class ArticlesController < ApplicationController
def indef
#articles = Article.all
end
def destroy_them_all
Article.destroy_all
redirect_to articles_path
end
end
And in the view where you want the user to click on a button to destroy all articles:
<%= link_to 'Destroy them all', destroy_them_all_path, method: :delete, confirm: 'Are you crazy?' %>
Don't forget to add a named route in your routes file:
match '/articles/destroy_them_all', to: 'Articles#destroy_them_all', via: :delete
That should work. Though you might have to check rake routes to make sure I got the destroy_them_all_path correct.
try this:
Article controller:
def destroy_all
#Articles = Article.all
#Articles.each do |a|
a.destroy
end
redirect_to articless_path, notice: "Delted"
end
routes:
post "articles/destroy_all"
view:
<%= form_tag ({ :controller => 'articles', :action => 'destroy_all' }) do%>
<%= submit_tag 'destroy all'%>
<% end %>
I have a submit form that is shown in a lightbox. I validate the form pretty heavily but there are circumstances where a playlist is already submitted, in which case I can't tell before a form is submitted.
Currently I have :disable_with => 'Submitting...'
It would be completely awesome if the error was sent back to the form, and the submit button would re-enable.
Would this be possible without too much complication?
I also have a second related question... Because my form is in a lightbox, it's code is actually ON the index page (my app is just one page pretty much). This means that there really is no 'new' action, but rather a #playlist = Playlist.new(:title => ...) in my index action (along with a #playlists = Playlist.all, etc). In my routes I did this: resources :playlists, :only => [:create]
Does this sound about right the way I did it?
EDIT: HEre is some code, although it's basically about as simple as you can imagine it.
The following kind of works... it creates the playlist if its valid, otherwise it does nothing. Both times create.js.erb is called.. i just dont now how to make this work to completion now. on success i need to close the window, on failure i need to load the errors into the form thats already on the screen. Im not sure where that goes though :/
before_filter :load_genres, :only =>[:index, :user]
before_filter :new_playlist, :only => [:index, :new]
def index
#playlists = Playlist.order('created_at DESC').page(params[:page]).per(30)
end
def create
#playlist = Playlist.new(params[:playlist])
respond_to do |format|
if #playlist.save
format.html { redirect_to root_path, :notice => "Playlist submitted" }
format.js {}
else
format.html { render :action => :new, :layout => !request.xhr? }
format.js {}
end
end
end
def new
end
def load_genres
#genres = Genre.order(:name)
end
def new_playlist
#playlist = Playlist.new(:title => params[:title], :url => params[:url], :description => params[:description])
end
Heres the first like of my form (located in index.html.erb):
<%= form_for #playlist, :remote => true do |f| %>
I currently have no html or code in create.html.erb
Here is my solution to this Issue.... I put these in all my js.erb files
$('#flash').html("<%= escape_javascript raw(flash_display) %>");
With this Helper
def flash_display
response = ""
flash.each do |name, msg|
response = response + content_tag(:div, msg, :id => "flash_#{name}")
end
flash.discard
response
end
this works well with the flash div already set up in layout
<div id="flash">
<% flash.each do |name, msg| %>
<%= content_tag :div, msg, :id => "flash_#{name}" %>
<% end %>
</div>
Hope this Helps
Without seeing your code I can only make broad suggestions.
One would be to set up a js.erb response to the controller action-- or you could do a js script tag in an if clause inside your HTML. Either way you would use jQuery to update the element.
Inside the js/html erb file:
jQuery('#flash_area).html(<flash message>);
The the flash area in the view would need an id of flash_area (or whatever you want to name it).