I have this problem:
My web application has a form where the users can customize their profile.
In each profile can be specified many skills and I want to allow the users to press one button (add new skill) in order to specify as many skills as they want.
So this is the controller code:
accepts_nested_attributes_for :skills, :allow_destroy=>true, :reject_if => lambda {|a| a[:name].blank?}
This is the form (just the part with the nested attribute skill):
<%= f.fields_for :skills do |builder|%>
<div class="field">
<%= builder.label :skill %>
<%= builder.text_field :name%>
<%= builder.hidden_field :_destroy %>
<%= link_to 'remove', '#', :onclick=>'removeField()'%>
</div>
<%end%>
This form work perfectly and shows each skill of the user, it allows editing etc. The problem now, is that I want to add a link to "add new skill" so a javascript function that change the form and add new skill input field, I really have no idea to how to act, mainly because the nested attributes have specific id and name in the form that I don't understand:
<input id="profile_skills_attributes_0_name" name="profile[skills_attributes][0][name]" size="30" type="text" value="Mathematicansszz" />
It also add another hidden input field with the id of the skill (impossible to predict if the skill is not created), make impossible to create new skill from an HTML static pages?
<input id="profile_skills_attributes_0_id" name="profile[skills_attributes][0][id]" type="hidden" value="3" />
Any idea or workaround ? Thank you
I have written a gem that can handle that makes handling nested forms dynamically easier: cocoon.
The gem works with the standard rails formhelpers, but also with formtastic or simple_form.
I would also advise you to checkout formtastic or simple_form, as those are awesome gems to make form-handling easier. But as with HAML, that is a personal choice.
fields_for creates an array of fields. The "0" in the name represents the index in the array. Since you're creating new records you can simply ignore the hidden id field.
So via javascript you just need to do two things:
1. Count the number of existing inputs for the child collection in order to get a new index.
2. Add a new text field using the new index.
This is relatively easy to do with jQuery using a wildcard selector like $('input[name$="][name]"]').length; to retrieve a count (for the new index). If you have another field for array with name fields you may want to use a regex selector instead (via plugin). An easier way might be to just add a class to each of your skill name inputs and use that class as the selector when counting.
To append the new input see:
http://api.jquery.com/append/
If you're not using jquery, then it should be similar in other frameworks with a little googling.
Related
I have a Domain Class Project with a one-to-many property users :
static hasMany = [users: User]
In my scaffolding code the view is created with:
<div class="fieldcontain ${hasErrors(bean: projectInstance, field: 'users', 'error')} ">
<label for="users">
<g:message code="project.users.label" default="Users" />
</label>
<g:select name="users" from="${usermanagement.User.list()}" multiple="multiple" optionKey="id" size="5" value="${projectInstance?.users*.id}" class="many-to-many"/>
</div>
This results in a simple list where I can select multiple users. The user list is expected to be quite big so this selection isn't really viable. Is there a simple way in grails to do this a bit more comfortable? The best solution I can imagine would be a list with an autocomplete searchform and a second list where the selected entries are displayed.
I don't think that there is an easy way to do this and that I probably have to use javascript or jquery (autocomplete etc.)
Any help improving my current status (selection from huge list via ctrl + click)
would be very appreciated.
There is a jQuery plugin called Chosen that will do what you are wanting, it supports multiple selections. I have a use case much like yours in one of my apps and Chosen worked out great:
http://harvesthq.github.io/chosen/
A possible solution is using some javascript based stuff like boostrap select2 or Kendo UI Multiselect. They are based on a html select box that unobtrusively enhanced the selection model of a this html element. So there is no real javascript code to implement, since the selection model for the html form stays the same as with disabled javascript.
I'm trying to set a text_field and text area on a webpage that doesn't have an id any longer. I'm guessing the site is trying to avoid automation. The input and textarea tags are inside of a form. Here are the input and textarea tags and what is contained.
<input class="uniform-input ng-pristine ng-invalid ng-invalid-required ng-valid-maxlength" type="text" data-invalid-chars="" data-max-length="50" required="" placeholder="Subject" data-float-label="true" data-ng-model="message.Subject"></input>
<textarea class="uniform-input ng-pristine ng-invalid ng-invalid-required ng-valid-maxlength" data-invalid-chars="" data-max-length="4000" required="" placeholder="Enter your message here" data-ng-keypress="view.error = false" data-float-label="true" data-ng-model="message.Body"></textarea>
Also there is a button that I need to click after submitting the text with this button tag:
<button data-ng-if="!paymentInfo" type="button" class="button button-grey ng-scope" data-ng-click="ctrl.sendMessage()" data-ng-disabled="view.waiting" data-ng-class="{ 'button-disabled': view.waiting }">Send Now</button>
How do I click it when it has no name?
Any help as to how to set this with Watir would be very appreciated. If Watir is unable to do it is there a possible JS workaround that I could use? Please let me know if any further information is needed to help.
Using ruby gem watir
require 'watir-webdriver'
$browser = Watir::Browser.new
$browser.goto "yourwebsite.com"
$x = 0
def test
print "#{$x}"
begin
$browser.text_fields[$x].set "#{$x}"
rescue StandardError => e
puts " no text field found, try again.\n\n"
end
$x += 1
end
Keep changing the value of X to see what text fields you are manipulating. I suppose you could make a loop but you might get an error. Keep calling test until you find what you're looking for.
The elements do look like they have some descriptive attributes. The text fields have a data-ng-model that describes the field. As well, the button has a text that is likely unique.
Therefore, I would do:
browser.text_field(:data_ng_model => 'message.Subject').set('subject text')
browser.textarea(:data_ng_model => 'message.Body').set('body text')
browser.button(:text => 'Send Now').click
I think this approach is more expressive in terms of what your code is doing. As well, it can be more robust as it is not susceptible to fields being re-ordered or other fields being added/removed.
I have been identifying those ng-data objects via xpath, mainly when there is not a more specific way to identify them. Justin is right about an approach that is robust; find a way taht does not need to be refactored down the road. Here is what I would have:
browser.text_field(xpath: '//input[#data-ng-model="message.Subject"]').set("Hello")
browser.button(:text => 'Send Now').click
I prefer not using a lot of xpath, except for when it guarantees me a unique way to find an object on a page.
I'm using foundation in a rails app and I'm looking for a way to validate the length of text fields in a form: I'd like to display an error when the text field contains too many characters (but not when it's empty).
I tried to use Foundation's abide and create custom named patterns as explained in the docs.
Here are the contents of my application.js file including the custom patterns upon Foundation initialization:
$(function(){
$(document)
.foundation()
.foundation('abide', {
patterns: {
short_field: /^.{,40}$/,
long_field: /^.{,72}$/
}
});
});
And here is my form code in the view:
<form data-abide>
<div class="long-name-field">
<input type="text" pattern="long_field" placeholder="Long Field">
<small class="error">Too long.</small>
</div>
<div class="short-name-field">
<input type="text" pattern="short_field" placeholder="Short Field">
<small class="error">Too long.</small>
</div>
</form>
The problem is that when I load my form page all the fields always display the error message, whether they're empty, filled under their character limit or exceeding their character limit.
Anyone successfully used abide to do something similar (or knows a better way that is not using custom named patterns)?
Cheers.
I finally managed to make it work!
The problem was that /^.{,40}$/ is not a valid regexp syntax, you have to use /^.{0,40}$/ explicitly.
I mistake it with the /.{5,}/ syntax that you can use to impose a only a lower limit.
I could not make the javascript abide work in my Rails 4 app, so I just added the regex directly as an attribute like so:
<%= text_area_tag 'answer', #current_answer,
:placeholder => 'required', :required => '', :pattern => '^(.){0,1000}$' %>
For validating minimum length only I use:
<%= text_area_tag 'answer', #current_answer,
:placeholder => 'required', :required => '', :pattern => '^(.){100,}$' %>
Is there a specific reason that most everyone implements edit-in-place as a shown 'display' div and a hidden 'edit' div that are toggled on and off when somebody clicks on the associated 'edit' button like so?
<div id="title">
<div class="display">
<h1>
My Title
</h1>
</div>
<div class="edit">
<input type="text" value="My Title" />
<span class="save_edit_button"></span>
Cancel
</div>
</div>
Everywhere I look, I see edit-in-place basically handled like this. This approach certainly makes sense when you are rendering all views on the server side and delivering them to the client. However, with pure AJAX apps and frameworks like backbone.js, it seems that we could make our code much more DRY by rendering edit-in-place form elements on the fly as necessary, possibly even making a factory method that determines which form element to render. e.g.
an H1 element with class "title" is replaced by <input type="text" />
a span with class "year_founded" is replaced by <input type="number" min="1900" max="2050" />
a span with class "price" is replaced by an input with the appropriate mask to only allow prices to be input.
Is this practice of rendering all edit-in-place form elements a historical legacy leftover from when pages were rendered on the server-side?
Given the flexibility and power we have with client-side MVC frameworks like Backbone.js, is there a reason for not creating and inserting the form elements on the fly when necessary using a factory method? Something like this:
HTML
<div id="description">
Lorem ipsum dolar set amit...
</div>
<span class="edit_button"></span>
Backbone.js View
events: {
"click .edit_button": "renderEditInPlaceForm",
},
renderEditInPlaceForm: function:(e) {
var el = $(e.currentTarget).previous();
var id = el.attr('id');
var value = el.text();
var tagName = el.tagName();
var view = new editInPlaceForm({
id: id,
type: tagName,
value: value
});
$("#id").html(view.render().el)
},
Where editInPlaceForm is a factory that returns the appropriate edit-in-place form element type based on tagName. This factory view also controls all its own logic for saving an edit, canceling an edit, making requests to the server and rerendering the appropriate original element that was replaced with the .html() function?
It seems to me that if we use this approach then we could also render the <span class="edit_button"></span> buttons on the fly based on a user's editing rights like so:
<h1 id="title">
<%= document.get("title") %>
</h1>
<% if (user.allowedToEdit( document, title )) { %>
<span class="edit_glyph"></span>
<% } %>
where the allowedToEdit function on the user model accepts a model and attribute as its arguments.
It's an interesting idea. The devil is in the detail.
While your simple example is easily rendered as an editable form on the fly, things quickly get trickier when dealing with other data types.
For example - suppose my edit form requires the user to choose a value from a select list. On the display form I can simply display the user's choice, but for the edit form I am going to need those other available choices. Where do I hide them on the display? Similar issues exist for checkboxes, radio lists...
So, perhaps we should consider rendering the edit form, and then deriving our display-view from that?
After 5 Backbone apps I came to same thoughts.
When things are complicated you have forms to show relations between user data,
but in simple cases you just need input, select, checkbox over h1, div or span
Now I am searching for jQuery plugin to make simple in place editing without ajax.
jQuery but not Backbone becuase I don't want to be tight coupled with Backbone for such small thing.
Likely to wright my own jQuery + Synapse plugin http://bruth.github.com/synapse/docs/.
Synapse for binding with model and jQuery for input placing
I am currently trying to write functional tests for a charging form which gets loaded on to the page via AJAX(jQuery). It loads the form from the charge_form action which returns the consult_form.js.erb view.
This all works, but I am having trouble with my testing.
In the functional I can go to the action but I cannot use assert_select to find a an element and verify that the form is in fact there.
Error:
1) Failure:
test_should_create_new_consult(ConsultsControllerTest) [/test/functional/consults_controller_test.rb:8]:
Expected at least 1 element matching "h4", found 0.
<false> is not true.
This is the view.
consult_form.js.erb:
<div id="charging_form">
<h4>Charging form</h4>
<div class="left" id="charge_selection">
<%= select_tag("select_category", options_from_collection_for_select(#categories, :id, :name)) %><br/>
...
consults_controller_test.rb:
require 'test_helper'
class ConsultsControllerTest < ActionController::TestCase
def test_should_create_new_consult
get_with_user :charge_form, :animal_id => animals(:one), :id => consults(:one), :format => 'js'
assert_response :success
assert_select 'h4', "Charging form" #can't find h4
end
end
Is there a problem with using assert_select with types other than html?
Thank you for any help!
So, the problem was that I should put javascript in .js and HTML in .html :P Kinda obvious.
I was putting html into a javascript file. I fixed it by simply renaming charge_form.js.erb to charge_form.html.erb.