I'm playing with the messenger gem in rails 4.
I have a graph of nodes and I want to be able to bring up a message box (initially in a different page but will make it a partial later) when a node is pressed so that the current user can message that node.
The id for the clicked node is kept in a div called NameID
At the moment all I've got working is a button that opens the new message page and then you can choose a user from a drop down list. I guess I want that drop down list- the recipient- to be prepopulated from the currently clicked node on the index page.
Here is what I have so far:
index.html.erb
<p><a class="btn btn-lg btn-primary" id="BtnMessageNode" href="/messages/new">Start conversation</a></p>
<div id=NameID><<THIS IS POPULATED BY JAVASCRIPT>></div>
messages_controller.rb
class MessagesController < ApplicationController
before_action :authenticate_user!
def new
#chosen_recipient = User.find_by(id: params[:to].to_i) if params[:to]
end
def create
recipients = User.where(id: params['recipients'])
conversation = current_user.send_message(recipients, params[:message][:body], params[:message][:subject]).conversation
flash[:success] = "Message has been sent!"
redirect_to conversation_path(conversation)
end
end
helpers/messages_helper.rb
module MessagesHelper
def recipients_options(chosen_recipient = nil)
s = ''
User.all.each do |user|
s << "<option value='#{user.id}' data-img-src='#{gravatar_image_url(user.email, size: 50)}' #{'selected' if user == chosen_recipient}>#{user.name}</option>"
end
s.html_safe
end
end
messages/new.html.erb
<% page_header "Start Conversation" %>
<%= form_tag messages_path, method: :post do %>
<div class="form-group">
<%= label_tag 'message[subject]', 'Subject' %>
<%= text_field_tag 'message[subject]', nil, class: 'form-control', required: true %>
</div>
<div class="form-group">
<%= label_tag 'message[body]', 'Message' %>
<%= text_area_tag 'message[body]', nil, cols: 3, class: 'form-control', required: true %>
</div>
<div class="form-group">
<%= label_tag 'recipients', 'Choose recipients' %>
<%= select_tag 'recipients', recipients_options(#chosen_recipient), multiple: true, class: 'form-control chosen-it' %>
</div>
<%= submit_tag 'Send', class: 'btn btn-primary' %>
<% end %>
There are basically two ways to pass parameters to GET routes:
A. Named segments
/users/:user_id/message/new
This nested route would be great if you are sending a message to a single user.
B. Query parameters
Rails supports query parameters as well:
/message/new?to=2
Rails automatically adds query parameters to the params hash. So in this case you would do params[:to].
You can use the Rails route helpers so that you don't have to deal with encoding urls yourself:
new_message_path(to: #user.id)
Use query params for optional parameters like filters and sorting or in this case a preset. Don't use them like users?id=5.
Related
I got this error with my user registration form in Rails:
ActionView::Template::Error (First argument in form cannot contain nil or be empty)
View:
<%= form_for User.new, url: create_user_path, method: :post do |f| %>
<div class="form-group">
<%= f.label :name, t("settings.account.fullname"), class: "form-label" %>
<%= f.text_field :name, class: "form-control #{form_is_invalid?(User.new, :name)}", placeholder: t("settings.account.fullname"), autofocus: "", required: "" %>
<div class="invalid-feedback d-block"><%=User.new.errors.full_messages_for(:name).first %></div>
</div>
Controller:
def create
#user = User.new(user_params)
#user.provider = #user_domain
end
and so on..
I'm new to ROR. Can any one help me with this please?
Apart from the bizarre error message this is just not how you do forms in Rails.
Since you're passing User.new to the form it will always be bound to a new instance of User. That means that anything the user has entered into the form will blanked out on an invalid form submission. User.new.errors.full_messages_for(:name).first will give a nil error since there are no validation messages on a record that has not been validated.
What you actually want is something like:
# routes.rb
resources :users
class UsersController < ApplicationController
# GET /users/new
def new
#user = User.new
end
# POST /users
def create
#user = User.new(user_params)
#user.provider = #user_domain
# ...
end
# ...
end
<%= form_with(model: #user) do |form| %>
<div class="form-group">
<%= f.label :name, t("settings.account.fullname"), class: "form-label" %>
<%= f.text_field :name, class: "form-control #{form_is_invalid?(User.new, :name)}", placeholder: t("settings.account.fullname"), autofocus: "", required: "" %>
<% if #user.errors.has_key?(:name) %>
<div class="invalid-feedback d-block">
<%= #user.errors.full_messages_for(:name).each do |msg| %>
<p><%= msg %></p>
<% end %>
</div>
<% end %>
<% end %>
If you just follow the Rails conventions you do not need to specify the URL or the method which are derived from the record. This lets you reuse the same form for updating existing records without changing anything in your code.
create_user_path is in itself extremely unidiomatic as Rails doesn't have a separate path for creating records. You create records by sending a POST request to the collection path (/users).
please am stocked trying to create a way for an admin to preview a file before creating users from the file("an xls file"), the problem am faced with is not knowing how to dynamically change the route base on the button the admin clicked, the buttons are supposed to either go to preview the file or create the users from the file.
<%= form_for :create_student, url:"/create_from_file", remote:true, html:{id:"create_student_from_file"} do|f| %>
<div id="student-fields0" class="fields_div">
<%= f.label "Upload file"%>
<%= f.file_field :Upload_file %>
<%= f.submit :create, id: 'create_student' %>
<div id="preview_student" class="button">preview</div>
</div>
<% end %>
I would solve this that way, for example:
Controller part
class YourContoller < ApplicationController
# POST /create_from_file
def create_from_file
# Initialize user or something
user = User.new(params)
if params[:preview]
return redirect_to(:preview_from_file, user: user)
end
# Create new user here
end
# GET /preview_from_file
# Add another view for preview page
def preview_from_file
#user = params[:user]
end
end
View part
<%= form_for :create_student, url:"/create_from_file", remote:true, html:{id:"create_student_from_file"} do|f| %>
<div id="student-fields0" class="fields_div">
<%= f.label "Upload file"%>
<%= f.file_field :Upload_file %>
<%= f.submit :create, id: 'create_student' %>
// This is what changed compared to your current view
<%= submit_tag 'preview', name: 'preview', value: 'true', class: 'button' %>
</div>
<% end %>
I am having trouble passing a non-model form field from view to controller. The value I am trying to pass is amount
Thank you.
views/donations/index.html.erb
<%= form_tag donations_path, style: (current_user.card_last4? ? "display:none" : nil) do %>
<div id="error_explanation">
<% if flash[:error].present? %>
<p><%= flash[:error] %></p>
<% end %>
</div>
<article>
<%= label_tag(:amount, 'Donation Amount:') %>
<%= text_field_tag(:amount) %>
</article>
<%= link_to 'Donate', new_donation_path(amount: :amount), class: 'btn btn-primary', id: 'donateButton' %>
<% end %>
controllers/donations_controller.erb
def create
customer = current_user.stripe_customer
amount = params[:amount]
token = params[:stripeToken]
begin
Charge.charge(amount, token)
end
...
models/charge.rb
def self.charge(amount, token)
charge = Stripe::Charge.create(
amount: amount,
source: token,
currency: 'usd',
description: 'Test Charge'
)
end
...
Use a view tag like
<%= text_field_tag 'donation[amount]' %>
And permit the parameter in your controller
def donation_params
params.require(:donation).permit(:amount)
You can access the value with donation_params[:amount].
You shouldn't use link_to to trigger the form submission. Use submit_tag instead. Besides that, make sure your strong params whitelist whatever you're submitting.
I guess you are not using strong params. Why not just add |f| at the end like this
<%= form_tag donations_path, style: (current_user.card_last4? ? "display:none" : nil) do |f| %>
and then use <%= f.text_field :amount %>
Then at the params you should do something like params["donation"]["amount"] to get the value
EDIT: at the end change link_to for f.submit
I am using elasticsearch on my rails 4 app to search through my articles.
However, when an article is not found it redirects to a page where it alerts the user that no results are found, and allows to request a page. Instead of it saying "no results found" I want it to say "no results for ______ found" in which is takes the search bar query.
I have defined a method in the articles_controller in order to get the query but it won't show up on the page since the "no results page" uses a different controller.
What would be the best way to pull the method onto another controller or view in this case? Having trouble finding a straight forward answer for what I am trying to do. Thank you very much.
articles_controller:
def index
if params[:query].present?
#articles = Article.search(params[:query], misspellings: {edit_distance: 1})
else
#articles = Article.all
end
if #articles.blank?
return redirect_to request_path
end
get_query
end
private
def get_query
#userquery = params[:query]
end
new.html.erb (view for "no results found" page. Uses a different controller than articles page):
<body class="contactrequest">
<div class="authform" style="margin-top:15px">
<%= simple_form_for #request, :url => request_path do |f| %>
<h3 style = "text-align:center; color:red;">No results found. <%= #userquery %></h3>
<h1 style = "text-align:center; color:black;">Request a Page</h1>
<h5 style = "text-align:center; color:black; margin-bottom: 25px;">Our experts will gather the information,<br> and have your page us ASAP!</h5>
<%= f.error_notification %>
<div class="form-group">
<%= f.text_field :email, placeholder: 'Email', :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.text_field :subject, placeholder: 'Item / Page Requested', class: 'form-control' %>
</div>
<div class="form-group">
<%= f.text_area :content, placeholder: 'Any additional details...', class: 'form-control', :rows => '5' %>
</div>
<%= f.submit 'Request it', :class => 'btn btn-lg btn-danger btn-block' %>
<% end %>
As you can see I've tried calling that #userquery method to display on the view page, but it doesn't show up since it's defined on a different controller. Any recommendations would be awesome.
I would suggest you render results and no results from the same controller/view. A nice clean way to do this is with partials. Your view could look something like:
<% if #articles.blank? %>
<%= render 'no_results' %>
<% else %>
<%= render 'results' %>
<% end %>
Then, you would simply create _no_results.html.erb and populate it with your current new.html.erb contents and do the same for your nominal results page (with partial _results.html.erb).
I'm working on a Rails 4 web application, using Devise and CanCanCan.
When a user first signs up to create an account, they are redirected to a subscription page where they enter in credit card details, etc.
User Creation -> Subscription Creation = Form works
This is working absolutely perfectly and once they enter in their subscription information, a permission change is made on their account and they can then access a new part of the site. All good.
Now the issue I am having is, if a user signs up, then tries to access this new part of the site without subscribing, I redirect them to the subscription page. However when I do this, the form just doesn't work. They hit submit and nothing happens.
User Creation -> Tries to access resource, gets redirected to
Subscription Creation = Form doesn't work
Here is the code I am using to perform the redirection:
application_controller.rb
def access_denied(exception)
redirect_to(new_subscription_path, alert: exception.message + " Please subscribe.")
end
The strange thing is that I am using the exact same code to redirect when they first create a user account. This is shown here:
registrations_controller.rb
def after_sign_up_path_for(resource)
new_subscription_path
end
Here is the code for the subscription controller:
class SubscriptionsController < ApplicationController
before_filter :authenticate_user!
def new
#subscription = Subscription.new
end
def create
#subscription = Subscription.new(subscription_params)
#user = current_user
#subscription.user_id = current_user.id
if #subscription.save_with_payment
redirect_to success_path, :notice => "Thank you for subscribing!"
if current_user.role = "guest"
User.update(current_user.id, role: "read")
end
UserMailer.subscription_welcome_email(#user).deliver
else
render :new
end
end
def show
#subscription = Subscription.find(params[:id])
end
def destroy
#subscription = Subscription.find_by(user_id: current_user.id)
User.update(current_user.id, role: "guest")
unless #subscription.stripe_customer_id.nil?
customer = Stripe::Customer.retrieve(#subscription.stripe_customer_id)
customer.subscriptions.retrieve(customer.subscriptions.first.id).delete
end
#user = current_user
UserMailer.subscription_destroy_email(#user).deliver
#subscription.destroy
rescue Stripe::StripeError => e
logger.error "Stripe Error: " + e.message
errors.add :base, "Unable to cancel your subscription. #{e.message}."
false
end
def subscription_params
params.require(:subscription).permit(:stripe_card_token, :last_4_digits, :plan, :expiry_month, :expiry_year)
end
end
Form code:
<div class='panel panel-default'>
<div class='panel-heading'>
<h2>Subscribe</h2>
</div>
<div class='panel-body'>
<%= semantic_form_for #subscription, :html => {:class => 'main-form'} do |f| %>
<font color=red><b><%= f.semantic_errors *f.object.errors.keys %></b></font>
<%= f.hidden_field :stripe_card_token %>
<div id='stripe_error' class="alert alert-info" style='display:none'>
</div>
<span class="help-block">Nothing is billed to your card for 7 days. <b>Guaranteed. </b>
<br>If you choose to continue after 7 days, only then will you be billed.</span>
<div class='form-group'>
<%= label_tag :card_number, "Credit Card Number" %><%= image_tag "welcome/checkout/amex.png", alt: "American Express", class: "credit-card-image" %><%= image_tag "welcome/checkout/mastercard.png", alt: "Mastercard", class: "credit-card-image" %><%= image_tag "welcome/checkout/visa.png", alt: "Visa", class: "credit-card-image" %>
<%= text_field_tag :card_number, nil, name: nil, class: 'form-control input-box', :placeholder => 'Credit Card Number' %>
</div>
<div class='row'>
<div class="col-xs-6">
<%= label_tag :card_code, "Security Code on Card (CVC)" %><%= image_tag "welcome/checkout/credit.png", alt: "Credit Card Image", class: "credit-card-image" %>
<%= text_field_tag :card_code, nil, name: nil, class: 'form-control input-box', :placeholder => 'Security Code on Card (CVC)' %>
</div>
<div class="col-xs-6">
<%= label_tag :card_month, "Card Expiration" %><br>
<%= select_month nil, {add_month_numbers: true}, {name: nil, id: "card_month", class: 'expiration'} %>
<%= select_year nil, {start_year: Date.today.year+1, end_year: Date.today.year+15}, {name: nil, id: "card_year", class: 'expiration'} %>
</div>
</div>
</div>
<div id="stripe_error">
<noscript>JavaScript is not enabled and is required for this form. First enable it in your web browser settings.</noscript>
</div>
<div>
<%= f.submit "Subscribe", class: 'btn standard-button' %>
</div>
<% end %>
</div>
</div>
Can anyone assist? Let me know if any extra code is required. Thanks
EDIT: Just to make things weird, this error is only happening when I hit a link in my navigation that is new_subscription_path .. the access denied works fine if I type in manually the URL of the permissioned resource. Could there be something wrong with the path I am using in the header? Do I need to pass something specific into it? I've also tried to check if it was JavaScript by adding in a console log, but nothing comes through, so I don't think this is an issue, despite it feeling like a JS issue.
You should check you javascript. Clearly it blocks form from submission. I've looked at your previous question here and it looks like your handleStripeResponse handler always goes into else condition block not submitting form as the result. My bet is that $('meta[name="stripe-key"]').attr('content') is undefined after your redirection.
Disabled Turbolinks solved this problem