I am using ruby 1.9.3 and rails 4.1.
I very new to ruby and have no previous javascripting knowledge.
I have a couple of simple has many relationships and one has many through relationship.
class Service < ActiveRecord::Base
has_many :websites, :dependent => :destroy
has_many :timetables, :dependent => :destroy
has_many :service_places, :dependent => :destroy
has_many :places, through: :service_places
end
class ServicePlace < ActiveRecord::Base
belongs_to :service
belongs_to :place
end
class Website < ActiveRecord::Base
belongs_to :service
end
class Place < ActiveRecord::Base
has_many :service_places
has_many :services, through: :service_places
end
class Timetable < ActiveRecord::Base
belongs_to :service
end
I have create nested forms in the service form (_form.html.erb) for each of the associations which render fine, all the CRUD functions work.
<%= form_for(@service, :html => {:class => "form-horizontal", :role => "form"}) do |f| %>
<%= render 'wcc_style/layouts/error_messages', object: @service %>
<!-- I've skipped some of the html for the service details to just rendering the forms... -->
<!-- Adds the Service_Places associations -->
<%= f.fields_for :service_places do |service_places| %>
<%= render "service_place_fields", :f => service_places %>
<%end %>
<!-- Adds the website associations via a partial -->
<%= f.fields_for :websites do |website| %>
<%= render "website_fields", :f => website %>
<%end %>
<%= link_to_add_fields "Add a website", f, :websites, "btn btn-default", "Add a new website" %>
<!-- Adds the timetables associations via a partial -->
<%=f.fields_for :timetables do |timetable| %>
<%= render "timetable_fields", :f => timetable %>
<%end %>
<%= f.submit 'Save', :class => 'btn btn-primary' %>
<%= link_to 'Cancel', services_path, :class => "btn btn-default", :role =>"button" %>
<% end %>
Example of website partial (_website_fields.html.erb)
<!-- This partial holds the websites information for main service form -->
<div class="form-group">
<%= f.label :website, :class=>'col-sm-2 control-label' %>
<div class="col-sm-4">
<%= f.text_field :url, :class => "form-control", :placeholder => "Service Provider Website" %>
</div>
</div>
What I am trying to do now is to add functionality to be able to add and remove items (eg websites, timetables, service_places) related to the service when adding\editing a service record. I've seen a lot of posts relating to this but nothing with a good working solution using jquery and rails 4.
Having done some research on this I found the old railcasts 196 and 197. These were out of date and didn't work in rails 4. More research indicated a depreciated command. I saw there was a newer version of the railscasts which I haven't looked at yet. I did read the comments on the revised version and it indicates that it uses coffeescript, although I can't confirm this.
I have also seen links to other potential solutions using the cocoon gem and nested_form_fields. Both seem to use HAML which I am unfamiliar with.
I am trying to do this using jquery, to help me understand the javascript process more than anything (because I'll need to support the development when it's complete and our standard is to use JQuery where possible).
I've managed to get the add sort of working in that it renders a website label and checkbox on first press but then on subsequent clicks simply renders this again for about a second before disappearing. The position isn't where I would expect it to be either, at the top of the page, but I am sure that is down to our own styling gem which deals with the CSS. I thought that javascript was not supposed to do a POST which is what it appears to do because it clears my other fields on each button click.
My first question is how can I get the add function to add a new item without clearing the other items in my services form?
My second question is how do I get a delete function to work. I am assuming that the delete has to be within the form builder in the partial (eg one delete per item).
I've included the application helper and javascript below (cobbled together from various posts).
application_helper.rb module ApplicationHelper
#def link_to_remove_fields(name, f)
# f.hidden_field(:destroy) + link_to_function(name, "remove_fields(this)")
#end
# remove link doesn't work!!!!!!!!!
def link_to_remove_fields(name, f)
f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)")
end
# This now sort of works but the field disappears when it is created after about a second. It also appears at the top of the page.
def link_to_add_fields(name, f, association, cssClass, title)
new_object = f.object.class.reflect_on_association(association).klass.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render(association.to_s.singularize + "_fields", :f => builder)
end
link_to name, "#", :onclick => h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")"), :class => cssClass, :title => title
end
end
application.js
function remove_fields(link) {
$(link).prev("input[type=hidden]").val("1");
$(link).closest(".fields").hide();
}
function add_fields(link, association, content) {
var new_id = new Date().getTime();
var regexp = new RegExp("new_" + association, "g")
$(link).parent().before(content.replace(regexp, new_id));
}
services_controller.rb
class ServicesController < ApplicationController
# Set the service record before each action
before_action :set_service, only: [:show, :edit, :update, :destroy]
################################################################################
# Create
################################################################################
def create
@service = Service.new(service_params)
respond_to do |format|
if @service.save
format.html { redirect_to @service, notice: 'Service was successfully created.' }
format.json { render action: 'show', status: :created, location: @service }
else
@service.websites.build
@service.timetables.build
@service.service_places.build
format.html { render action: 'new' }
format.json { render json: @service.errors, status: :unprocessable_entity }
end
end
end
################################################################################
# Delete
################################################################################
def destroy
@service.destroy
respond_to do |format|
format.html { redirect_to services_url , notice: 'Service was successfully deleted.' }
format.json { head :no_content }
end
end
################################################################################
# Edit
################################################################################
def edit
@service.websites.build
@service.timetables.build
@service.service_places.build
end
################################################################################
# Index
################################################################################
def index
@services = Service.order(:service_number).page params[:page]
end
################################################################################
# New
################################################################################
def new
@service = Service.new
@service.websites.build
@service.timetables.build
@service.service_places.build
end
################################################################################
# Search
################################################################################
def search
@search_term = params[:search_term]
if @search_term && @search_term.strip.empty?
redirect_to root_path
else
@services = Service.search(@search_term).order(:service_number).page params[:page]
end
end
################################################################################
# Show
################################################################################
def show
end
################################################################################
# Update
################################################################################
def update
respond_to do |format|
if @service.update(service_params)
format.html { redirect_to @service, notice: 'Service was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: @service.errors, status: :unprocessable_entity }
end
end
end
################################################################################
# Private methods
################################################################################
private
# Use callbacks to share common setup or constraints between actions.
def set_service
@service = Service.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def service_params
params.require(:service).permit(:service_number, :operator, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday, :bank_holiday, :search_term, websites_attributes: [:id, :service_id, :url, :_destroy], timetables_attributes: [:id, :service_id, :url, :_destroy], service_places_attributes: [:id, :service_id, :position, :place_id, :_destroy], places_attributes: [:id, :place_name, :active, :_destroy])
end
end
Having scoured the internet I can't seem to find any links on how to get this working using jquery and standard ruby\rails. Does anyone know of any links or resources that would be useful for trying to achieve this?
I am not adverse to using gems like cocoon or nested_form_fields, assuming I can convert the HAML to ERB and coffeescript to javascript. Being a newbie to both ruby\rails and javascript having builders doing stuff is great but when it goes wrong I wouldn't have a clue how to fix it.
I appreciate any help or guidance.