ruby on rails forces show after submit - ruby-on-rails-4

I'm editing a record and receive a constraint related error after hitting "Save". Then I correct the form field involved and click "Save". Ruby on Rails forces me to the show action whilst I'd rather stay on the edit form. Is this by design or I'm (hopefully) missing something here ?
controller
def update
#profile_field = safe_find ProfileField, params[:id], "profile field"
if #profile_field.update_attributes(input_params)
logger.tagged("#{self.class} #{__method__}") {logger.info #profile_field.inspect }
flash[:success] = "profile field updated"
redirect_to(request.referrer || root_path)
else
render 'edit'
end
end
view
= form_for #profile_field do |f|
= render 'shared/error_messages', object: f.object
.field
= f.label :name_en
= f.text_field :name_en
= f.label :name_et
= f.text_field :name_et
= f.label :name_ru
= f.text_field :name_ru
= f.label :field_type
= f.select :field_type, options_for_select(ProfileField.field_types,
:selected => f.object.field_type), :default => f.object.field_type
= button_tag(type: 'submit', class: 'btn btn-primary btn-block') do
%span.glyphicon.glyphicon-floppy-disk
Save
= link_to profile_fields_path, class: "btn btn-default btn-block" do
%span.glyphicon.glyphicon-arrow-left
Back

Related

Rails Multiple Uploads with Shrine and nested attachments

I got an issue with uploading multiple files to AWS. I have two models Lessons && Attachments. Lessons has_many :attachments and I'm setting up a form that has lesson fields and I want to upload multiple attachments to that lesson. Everything uploads correctly except when I upload multiple files, it creates a new lesson and attachment. I'm working off:
https://gorails.com/episodes/multiple-file-uploads-with-shrine?autoplay=1
And
https://u.osu.edu/hasnan.1/2014/03/30/rails-4-multiple-file-upload-with-carrierwave-nested-form-and-jquery-file-upload/
Models
Lessons Model
class Lesson < ApplicationRecord
belongs_to :user
has_many :attachments, dependent: :destroy
accepts_nested_attributes_for :attachments
end
Attachment Model
class Attachment < ApplicationRecord
belongs_to :lesson, optional: true
include AttachmentUploader::Attachment.new(:media)
end
Controllers
Lessons controller
Class LessonsController < ApplicationController
# truncated for brevity.
def new
#lesson = current_user.lessons.build
end
def create
#lesson = current_user.lessons.build(lesson_params)
respond_to do |format|
if #lesson.save
if params[:media]
params[:media].each { |media|
#lesson.attachments.create(media_data: media)
}
end
format.html { redirect_to #lesson, notice: 'Lesson was successfully created.' }
format.json { render :show, status: :created, location: #lesson }
else
puts "\n\n\n#{#lesson.errors.full_messages.to_sentence}\n\n\n"
format.html { render :new, notice: #lesson.errors }
end
end
end
private
def set_lesson
#lesson = Lesson.find(params[:id])
end
def lesson_params
params.require(:lesson).permit(
:title,
:content,
:document,
:category_id,
:pinned,
:bootsy_image_gallery_id,
attachments_attributes: {media: []},
)
end
end
Attachment Controller
class AttachmentsController < ApplicationController
before_action :set_attachment, only: [:edit, :update, :destroy]
def create
#attachment = Attachment.new(attachment_params)
#attachment.save
end
private
def set_attachment
#attachment = Attachment.find(params[:id])
end
def attachment_params
params.fetch(:attachment, {})
end
end
ERB
_form.html.erb
<%= #lesson.errors.full_messages.first if #lesson.errors.any? %>
<%= form_for #lesson do |f| %>
<div class="control-group">
<%= f.label :title %>
<div class="controls">
<%= f.text_field :title, required: true %>
</div>
<div>
<%= f.label :content %>
<%= f.bootsy_area :content, editor_options: { html: false }, rows: "20", cols: "100" %>
</div>
<div>
<%= f.label 'File under at least one class' %>
<%= f.collection_select :category_id, Subject.all, :id, :name, { promt: "Choose a Class" } %>
</div>
<div>
<%= f.label :pinned %>
<%= f.label :pinned, "Yes", value: "Yes" %>
<%= f.radio_button :pinned, true%>
<%= f.label :pinned, "No", value: "No" %>
<%= f.radio_button :pinned, false, checked: true %>
</div>
<hr>
<div>
<%= f.label 'Or Upoad a file' %>
<%
######################
# This is where I have the attachment file field.
######################
%>
<%= file_field_tag "media[]", type: :file, multiple: true %>
</div>
<br>
<%= f.submit nil %>
<%= link_to 'Cancel', lessons_path%>
<div class="form-actions btn-a">
<%= link_to 'Cancel', lessons_path, class: "btn btn-default" %>
</div>
Routes
Rails.application.routes.draw do
mount AttachmentUploader::UploadEndpoint => "/attachments/upload"
resources :lessons do
member do
put "like", to: "lessons#upvote"
put "dislike", to: "lessons#downvote"
end
resources :comments
resources :attachments
end
root 'static#index'
end
JS
$(document).on("turbolinks:load", function () {
$("[type=file]").fileupload({
add: function (e, data) {
data.progressBar = $('<div class="progress" style="width: 300px"><div class="progress-bar"></div></dov>').insertAfter("#file-upload");
var options = {
extension: data.files[0].name.match(/(\.\w+)?$/)[0],
_: Date.now() // revent caching
}
$.getJSON("/attachments/upload/cache/presign", options, function (result) {
data.formData = result['fields'];
data.url = result['url'];
data.paramName = "file";
data.submit();
});
},
progress: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
var percentage = progress.toString() + '%';
data.progressBar.find(".progress-bar").css("width", percentage).html(percentage);
},
done: function (e, data) {
console.log("done", data);
data.progressBar.remove();
var document = {
id: data.formData.key.match(/cache\/(.+)/)[1],
storage: 'cache',
metadata: {
size: data.files[0].size,
filename: data.files[0].name.match(/[^\/\\]+$/)[0],
mime_type: data.files[0].type
}
}
form = $(this).closest("form");
form_data = new FormData(form[0]);
form_data.append($(this).attr("name"), JSON.stringify(document))
$.ajax(form.attr("action"), {
contentType: false,
processData: false,
data: form_data,
method: form.attr("method"),
dataType: "json",
success: function(response) {
var $img = $("<img/>", { src: response.image_url, width: 400 });
var $div = $("<div/>").append($img);
$("#photos").append($div);
}
});
}
});
});
The reason this is happening is because the Javascript looks for the URL of the file field's parent form. You have the form for creating a new Lesson, which means every time you upload a file, it will create a new lesson.
Some options to consider are:
Place the upload form in a place that's only available after the Lesson has been created. This lets you create a form_for [#lesson, Attachment.new] which will point to the right URL and let you upload files really quickly, but the Lesson must be created first.
You could adjust the JS not to submit an AJAX request right away and instead attach hidden fields with the image data.
My screencast covered this method because you had to create the Album first before uploading on the Album's show page. I think this ends up being a better user experience so they aren't worrying about file uploads failing and causing their other form data to possibly get lost or not saved.

ActiveModel::ForbiddenAttributesError use only params

I'm getting ActiveModel::ForbiddenAttributesError in MicropostsController#create on line #2 in the create action.
Tried also changing f.hidden_field to hidden_field_tag but still getting ForbiddenAttributesError
micropost_controller
def create
tag = Tag.find(params[:micropost][:tag_id])
#micropost = tag.microposts.build(params[:micropost])
#micropost.user_id = current_user.id
if #micropost.save
flash[:success] = "Posted!"
redirect_to root_path
else
render 'static_pages/home'
end
end
tags_controller
def details
#tag = Tag.find(params[:id])
#microposts = #tag.microposts
#micropost = #tag.microposts.build if sign_in?
end
micropost form
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: #micropost %>
<div class="field">
<%= f.text_area :content, placeholder: "Your post" %>
<%= f.hidden_field :tag_id %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
in tags.rb
has_many :microposts, dependent: :destroy
in microposts.rb
belongs_to :user
belongs_to :tag
It seems that you need to delete tag_id from your microposts parameters:
tag = Tag.find(params[:micropost].delete(:tag_id))
#micropost = tag.microposts.build(params[:micropost])
If that won't fix it, just whit-list the params (it's good idea anyway):
micropost_params = params.require(:micropost).permit(:name, :title, ...)
#micropost = tag.microposts.build(micropost_params)
this
def create
tag = Tag.find(params[:micropost][:tag_id])
#...
end
should probably be changed to this
def create
tag = Tag.find(params[:tag_id])
#...
end

Rails instance variable not passing to view with "Render"

Basic rails question..I have the following: a user has many packs and a pack belongs to a category. In the new pack form I want make a select field with the pack's categories. I have the following code :
def new_pack
#pack=Pack.new()
#user= User.find(params[:id])
#categories = Category.all
end
def create
#pack=Pack.new(pack_params)
#user= User.find(params[:user_id])
if #pack.save
#user.packs << #pack
flash[:notice]="Thank you!"
redirect_to(:action=>'attempt_activation', :id=> #pack.id)
else
render :action=> "new_pack", :id=>#user.id
end
end
And in my view:
<%= form_for(:pack, :url=>{:controller=> "packs", :action=>'create', :user_id=> #user.id}) do |f| %>
<h5>Registration takes less than 2 minutes.</h5>
<label>Title<b style="color:red;">*</b>
<%= f.text_field(:title, :placeholder=>"") %>
</label>
<label>Category<b style="color:red;">*</b>
<%= f.select(:category_id, #categories.map {|s| [s.title, s.id]}, :selected=> #categories.second) %>
...
THE PROBLEM is that if there are errors at the form and and the new_pack action has to be rendered again it throws an error:
ActionView::Template::Error (undefined method `map' for nil:NilClass):
</div>
<div class="large-4 columns">
<label>Category
<%= f.select(:category_id, #categories.map {|s| [s.title, s.id]}, :selected=> #pack.category) %>
</div>
Why is this happening? render is used to render a particular view using the instance variables available in the action but here is not loading again the #categories.
Thank you.
Add #categories = Category.all in create method:
def create
#pack = Pack.new(pack_params)
#user = User.find(params[:user_id])
if #pack.save
#user.packs << #pack
flash[:notice] = "Thank you!"
redirect_to(:action => 'attempt_activation', :id => #pack.id)
else
#categories = Category.all
render :action=> "new_pack", :id => #user.id
end
end

rails render action with anchor

i have a form with simple_form gem. if i digit the url in the browser with the anchor tag it works:
localhost:3000/contacts/#new_contact #show me the correct form in the page
this is my controller:
class ContactsController < ApplicationController
def index
#message = Contact.new()
end
def create
#message = Contact.new(msg_params)
#message.request = request
if #message.deliver
redirect_to contacts_path , flash: { success: 'Ok' }
else
render action: :index #how can i render the new_contact anchor??
end
rescue ScriptError
redirect_to contacts_path , flash: { error: 'spam' }
end
private
def msg_params
params.require(:contact).permit(:name,:email,:message,:spam)
end
end
this is my form:
<%= simple_form_for #message, defaults: { label: false} do |f| %>
<%= f.input :name %>
<%= f.input :email %>
<%= f.input :message, :as => :text,input_html: { rows: 7 } %>
<div class="hidden">
<%= f.input :spam, :hint => 'Leave this field blank!' %>
</div>
<%= f.button :submit, 'Send', class: 'btn-block btn-success btn-lg' %>
<% end %>
how can i show the new_contact anchor with the render method?
You can't use render to modify the anchor tag. In fact, render has nothing at all to do with the URI.
You can use existing links to link to "/url#anchor", redirect there, or use javascript to achieve the same effect.
You might also need to add :id => 'anchor' if the anchor itself isn't setup on the form.

Carmen Rails- Devise - Rails 4: issue with country_code

I'm new and I have a basic question
When I create a new user with devise using carmen-rails with Rails 4, country_code and states_code don't pass
Both country_code and state_code are permitted
Here are my codes:
in Devise registration:
= f.label :country_code
= f.country_select :country_code, {priority: %w(US CA), prompt: "Please select your country"}, :style => "width:170px;float:left;font-size:14px;", :type => 'text'
= f.label :state_code
= render partial: '/users/subregion_select', locals: {parent_region: f.object.country_code}
in users/ subregionselect:
<% parent_region ||= params[:parent_region] %> <% unless parent_region.nil? %>
<% country = Carmen::Country.coded(parent_region) %>
<% end %>
<% if country.nil? %>
Please select a country above
<% elsif country.subregions? %>
<%= f.subregion_select(:user, :state_code, parent_region) %>
<% else %>
<%= text_field(:user, :state_code) %>
<% end %>
in users.js.coffee
$('select#user_country_code').change (event) ->
select_wrapper = $('#user_state_code_wrapper')
$('select', select_wrapper).attr('disabled', true)
country_code = $(this).val()
url = "/users/subregion_options?parent_region=#{country_code}"
select_wrapper.load(url)
in routes.rb
get '/users/subregion_options' => 'users#subregion_options'
in users controllers:
def subregion_options
render partial: 'subregion_select'
end
On my console, all the data are saved except for country_code: nil (even though I put United States) and the states_code is not generated when I select a country.
Is that because I should need to use Formstatic?
Thank you for your help :)
Looks like an issue with Devise params sanitation.
#application_controller
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u|
u.permit(:email,
:password,
:password_confirmation,
:first_name,
:last_name,
:street1,
:street2,
:city,
:state,
:zipcode,
:country_code)
}
Give that a try!
Cheers