I have a form used for creating a team (the model is Team).
The form adds members to the team (via the model TeamMember).
Each team works for a company. All users belong to a company.
I currently have a validation on the TeamMember model which prevents a user from becoming a team member when the user works for a different company than the team works for.
Recently, I've been asked to modify this functionality, since there are occasions when a team needs to add a member who works for a different company.
My simple validation has now become complicated.
I now need to provide a warning when a proposed team member works for a different company, and give the user a chance to confirm (yes/no) whether this is ok before saving the change.
What is a clean, rails-friendly way to accomplish this? I imagine there is an Ajax solution (maybe a lightbox), but I'm not sure the best way to implement it or whether someone already has a clever Gem/Plugin to handle this situation.
I would use a custom validation for this, as you are now, and add a modifiable attribute to the model that doesn't save to the db. For example:
class TeamMember < ActiveRecord::Base
attr_accessor :user_confirmed
after_intialize :setup
def setup
self.user_confirmed = false
end
end
What you need to do is add a check in your validation method if self.user_confirmed is false. If it is, in addition to the db relation you described above, make it invalid. Then on your view, add an if statement:
<% if #team_member.errors[:key_you_use_to_add_to_base].first == "Error Message" %>
<%= f.label :user_confirmed, "User works for another company, are you sure?" %>
<%= f.check_box :user_confirmed, {}, "true", "false" %>
<% end %>
Keep in mind since check_box method uses strings, youll need to convert the result of that attribute in your params hash to a boolean.
I think that should do it. Note that you may need to adjust the if statement if any other validation errors might be added to the same key.
Related
I'd like to have a drop down in my Rails form where users can select an area of a city, e.g. "Marchmont", "New Town", "Baberton" etc, when adding an order. I'd like that once they have made a selection, this will then be the default selection for the following times they use the form to add an order (so that they don't have to keep selecting it) but also that they can change their selection at any time. Hope that makes sense. I'm using the Simple Form gem. Thanks in advance! :)
#Steve
I will make a couple of assumptions.
1.) you know how to create forms within the rails templating engine.
2.) you understand how to create a dropdown menu using the Simple Form gem
So you have a couple of options based on what you actually want to accomplish. Based on what you are briefly describing, it sounds like you have some kind of an e-commerce/checkout situation that you want auto-completion to make it easier for a user.
there are a couple of approaches to storing this data.
Saving the user Data.
1.) Save it right on the user model under district_of_last_order
2.) Save it right on the order model that a user has_many orders. Then you can pull the first order's city district and select that
Personally I would lean on #2 as you probably want to be able to tightly couple the order with the user and saving that information twice is redundant since you can always do something like current_user.orders.first.district or whatever,
in your ERB where you build the form you can then do something along these lines:
<%= simple_form_for(#order) do |f| %>
... other input fields
<% if current_user.orders.first %>
<%= f.input as: :select selected: current_user.orders.first.district %>
<% else %>
<%= ... your regular dropdown menu here without the default %>
<% end %>
... rest of your form
If you have the option of using gems, I have had good results with select2-rails.
Alright, so I'm very new to rails, so please forgive me if some of these questions seem trivial.
I'm working on a group project in which users view/create the projects that exist, and the projects can display a list of associated users within it. Someone had originally set it up as one-to-many relationship and we had to make it a habtm association.
What I'm asking is how do I make this if statement work that was originally:
<strong><%= project_model.user.name if project_model.user %></strong><br />
</div>
<% if project_model.user == current_user %><br />
Now project_model should have a database/list within project_model.users (a .build was used when creating the project_model so it should at least contain the current_user. Hopefully....)
Something to note: #project_models is defined in the controller, but this is in a 'do' where #project_models.each do |project_models|
Side note: Some of the things one of my group members did is a little outside the scope of the class and so I'm a bit lost on what some of his code is actually doing. If someone could explain what the project_model.user.name if project_model.user contained within the <strong> is actually doing, it would also be really helpful.
Are you asking how to find out if project_model is associated with current_user? If so, try this:
project_model.users.exists?(current_user.id)
Also, this statement:
project_model.user.name if project_model.user
is trying to see if project_model has an existing associated user before attempting to access its name property. If the property_model does not have a user, then property_model.user will return nil. The if statement will treat this as a false value and therefore will not execute the code before it. This avoids a
NoMethodError: undefined method `name' for nil:NilClass
error if project_model.user doesn't exist. However, since you've changed to a habtm association, one ProjectModel can have multiple User's. Perhaps you want to use a project_model.users.each call to display all of the users for the project_model instead?
The way it is written here suggests a Project used to have one user (project.user is singular). Now you have many users working on a same project which should translate to: project.users (notice users is plural) meaning you're dealing with a list of users.
The 2nd if statement checks if you're part of a project before showing you stuff only involved users can know. So you can replace:
<% if project_model.user == current_user %>
# by
<% if project_model.users.map(&:id).includes?(current_user.id) %>
Since we're dealing with a list of users now you also need to iterate through te users you want to display.
So you could replace :
<%= project_model.user.name if project_model.user %>
# by
<%= project_model.users.map(&:name).join(", ") %>
Some pseudocode from my app:
User has many Products
User has many Projects
Project and Product belong to User
Furthermore:
Project has one Video
Video belongs to Project
I have a multi-step wizard built using the Wicked gem. In step one I create and save a Project. In step two I add a Video to that Project:
= form_for #project do |f|
= f.fields_for :video_attributes do |v|
= v.file_field :file
Everything works fine, but I'd like to add a Product to the Project's User during this same step. I'm a little confused as to how accepts nested attributes works for this sort of thing.
I imagine I need to do something like this in my wicked controller:
#user = current_user
# wicked makes us use :project_id as it hijacks :id
#project = #user.projects.find(params[:project_id])
#user.products.build
But where do I stick the 'nested attributes for' call? Do I need more than one call to accepts_nested_attributes_for? Would this work?
Make Project model accept nested attributes for User
Make User model accept nested attributes for Product
= form_for #product do |f|
= f.fields_for :user_attributes do |u|
= u.fields_for :product_attributes do |p|
= p.file_field :image
I can't try the code out till tomorrow, but i will sleep better knowing i can solve this when i get to it.
You certainly can extend nested attributes through several objects by nesting the fields_for calls... But you can sometimes get into trouble if you jump back and forth between objects like it looks like you're headed towards here. I've had problems with circular save issues resulting from structures like that. For this reason, I recommend keeping the accepts_nested_attributes_for as a one-way street. So, if a user accepts_nested_attributes_for projects then a project should not also accepts_nested_attributes_for users. Given that, your form has to be built based on the root object. I don't know your project but for mine that was the user. Basically, it's more likely the user would be the central relationship. Hope this helps.
Also, I'm not sure why your fields_for calls are using <some object>_attributes. Unless you're doing something special, those should be relation names like f.fields_for :video. This way the fields_for call loops through each object of that type in the collection.
In my new Rails application (it's my first one!) Users can manage their Clients and also create Projects for each Client.
Users -----< Clients -----< Projects
In order to create a new Project for a Client, a user has to select the Client from a select box like this:
<%= f.label :client_id %>
<% options = current_user.clients.all.map { |client| [client.name, client.id] } %>
<%= f.select(:client_id, options) %>
My controller action looks like this:
client = current_user.clients.find(params[:project][:client_id])
#project = client.projects.build(params[:project])
This works quite well but I don't feel particularly good about putting the client_id into my form directly. In my Project model it even says:
attr_accessible :name, :client_id
So is this a security flaw? I don't want anyone to tamper with these foreign keys. Is there a more secure way to set foreign keys in Rails?
So Rails has a bunch of great form helpers for this sort of thing. Check this out:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-options_from_collection_for_select
This method will generate what you are looking for. To answer your question, no one can really tamper with these ids.
If you are comfortable allowing a user to select a Client for the Project, then the worst thing that can really happen is that you have an extra Project with a Client who isn't associated with that project.
Here's how I'd do what you were going for:
<%= f.select :client_id, options_from_collection_for_select(current_user.clients.all, 'id', 'name') %>
The methods takes the colleciton first (In your case its current_user.clients.all), then the attribute for the value of the option (in your case the id), then it takes the label attribute (in your case the name of the client)
You are correct that you need attr_accesible :client_id but like I said, this really isn't a security concern.
I would like to know which way is the best to resolve my question :
I have a form in order to select people via a select field. If the name is missing in the select field, a text field is available to add directly the person's name.
- The form in new.html.erb is the format of the new action of the Team controller.
- The list of the people is extracted from the People model.
def new
#team = Team.new
#people = People.all
end
I created an attribute in the Team model to store the new_person text field :
class Team < ActiveRecord::Base
attr_accessor :new_person
...
end
Finally, here's an extract of my view :
<%= f.select :person_id, #people.map { |p| [p.name, p.id] } %>
<%= f.text_field :new_person %>
Obviously, I would like to save the new person in the table Person before saving the data from the form. As usual, the id are saved instead of the names
At this point, I've got two issues :
1/ The params array has the key new_person what doesn't have the table. So it is not possible to use the Team.new(params[:team]) method. Does exist an easy solution to avoid this problem ?
2/ As I need the person_id, how can I get it when the name comes from the new_person field? In using the before_filter method ?
Thanks a lot,
Camille.
1) You should consider using fields_for in your view within your form_for block. This will allow you to specify that the fields within the fields_for block are attributes of a different model, will generate the appropriately named input fields, and allow you to use params[:team] in your controller. See the FormHelper documentation for more on this.
2) While you could do something in your controller to first check for a value in the new_person field, create the record, update the contents of params[:team] with the value of the newly created person and create the team, this feels a bit like a hack to me. Another possible solution which may be less fragile would be to use some JavaScript on the page that would render some kind of modal dialog for the user to create the new person, submit the new person to the person#create controller method, then refresh your drop down. It would probably not be terribly difficult to do this using a jQuery UI modal form (very good example at that link to do what you need) with Rails remote form and unobtrusive JavaScript.
This is probably a more difficult solution to your second question than you are hoping for, but probably more useful in the long run.