Rails Active Admin - Edit/create association on show page? - ruby-on-rails

I have a show page for an author that lists the authors books. I'd like to be able to add a book name in line to quickly add a new book association. I could do it pretty easily with rails MVC on the front end, but I am having a hard time undertsanding the activeadmin DSL to do things that are not quite out of the box.
Here's the current table of data on the show page, straight-forward.
show :title => :name do
panel "Books" do
table_for(author.books) do
column("Book") { |book| link_to book.name.titleize, admin_book_path(book) }
column("Release") { |book| book.release_date.to_formatted_s(:long_ordinal)}
column("Sales") { |book| book.orders.count }
end
end
end
What I would like to see is at the bottom of the list a blank textfield that I can input a new book title and click ADD to add it to the list. But I only see info in the docs on how to edit the form itself, which gets shown only on the edit page.
Overall I love how quickly activeadmin sets up, but it feels like I'm missing some basic concepts of how it functions, and the docs aren't helping me grasp it...

You could for example render a from as a partial in the Show page and add any needed controller actions to add books and redirect back to Show page.
row("Add book") do |item|
div do
render "form_to_add_books"
end

Just use associations in your models and use it like this
form do |f|
f.inputs do
f.input :book_name
f.inputs do
f.has_many :titles do |p|
p.input :title
end
end
end
f.has_many will automatically generates the button.
And don't forget to use
accepts_nested_attributes_for :titles
in your books model.

Related

Edit/create nested resources in Formtastic (Rails)

This question is regarding Rails 4/postgresql and the app is hosted on Heroku.
I am making a Quiz-functionality on a website and I am wondering on how to implement the forms (using Formtastic) best to make this is easy as possible. I have three models:
Quiz (has_many :quiz_questions), e.g. "Test to see how awesome you are"
QuizQuestion(belongs_to :quiz, has_many :quiz_options). e.g. "1. Which is your favorite color")
QuizOption (belongs_to :quiz_question). e.g. "Blue"
I have set up the forms like this:
<%= semantic_form_for([:admin, #quiz], :url => admin_quiz_path(#quiz.id)) do |f| %>
<%= render 'form' , :f => f %>
<% end %>
where the form looks like this:
<%= f.inputs %>
<h3>Quiz questions</h3>
<%= f.semantic_fields_for :quiz_questions do |qq_f| %>
<%= qq_f.inputs %>
<h4>Quiz options</h4>
<%= qq_f.semantic_fields_for :quiz_options do |qqo_f| %>
<%= qqo_f.inputs %>
<% end %>
<% end %>
<%= f.actions do %>
<%= f.action :submit %>
or go <%= link_to 'back', admin_quizzes_path %>
<% end %>
It seems, however, not to be working the way I want. I expect to be able to see the fields of QuizQuestion and QuizOptions in this form (there are objects for those) but I don't.
More importantly is that I would like to be able to create a New QuizQuestion and subsequently QuizOption in this form. It doesn't necessarily have to be jQuery/ajax or anything but I would like to do it all from this form.
Basically, I would like my workflow to be like:
Create a Quiz and add values to it. Click Create.
Add QuizQuestion number one and add the values to it (like "name label"). Click Create.
Add QuizOption related to QuizQuestion number one, and its "name label". Click create.
Repeat for QuizQuestion/QuizOption until the Quiz is done.
How can I do this?
For your workflow you might have to add accept_nested_attributes_for for the nested resources, this way when creating an object object you can actually create nested children (as long as they fulfill all the validations). This way:
# A quiz :has_many :quiz_questions
#quiz = Quiz.create(...)
with a declaration like:
has_many :quiz_questions
accepts_nested_attributes_for :quiz_questions
in your Quiz model you'll actually be able to create QuizQuestion from the quiz model like:
# using the previously quiz model
quiz.quiz_questions.create(...)
Doing the same for the deeply nested associations will do have the same effect.
Perhaps the reason why you don't see any field on the form is because there is not nested object created. Let me explain. When you create a new Quiz object, in your quizs_controller (or whatever the inflection for quiz is...) you need a:
def new
quiz = Quiz.new()
end
and
def create
Quiz.new(quiz_params)
end
private
def quiz_params
# whitelisted parameters sent along with the form
params.require(:quiz).permit(...)
end
if you actually want to be able to see the fields in the form you'll have to use the build method and actually populate that new object with respective the nested resources.
Note that for this to work with the form you will have to whitelist in the quizzes_controller the right attributes. You can debug the params you receive once you send the new quiz formulary and check that everything is right.
TIP! if you don't want to worry about the JS when adding nested resources dynamically, I recommend you using the cocoon gem

Rails nested_form with has_many without create additional columns in child's table

I'm using
class Mother < ActiveRecord::Base
has_many :boys
accepts_nested_attributes_for :boys, reject_if: :all_blank, allow_destroy: true
end
and
class Boy < ActiveRecord::Base
belongs_to :mother
attr_accessor :mother_id
end
because I can't modify columns of Boys table.
Now I create nested form in Rails but when I go to save Mother and Boys it says:
ActiveModel::MissingAttributeError - can't write unknown attribute `mother_id`:
And it is normal because the column mother_id doesn't exists. But I don't need to save Mother model, just the many boys in the form I use with gem cocoon and then delete immediately the mother's one that I don't need anymore.
How to bypass the inexistence of column mother_id in boy's table and just save the boys rows without references to "fake" mother?
Is there a way in Rails to create a form in which I can use a mechanism like Cocoon but with the same instance of the model and not a child one, without "accepts_nested_attributes_for" to be clear?
Have a Mother model is completely unnecessary in your case, also use cocoon is unnecessary because you do not need to save your parent model. What I would do is have a form just for create Boy's and if you want it to look "nested" form, then you can Ajaxify it for give it the appearance of nested.
Delete your Mother model, you do not need it, then add a basic set of routes for your Boy
routes.rb
resources :boys
boys_controller.rb
def create
respond_to do |format|
#boy = Boy.new boy_params
#boy.save
format.js
end
end
_form.rb
#just make sure you have remote: true and check that you are always creating a new Boy
<%= form_for Boy.new, builder: yourBuilder, remote: true do |f| %>
.....
<% end %>
Some where in your view add a div where you can show your boys and also for later append them each time you create a new one:
<div id="boys">
#Whatever collection you can get for show the current created boys I do not know, let's say current_user
<% current_user.boys.each do |boy| %>
<%= render boy %>
<% end %>
</div>
Next just add a script for your create action:
boys/create.js.erb
#If #boy is still not persisted it means it has errors, display errors
<% if #boy.new_record? %>
$('#boy_form').append("<span class=\"error\"><%= j #boy.errors.full_messages.join(', ').html_safe %></span>");
setTimeout(function() {
$('#new_group_goal .error').remove();
}, 8000);
<% else %>
#Else we prepend the #boy in the div #boys and that's it, you form is beautifully ajaxified.
$('#boys').prepend("<%= j render(#boy) %>");
setTimeout(function() {
$('# .error').remove();
}, 2000);
<% end %>
This way you have a unic form for create Boy's with the Ajax you can add boys dynamically and it will look like a nested form, just instead of have 1 submit action you perform a submit for create all the boys, you will perform 1 submit action for each boy and display it dynamically below the form, this way is more flexible in addition to that this way has better performance. Let me know if you understood my point.

More than a simple nested form

campaign.rb
class Campaign < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :item
end
item.rb
class Item < ActiveRecord::Base
belongs_to :campaign
end
Campaign has 2 attributes: title and description
Item has 1 attirubte: name
I'll try explain myself by words, I want to create a nested form where they user insert the campaign's name and description but he can insert more than just 1 item, he can insert a list of items (in particular there will be a "+" button that when clicked a new item row will appear and the user can insert items).
At the end all is send all together clicking just one submit button.
How can I reach my goal with rails?
I answered a question just yesterday. Here's the link: Rails accepts_nested_attributes_for with f.fields_for and AJAX
I'll write out how it works & provide some resources to help give you some more ideas:
How It Works
Loading associative fields into a form is done by using f.fields_for
You'll do it like this:
#app/views/campaigns/new.html.erb
<%= form_for #campaign do |f| %>
<%= f.fields_for :items do |a| %>
<%= a.text_field :information %>
<% end %>
<% end %>
In order to get this to work, you have to build the associated ActiveRecord objects in the backend before you render the view, like this:
#app/controllers/campaigns_controller.rb
def new
#campaign = Campaign.new
#campaign.items.build
end
Adding Extra Fields Via Ajax
Adding extra fields with Ajax requires engineering a new solution to the issue
The way you do this is to take the f.fields_for text & put it into a partial. This partial can be called from the original view, as well as another view (which we can render through Ajax)
The Ajax part works by basically taking a request from your form (the ajax request), and then using another action in your controller to build a new ActiveRecord object & render another partial that will contain another form. This partial will then call the original f.fields_for partial, allowing you to render another field
Your Ajax can then extract the new field & append it to your page. The way you get around the id issue (keeping the IDs sequential & unique) is to employ the child_index method, and use Time.now.to_i to generate a timestamp
If you read my answer referenced at the top of this answer, all of this will make sense :)
Some great resources for this:
RailsCasts Nested Forms
Adding Fields With Ajax
A nice gem along with tutorial is available from ryanbates who is the author of railscasts.com site.You can use this and have a look at tutorial here
And also if you want to try manually use the fields_for while writing in the form like here and manage some jquery code for add or remove.

Rails Search Form

I'm creating an application that tracks users and achievements (think, xbox live, etc.) These tables are linked via a join table. I would like to have a search form on my index that lets users type in a users name and a new page is loaded with a list of all achievements that user has earned. I'm not entirely sure how to set up this search form, on the index, to actually search the user table and return the results on a new page. Any help would be greatly appreciated. If you require more information then I'll be happy to provide it.
Here's a bit of skeleton code to get you started based off what I think you need from what you have said. I hope this is useful.
For the search bit you could do something like this in your index view:
<%= form_for User.new, :url => "search" do |f| %>
<%= f.label :name %>
<%- f.text_field :name %>
<%- end %>
In your controller:
def search
q = params[:user][:name]
#users = User.find(:all, :conditions => ["name LIKE %?%",q])
end
and in your search view:
<%-#users.each do |user| %>
Name: <%=user.name %>
<%- user.achievements.each do |achievement| %>
<%= achievement.name %>
<%- end %>
<%- end %>
You would, of course, need to ensure the users and achievement models are correctly linked:
class User << ActiveRecord::Base
has_many :achievements
end
There are plenty of tutorials and things about this e.g.:
http://blog.devinterface.com/2010/05/how-to-model-a-custom-search-form-in-rails/
Look the thing is every basic explanation in Rails3 starting with the Initial Tutorial provided by them explains you how to setup a new Controller/Model. The example was only one of thousands explaining the same problem.
It is a very broad range of different things you can do to achieve this. Basically you have to put some code in the controller:
which handles the search (including the activerecord stuff or whichever technique you use to access your model)
which sets some variables necessary for the search form
Setup two routes etc... Its to broad and completely covered even by the basic official rails3 tutorial.
Here is an application based on searchlogic is very useful and you can search by whatever you want
https://github.com/railscasts/176-searchlogic
You may want to check out the Ransack gem. https://github.com/activerecord-hackery/ransack

Searching within an associated array in ruby - rails 3

So lets say we have a Post model, a User model and a View model.
When a user views a post, a new record is created in the Views table. The table links the user, the post, and the current time. Later if that user goes back to view the post again, the record is updated with a new time. Pretty basic stuff.
Posts has_many views, and Users has_many views
Views belongs to Posts and Users
In the index view of the Posts, I want to call the specific view for each post i.e.
<% #Posts.each do |post| %>
<%= post.name %><br/>
<%= post.views %> # This connects all of the views related to this post.
# How do I get the only one connected to this post and the current_user.id?
<% end %>
I feel like theres some simple way of accomplishing this that I'm totally forgetting
You could do something like
current_user.views.where(:post_id => post.id)
# this may work, not sure
current_user.views.where(:post => post)
or
post.views.where(:user_id => current_user.id)
# this may work, not sure
post.views.where(:user => current_user)

Resources