Rails 4 form_tag submitting to wrong controller action - forms

This feels dumb, but so far everything I've read makes me feel like I'm doing it right but it's still not working.
I'm using a form_tag to submit params to a custom controller action. Instead of the action I indicate, it seems intents on submitting to the show action in my controller, which I need to reserve for profiles. Anyways here's the code (please excuse it's un-refactored state):
Doctors controller:
class DoctorsController < ApplicationController
def new
end
def show
#doctor_list = Doctor.find(params[:id])
end
def index
#doctor_list = Doctor.all
end
def search
end
def results
if params[:zip] && params[:zip].length === 5 && params[:zip]
#doctor_list = Doctor.where("zip = ?", params[:zip])
elsif params[:id]
begin
#doctor_list = []
#doctor_list<<Doctor.find(params[:id])
rescue
flash.now[:errors] = "That doctor does not exist!"
render 'search'
end
else
flash.now[:errors] = "That is not a valid zipcode!"
render 'search'
end
end
end
Routes:
resources :users
resources :doctors
root 'doctors#search'
get 'doctors/results' => 'doctors#results'
search.html.erb:
<% provide(:title, "Home") %>
<div class="hero">
<h1>Find an M.D.</h1>
<%= form_tag(doctors_results_path, method: "get") do %>
<%= label_tag("Zipcode: ") %>
<%= text_field_tag(:zip) %><br>
<%= submit_tag "FIND", class: "button"%>
<% end %>
</div>
Again, the issue is that I'm getting an error (Couldn't find Doctor with 'id'=results) because the form is using my show action vs. my results action. The application trace says the error is located at app/controllers/doctors_controller.rb:6:in 'show'. As an added confusion, I don't really understand why it's sending "id"=>"results" as part of the params hash on submit, but it seems like that might be a non-issue if it would use the correct controller action to begin with.
Thanks for any thoughts.

Yes its a priority issue. Since the resources :doctors is first, the GET show will be given the priority than get 'doctors/results' => 'doctors#results'
Moving the get 'doctors/results' => 'doctors#results' above resources :doctors should solve your problem
#routes.rb
root 'doctors#search'
get 'doctors/results' => 'doctors#results'
resources :users
resources :doctors

Related

How can we require a parameter in an ejs template such that ejs.render fails when the parameter is undefined?

I need to set the parameter so that if the parameter passed through to the template is undefined, the template rendering fails with an error.
Example template:
let template = "Hello <%=info.name%>, this is a test template.";
And when calling render, I send the following:
let data = {
info: {
name: "Bob"
}
};
let rendered_template = ejs.render(template, data);
This gives the following value for rendered_template:
Hello Bob, this is a test template.
However, if info.name is undefined, then the template still gets rendered (it will only fail if info itself is undefined, because it won't be able to read property name of undefined), and the result is the following:
Hello , this is a test template.
How can I set the info.name parameter to be mandatory, so that ejs.render fails if the value of info.name is undefined?
You could use the include function in conjunction with an if/else block:
<% if (info.name != undefined) { %>
<%- include 'someTemplate' %>
<% } %>
<% else { %>
<%- include 'someOtherTemplate' %>
<% } %>
Where someOtherTemplate is the error page you want to render when info.name is undefined.
Update
To send an error message with node.js, you can do something like this:
app.get('/someRoute', function(req, res) {
if (info.name) {
res.render('someTemplate', {info:info});
}
else {
res.status(403);
}
});

Shrine with Rails multiple polymorphic image uploads

I've been struggling for about 5 hours trying to understand why Shrine is blocking my uploads. I either get errors like "Shrine: Invalid file", or "Expected Array but got string" in strong params. If there aren't errors, the images aren't actually saved.
require "image_processing/mini_magick"
class ImageUploader < Shrine
include ImageProcessing::MiniMagick
plugin :activerecord
plugin :backgrounding
plugin :cached_attachment_data
plugin :determine_mime_type
plugin :delete_raw
plugin :direct_upload
plugin :logging, logger: Rails.logger
plugin :processing
plugin :remove_attachment
plugin :store_dimensions
plugin :validation_helpers
plugin :versions
Attacher.validate do
validate_max_size 2.megabytes, message: 'is too large (max is 2 MB)'
validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
end
def process(io, context)
case context[:phase]
when :store
thumb = resize_to_limit!(io.download, 200, 200)
{ original: io, thumb: thumb }
end
end
end
class Image < ActiveRecord::Base
include ImageUploader[:image]
belongs_to :imageable, polymorphic: true
end
class Product < ApplicationRecord
has_many :images, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :images, allow_destroy: true
...
# Strong Params:
def product_params
params.require(:product).permit(
:name, :brand_id, :category_id, :price, :compare_price, :description,
images_attributes: { image: [] },
product_properties_attributes: [:id, :property_id, :value]
)
...
And my view:
<%= f.fields_for :images do |image_form| %>
<%= image_form.file_field :image, multiple: true %>
<% end %>
According to everything I've read on the docs or from gorails, this should work. Do I need to restructure the images_attributes hash? I also tried using direct_uploads, but struggled to get the presigned_url to work with S3.
Refile makes this really easy, so I'll probably run crying back to that.
Is there something I'm obviously doing wrong?
According to the fields_for documentation, the provided block will be called for each image in the project.images collection. So if your product currently doesn't have any images, the block won't be called (according to the docs).
For nested attributes to work, you need to forward the following parameters when creating the Product:
product[images_attributes][0][image] = <file object or data hash>
product[images_attributes][1][image] = <file object or data hash>
product[images_attributes][2][image] = <file object or data hash>
...
If you look at the "Multiple Files" Shrine guide, it's recommended that you just have a single file field which accepts multiple files:
<input type="file" name="file" multiple>
And then setup direct uploads for this field using Uppy, dynamically generating the image field for each uploaded file populated with the uploaded file data hash:
<input type="hidden" name="product[images_attributes][0][image]" value='{"id":"...","storage":"cache","metadata":{...}}'>
<input type="hidden" name="product[images_attributes][1][image]" value='{"id":"...","storage":"cache","metadata":{...}}'>
....
Alternatively you can just let users attach multiple files, which are all submitted to the app, and then destructure them in the controller:
class ProductsController < ApplicationController
def create
images_attributes = params["files"].map { |file| {image: file} }
Product.create(product_params.merge(images_attributes: images_attributes))
end
end
In that case you have to make sure your HTML form has the enctype="multipart/form-data" attribute set (otherwise only the files' filenames will get submitted, not files themselves).

Why would my Edit Form be view-able but not my New form of the same controller?

If I click on my edit link the link works and takes me to the edit form for the particular gallery I'd like to edit. If I click on the link to the new for the same controller I get the following error:
NoMethodError at /users/2/galleries/new
undefined method `galleries_path' for #<#<Class:0x007f4714fbac68>:0x000000060f32e0>
Did you mean? gallery_path
I've done extensive searching for the solution to this, but I'm not sure what I am doing wrong. This was working fine until I updated the routes to have galleries a nested resource to the user. I was in the process of updating/correcting my links and forms when I couldn't figure out how to get past this issue.
Below are my routes, the galleries/_form, galleries/edit, galleries/_new, parts of the gallery controller.
routes.rb
resources :users, shallow: true do
resources :galleries
resources :images
resources :albums
end
galleries.html.erb
<h1>New Gallery</h1>
<%= render 'form' %>
<%= link_to 'Dashboard', user_path(current_user) %>
galleries/edit.html.erb
<h1>Update Gallery</h1>
<%= render 'form' %>
<%= link_to 'Dashboard', user_path(current_user) %>
galleries_controller.rb
def new
#gallery = current_user.galleries.new
end
def create
#gallery = current_user.galleries.build(gallery_params)
respond_to do |format|
if #gallery.save
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render :show, status: :created, location: #gallery }
else
format.html { render :new }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
def show
end
def update
if #gallery.update_attributes(gallery_params)
flash[:success] = "Gallery Updated"
redirect_to #gallery
else
render 'edit'
end
end
private
def set_gallery
#gallery = Gallery.find(params[:id])
end
def gallery_params
params.require(:gallery).permit(:title, images_files: [])
end
If you need more from me let me know. Thanks in advance.
I ended up not using 1 form for edit and new. I made two separate forms, violating DRY principles, but getting my app to work so I could move on.

Symfony2 forms - No Form Builder

new to Symfony and trying to understand something. I have index.twig.html and in it, I have a form
<form action="{{ path('SpecialAlertBundle_add') }}" method="post" enctype="multipart/form-data" class="addAlertForm">
<textarea class="addMargin" id="na_command" name="na_command" rows="3" cols="50" placeholder="A20APRLONLAX"></textarea>
<button type="button" class="btn btn-default" id="submit_alert" name="submit_alert">Submit</button>
{{ name }}
</form>
I wont add all the html, but its a normal form, not using Form Builder.
I have a route set up
SpecialAlertBundle_add:
pattern: /
defaults: { _controller: SpecialAlertBundle:Alert:add }
requirements:
_method: GET|POST
So that route displays my form ok when I go to localhost:8000. It also states which controller to use. As for the controller, I have
class AlertController extends Controller
{
public function addAction()
{
$request = $this->get('request_stack')->getCurrentRequest();
if ($request->request->has('submit_alert')) {
$name = $request->request->get('na_command');
} else {
$name = 'Not submitted yet';
}
return $this->render('SpecialAlertBundle:Page:index.html.twig', array(
'name' => $name
));
}
}
The first thing I want to clear up is that return in the controller. Is this the view I want it to render AFTER the form has been submitted?
Second thing is, at the moment, The {{name}} in the template is always displaying Not submitted yet. Even when I submit the form with data, nothing seems to happen. It seems that the button is doing nothing. Even when I look in the debug console, I see no request being made.
So I was hoping someone could advise me on what I am doing wrong here?
Thanks
First of all why don't you use Request directly in controller instead of request_stack? Request stack is mostly for injecting it to service (and not to inject request to the service).
So, you can do something like this:
public function addAction(Request $request)
{}
Then I'd suggest you to separate get request and post request. Just define two different routes.
For example:
SpecialAlertBundle_add:
pattern: /
defaults: { _controller: SpecialAlertBundle:Alert:add }
requirements:
_method: GET
SpecialAlertBundle_create:
pattern: /
defaults: { _controller: SpecialAlertBundle:Alert:create }
requirements:
_method: POST
After this you will have to change your form action value: set it to 'SpecialAlertBundle_create'
And it will be cleaner which one is now. After that you just don't need the checking on existence of 'submit_alert' property in request. You can assign the value of 'na_command' field to the $name:
$name = $request->get('na_command');

Coffee-Reactify Compilation Issue

I'm really hoping someone can help me with this issue because I've searched high and low to no avail and tried everything I can think of. I'm new to ReactJS and Browserify (though I don't think this has anything to do with Browserify) and can't seem to get this code working. I've been following along with the video series "Getting Started With React.js," and section 5.2 introduces Browserify and setting it up properly to work with React. Using plain old JavaScript, I'm able to get it working no problem, but when I try to use Coffee-Reactify and CoffeeScript, everything compiles fine, but when I load the page, I get this error:
"Uncaught TypeError: Cannot read property 'firstChild' of undefined"
When I follow the stack trace, it seems to error out in the findComponentRoot method of React, which I haven't touched. This leads me to believe there's something wrong with my CoffeeScript, but I've compared it line for line with the JavaScript, and aside from the additional "return" statements the CoffeeScript compiler adds, nothing seems too different. If anyone out there can replicate or identify my issue, I'd greatly appreciate it! Here is the code for all of my files, and thank you all very much in advance!
index.jade
doctype html
html
head
meta(charset='utf-8')
title React Tools
link(rel='stylesheet', href='bower_components/bootstrap/dist/css/bootstrap.css')
body
#app
script(src='bower_components/lodash/dist/lodash.js')
script(src='bower_components/react/react.js')
script(src='build/app.js')
app.coffee
MessageBox = require('./MessageBox.cjsx')
reactComponent = React.render(
<MessageBox />,
document.getElementById('app')
)
SubMessage.cjsx
SubMessage = React.createClass
handleDelete: (e) ->
#props.onDelete(#props.message)
propTypes:
message: React.PropTypes.string.isRequired
getDefaultProps: ->
message: "It's good to see you"
render: ->
<div>
{#props.message}
<button onClick={#handleDelete} className='btn btn-danger'>x</button>
</div>
module.exports = SubMessage
MessageBox.cjsx
React = require 'react'
SubMessage = require './SubMessage.cjsx'
MessageBox = React.createClass
deleteMessage: (message) ->
newMessages = _.without(#state.messages, message)
#setState
messages: newMessages
handleAdd: (e) ->
newMessage = #refs.newMessage.getDOMNode().value
newMessages = #state.messages.concat [newMessage]
#setState
messages: newMessages
getInitialState: ->
isVisible: true,
messages: [
'I like the world',
'Coffee flavored ice cream is underrated',
'My spoon is too big',
'Tuesday is coming.',
'I am a banana'
]
render: ->
inlineStyles =
display: if #state.isVisible then 'block' else 'none'
messages = #state.messages.map ((message) ->
<SubMessage message={message} onDelete={#deleteMessage} />
).bind(#)
return (
<div className='container jumbotron' style={inlineStyles}>
<h2>Hello, World</h2>
<input ref='newMessage' type='text' />
<button className='btn btn-primary' onClick={#handleAdd}>Add</button>
{ messages }
</div>
)
module.exports = MessageBox
As a side note, React is partially "working" because the messages array gets mapped to a SubMessage array and displays properly with the delete buttons. So the error seems to be happening at a later point in the cycle. Thanks again!
Wow, what an oversight on my part. It turns out I was loading React from two different places, once as a node module in my coffee/cjsx files and again in my HTML, but from a bower-installed version of React. I can't believe I spent so much time on this, but hopefully my struggle helps somebody else!