I have a question on a form with 15 checkboxes. I would like to take all items that are "checked" and combine the labels into a string that is entered into my database. I would rather not make a separate DB entry for every checkbox and selecting individuals on this field is not important, I simply need to record the response.
Is there an easy way to combine these and make that the value entered into the DB?
The following displays right, but I'm not sure how to actually submit the correct information.
HTML
<div class="form-inline">
<div class="form-group col-sm-12" style="margin-bottom: 10px">
<div class="field">
<div class="input check_boxes optional remote_issues">
<label style="display:block; padding-bottom: 1em" class="check_boxes optional">Which of the following pevent you from reaching your desired weight?</label>
<% boxes = ["Lack of Knowledge", "Physical Limitations", "Lack of Social Support", "Hunger", "Cravings", "Frequent Travel", "Social Events", "No Time", "Erratic schedule", "Finances", "Family Habits", "Stress", "Hormonal Issues", "Medications", "Illness", "Poor Sleep", "Health Conditions", "Age", "Slow Metabolism", "Alcohol", "No Exercise", "Emotional Eating", "Food Preferences", "Other"] %>
<% boxes.each do |box| %>
<%= render "health_intake", name: box %>
<% end %>
</div>
</div>
</div>
</div>
_health_intake Partial
<span class="checkbox col-xs-3">
<label for="remote_issues_lack_of_knowledge">
<input type="checkbox" value=#{<%= name %>} name="remote[issues][]" class="check_boxes optional" style="margin-right:1em"><%= name %>
</label>
</span>
Just join the values in your controller for that parameter array:
values = params[:issues].join
Furthermore, add a comma or something to the join method so you can split them later:
values = params[:issues].join(',')
You could have an Issue model where you store "Lack of Knowledge", etc. Then a join model FooIssue (assuming Foo is the model in your form above).
FooIssue belongs_to :foo and belongs_to :issue. Foo has_many :foo_issues and has_many :issues, through: :foo_issues. Foo accepts_nested_attributes_for :foo_issues.
In your form you display all issues as checkboxes. Issues that are checked get passed back to Foo as nested foo_issues attributes.
With this approach, rather than a serialized approach - which I think is where you were going - you can get better statistics around issues. "What percentage of people say 'Frequent Travel' is an issue?" Things like that.
Related
I've checked all the SO questions I can find related to this issue.
The rest of the form works and the test passes if I remove the validation that a PlanTemplate must have a Category.
I have created a Category with a name of 'Planets' in the Test script elsewhere.
I've just got Capybara screenshot working and have now updated it to include the text - it doesn't seem to be appearing in Capybara, but is appearing in local - I can't tell from my code why it would be hidden?
Full error:
1) PlanTemplate can be created by admin admin can create new plan
Failure/Error: expect(page).to have_content('Planets')
expected to find text "Planets" in "window.addEventListener(\"load\", function(){ window.cookieconsent.initialise({ \"palette\": { \"popup\": { \"background\": \"#252e39\" }, \"button\": { \"background\": \"#428bca\" } }, \"theme\": \"classic\", \"content\": { \"href\": \"Toggle navigation Communities Communities People Mates Weekly Activity view Recent Activity list view All Mate Goals Manage Mates Plans Goals All Goals Manage My Goals New Goal Admin Admin Account Profile Email Settings Manage Mates My Interests Log out New Plan Template Plan name: Plan description: What additional information is required? (optional) What is the delivery medium & requirements of plan? (optional) What should you do once the plan is complete? (optional) How many hours per week does this goal take to complete? How many days in total does this take to complete? /7 for weeks. This will be used to calculate start times What level of user is this plan for? beginner novice intermediate experienced expert What category is this goal in? Feed My Goals My Mates All goals Home Sign Up Log in Browse Users Feedback © Browse Goals Privacy TOS Cookies window.setTimeout(function() { $(\".alert\").fadeTo(500, 0).slideUp(500, function() { $(this).remove(); }); }, 6000);"
Things I have tried to date are commented out below (have also tried others and removed them):
let(:category) { create :category, name: 'Planets', id: 99 }
scenario 'admin can create new plan' do
login_as(create(:admin))
visit("/plans/new")
find('#plan-name').set('A test plan name')
find('#plan-desc').set('A test plan description, I think these need to be longish')
find('#plan-weekly-hours').set(1)
# page.check('Planets')
# find("label[for='Planets']").click
# find_field(['plan_template_category_ids_1']).check
# find('.checkbox').check
# check('Planets')
# find(:label, 'Planets').click
# check('.checkbox', allow_label_click: true)
# find(:label, 'plan_template_category_ids_1').click
# find('#plan_template_category_ids_1', visible: false).trigger('click')
find('#plan-days-to-complete').set(35)
find('#submit-new-plan').click
expect(page).to have_content('Plan template was successfully created.')
expect(page).to have_content('A test plan description, I think these need to be longish')
end
I have also added to rails_helper:
Capybara.automatic_label_click = true
Capybara.ignore_hidden_elements = false
The form partial code in question:
<%= form.label "What category is this goal in?" %><br>
<%= form.collection_check_boxes :category_ids, Category.all, :id, :name do |cb| %>
<% cb.label(class: "checkbox-inline input_checkbox") {cb.check_box(class: "checkbox") + cb.text} %>
<% end %>
The html snapshot from Capybara screenshot - have to paste it as an image as SO doesn't like it in code.
How it displays in the DEV browser *not test:
<div class="col-md-12 goalform">
<label>What category is this goal in?</label><br>
<input type="hidden" name="plan_template[category_ids][]" value=""><label class="checkbox-inline input_checkbox" for="plan_template_category_ids_2">
<input class="checkbox" type="checkbox" value="2" name="plan_template[category_ids][]" id="plan_template_category_ids_2">Dream Chasing</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_1"><input class="checkbox" type="checkbox" value="1" name="plan_template[category_ids][]" id="plan_template_category_ids_1">Weightloss</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_4"><input class="checkbox" type="checkbox" value="4" name="plan_template[category_ids][]" id="plan_template_category_ids_4">Productivity</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_11"><input class="checkbox" type="checkbox" value="11" name="plan_template[category_ids][]" id="plan_template_category_ids_11">Popular</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_3"><input class="checkbox" type="checkbox" value="3" name="plan_template[category_ids][]" id="plan_template_category_ids_3">Fitness</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_12"><input class="checkbox" type="checkbox" value="12" name="plan_template[category_ids][]" id="plan_template_category_ids_12">Health</label>
</div>
This could've been diagnosed by using a debugging tool (e.g. using the pry gem and adding 'binding.pry' inside of the test (and before the failing expect line) which would have shown me that the Categories were not being generated correctly due to the lazy loading of the 'let'
First, nowhere in the HTML you've shown is 'Planets' mentioned, so it's not surprising that Capybara can't find the checkbox. If you're trying to select the checkbox with a value of 1 then its label contents is 'Weightloss' according to the HTML.
Second, assuming the fields you're using set on are input elements, stop doing find(...).set(...) and just use fill_in.
Those changes would make your test code
scenario 'admin can create new plan' do
login_as(create(:admin))
visit("/plans/new")
fill_in('plan-name', with: 'A test plan name')
fill_in('plan-desc', with: 'A test plan description, I think these need to be longish')
find('#plan-weekly-hours').set(1) # I don't know what type of element this is? If a select use `choose`
# check accepts the id, unique name, or associated label text as a locator
check('Weightloss')
find('#plan-days-to-complete').set(35) # same as above
click('submit-new-plan')
expect(page).to have_content('Plan template was successfully created.')
expect(page).to have_content('A test plan description, I think these need to be longish')
end
If that doesn't work for you, please copy the full error you get and add it to your question.
Additionally, never set Capybara.ignore_hidden_elements = false when writing tests. Doing so makes your tests invalid, from a users perspective, and basically pointless. The only time that setting may make sense is when web scraping.
The error I was having was because I was using let(:category) { create :category, name: 'Planets', id: 99 } & let(:category) { create :category } as a way to create Categories, but they weren't usable in the test.
I created them without using a factory inside the test and it's working fine.
Category.create(name:" Category 1")
I also thought my PlanTemplate factory would create a category that could be used as I have inside of the factory categories { [create(:category)] }, but that isn't usable the way I expected.
I am fairly new to full stack development and I am trying to get a simple search form to work. The webpage is supposed to redirect the user to a page with a list of all videos containing the keyword. Whenever I type in a title that exists, I get :NoMethodError at /posts/:title/search
I've tried using a query but have failed.
This is my app.rb. I query all videos that contain :title.
get "/posts/:title/search" do
# authenticate!
#results = Video.all(title: params[:title])
if #results
erb :search_success
else
flash[:error] = "Video not found."
erb :search_success
end
end
This is search_success.erb where I want to have a list of the videos that contains the keyword in the title.
<div class="container">
<% #results.each do |r| %>
<h1><%= r.title %></h1>
<% end %>
</div>
This is navigation.erb where the search form lives.
<form action="/posts/:title/search" method="get">
<input type="text" type="text" placeholder="Search" aria-label="Search">
<button type="submit">Search</button>
</form>
Try changing
#results = Video.all(title: params[:title])
to
#results = Video.all(:title.like => "%#{params[:title])}%")
to get answers that don't need to match completely (i.e. match case etc.)
Also in your search form, you have two type attributes. You should change one of them to
name="title"
I'm making a website using ror and I have a list of fandoms, and I want to display different pictures and writing for that fandom. Right now, just for testing, I'm having it just display the name of the fandom at the top of the page, but no matter which one I click on, it only displays the name of the first fandom. Here's the parts of the files I think are relevant, but if you need to see more, I'll post it.
Section of routes.rb:
get 'fandoms' => 'fandoms#index'
get 'fandoms/new' => 'fandoms#new'
post 'fandoms' => 'fandoms#create'
get 'fandoms/:id' => 'fandoms#show', as: :fandom
resources :fandoms
The show method in fandoms_controller:
def show
#fandom = Fandom.find_by(params[:id])
end
The index.html.erb for fandoms:
<div class="main">
<div class="container">
<h2>Fandoms</h2>
<%= link_to "New Fandom (dev only)", "fandoms/new" %>
<% #fandoms.each do |fandom| %>
<div class="fandoms">
<%= link_to fandom.name, fandom_path(fandom) %>
</div>
<% end %>
</div>
And finally the show.html.erb for fandoms:
<div class="main">
<div class="container">
<h2>Fandoms</h2>
<div class="fandom">
<h1><%= #fandom.name %></h1>
</div>
</div>
</div>
I double checked my code with the Ror tutorial on codecademy, but I still couldn't find any differences for what I wanted to do.
Thanks.
Your show action has a subtle typo that's breaking your logic:
#fandom = Fandom.find_by(params[:id])
Should actually be:
#fandom = Fandom.find(params[:id])
The difference is, find_by will return the first row for which the conditions passed are met. You're not actually passing any conditions; just an ID, which in Postgres doesn't even work:
irb> Fandom.find_by(1)
PG::DatatypeMismatch: ERROR: argument of WHERE must be type boolean, not type integer
LINE 1: SELECT "fandoms".* FROM "fandoms" WHERE (1) LIMIT 1
Your database is more permissive, but seems to be treating it just as a WHERE TRUE clause and thus every single row meets the condition. As such, it returns the first row every time. Using find(id) will give you the desired result.
Thanks for any help you can provide! I have a Ruby on Rails application where I am trying to save maps with driving directions and waypoints. The data needs to come straight out of the entry form instead of the Google Maps javascript. I've solved the starting and ending points, but the waypoints are giving me a problem.
My questions:
How can I save each waypoint in its own record in the Waypoint table? I'm able to get the first waypoint into the table, but the rest of the "select multiple" options are ignored.
How can I make each waypoint.newsavedmap_id field the same as its corresponding newsavedmaps id so that I can call these up later?
HTML
<p>Enter a street address, city, and state:
<input id="startinput" type="text" name="starthere" size="56"></p>
<p>Or, select a location from the list:
<select id="startdrop" name="startthere">
<option value="">
<% for masterlocation in #masterlocation %>
<option value="<%= masterlocation.street_address %> <%= masterlocation.city %>, <%= masterlocation.state %>, <%= masterlocation.zip %>"><%= masterlocation.place_name %></option>
<% end %>
</select></p>
<div><b>Stops</b></div>
<div id="multiselectdiv1">
<select multiple id="waypoints" name="waypointsselected">
<% for masterlocation in #masterlocation %>
<option value="<%= masterlocation.street_address %> <%= masterlocation.city %>, <%= masterlocation.state %>, <%= masterlocation.zip %>"><%= masterlocation.place_name %></option>
<% end %>
</select>
</div>
<b>End</b>
<p>Enter a street address, city, and state:
<input id="endinput" type="text" name="endhere" size="56"></p>
<p>Or, select a location from the list:
<select id="enddrop" name="endthere">
<option value="">
<% for masterlocation in #masterlocation %>
<option value="<%= masterlocation.street_address %> <%= masterlocation.city %>, <%= masterlocation.state %>, <%= masterlocation.zip %>"><%= masterlocation.place_name %></option>
<% end %>
</select></p>
</div>
<div>
<input type="submit" onclick="calcRoute();" id="showmapview" value="Show Map">
</div>
I have two MySQL tables. The first is newsavedmaps:
id
itinerary_id
start
start_lat
start_long
start_masterlocation_id
end
end_lat
end_long
end_masterlocation_id
name
The second is waypoints:
id
newsavedmap_id
waypoint
waypoint_lat
waypoint_long
waypoint_masterlocation_id
The two are meant to be connected by newsavedmaps.id and waypoint.newsavedmap_id .
My newsavedmap_controller.rb includes:
def create
#newsavedmap = Newsavedmap.new(params[:newsavedmap])
#newsavedmap.name = params[:newsavedmapname]
if !params[:starthere].blank?
#newsavedmap.start = params[:starthere]
else
#newsavedmap.start = params[:startthere]
end
if !params[:endhere].blank?
#newsavedmap.end = params[:endhere]
else
#newsavedmap.end = params[:endthere]
end
if !params[:waypointsselected].blank?
#waypoint = Waypoint.new(params[:waypoint])
#waypoint.waypoint = params[:waypointsselected]
end
Edit 1
In response to Colinm's suggestion to wrap the controller in an iterator to get separate records for each address, I tried this, but I'm pretty sure I'm doing the wrong thing:
if !params[:waypointsselected].blank?
for waypoint in #waypoint
#waypoint = Waypoint.new(params[:waypoint])
#waypoint.waypoint = params[:waypointsselected]
#waypoint.newsavedmap = #newsavedmap
end
end
How can I save each waypoint in its own record in the Waypoint table? I'm able to get the first waypoint into the table, but the rest of the "select multiple" options are ignored.
Unless you have a really good reason not to, use Rails' form helpers instead of rolling your own form fields. They will take care of this automatically, as well as handle edge cases you may not even know about. (Like the gotcha that sending a multiple select back with no selections will leave the record unchanged. That doesn't sound like it's an issue in your use case, but Rails still has your back if you use the form helpers.)
In your case, you'd simply pass multiple: true in the html_options hash, like so:
<%= f.select :waypointsselected,
MasterLocation.waypoints_for_select,
{},
{ multiple: true }
%>
Note that this does assume you implement a waypoints_for_select method. Without seeing all the code, it looks like you have way too much logic in your view right now. It's brittle and verbose to construct the options array in the view; offloading that to the model or a helper keeps your view code cleaner and helps eliminate potential future bugs.
If you absolutely can't/absolutely don't want to use form helpers in this application, the key you're looking for is []. Rails sensibly assumes a form field represents a single value unless you explicitly denote it as an array. Just tack an empty array on the end of the field name:
<select multiple id="waypoints" name="waypointsselected[]">
...and that field's key in the params hash will instead contain an array of values representing the selected items:
{ waypointsselected: ["3", "6", "9"] }
As for associating the waypoints with the newsavedmaps, just set it explicitly during creation. You're almost there:
if !params[:waypointsselected].blank?
#waypoint = Waypoint.new(params[:waypoint])
#waypoint.waypoint = params[:waypointsselected]
# You can do it like this...
#waypoint.newsavedmap = #newsavedmap
# Or using the ID...
#waypoint.newsavedmap_id = #newsavedmap.id
end
(I haven't adapted this code to deal with the fact you now have an array of values in waypointsselected; I just plugged the relationship definitions into your existing code. Remember to adjust your code to expect an array and iterate over it.)
I am creating a site in which I utilize partial views to display various bits of data about a single Model. Here is a bit of the HTML. (Note, all of these are contained within a single form and the Index page that these partials are rendered in is strongly typed to the main model. The main model contains various lists of data.)
<div id="tab1"><% Html.RenderPartial("Tab1", Model); %></div>
<div id="tab2"><% Html.RenderPartial("Tab2", Model.AnItemList1.FirstOrDefault<AnItemList1>()); %></div>
<div id="tab3"><% Html.RenderPartial("Tab3", Model.AnItemList2.FirstOrDefault()); %></div>
Here is ONE of the partial views headers (for 'tab2'):
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AnItem1>" %>
The pages display correctly. The issue is that, when I enter data into the various parts of the partial pages and then submit the entire form (via POST), the data is not making it back to my data store (MSSQL) - but this only happens for any of the list items (that are contained within the Model). The first partial page does properly have its data set within the data store.
What am I doing wrong here? Should I only be passing the model to Html.RenderPartial and then get the specific model I need on the partial page? Should I pass the entire list and then get the first (right now, I only care about the first item in the list - that will EVENTUALLY change, but not any time soon).
Suggestions or thoughts appreciated.
Update: Here is how I accessing the properties on the partial views.
<div class="data-group">
<%: Html.CheckBoxFor(model => model.Property1) %>
<%: Html.LabelFor(model => model.Property1) %>
</div>
Update 2: Per request...
Controller Action (ScenarioController):
public ActionResult Index(int id = 0)
{
if (id == 0)
{
SavedScenario scenario = new SavedScenario();
scenario.AnItemList1.Add(new AnItem1());
scenario.AnItemList2.Add(new AnItem2());
return View("Index", scenario);
}
else
{
SavedScenario scenario = repository.GetScenario(id);
if (scenario == null)
return View("NotFound");
else
return View("Index", scenario);
}
}
[HttpPost]
public ActionResult Index(SavedScenario scenario)
{
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
Rendered HTML (I can only include parts of it - this is a small sample of what is in the form):
<form action="/Scenario" id="form0" method="post">
<!-- This is the one that works - the basic Scenario. Top level. -->
<fieldset>
<legend>Scenario Information</legend>
<div class="data-group">
<div class="editor-label">
<label for="ScenarioName">Scenario Name</label>
</div>
<div class="option1">
<input class="wide" id="ScenarioName" name="ScenarioName" type="text" value="" />
</div>
<div class="validation">
<div><span class="field-validation-valid" id="ScenarioName_validationMessage"></span></div>
</div>
</div>
</fieldset>
<!-- This does not work or get submitted (as far as I can tell). -->
<div id="tab2">
<fieldset>
<legend>Tab2</legend>
<div class="data-group">
<input id="Property1" name="Property1" type="checkbox" value="true" /><input name="Property1" type="hidden" value="false" />
<label for="Property1" />
</div>
</div>
</fieldset>
</form>
My apologies for having to keep this so generic.
Hard to guess from this much code. However you should make sure that all properties of your models have the same prefix when they are posted back to the server
Edit: form field names should match property names of your model to correctly bind all values. You have two fields with the same name that you can bind in following way
[HttpPost]
public ActionResult Index(SavedScenario scenario, List<bool> Property1)
{
// here you can do with values coming in property1
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
It might be issue with naming the fields on your partial forms. Try naming the fields on your partial views by prefixing it with the name of the Model passed into it...like 'AnItemList1.name' instead of just 'name'..I am just guessing here though...but that's what I did sometimes to fix the problem when I was getting values as null..