Created
April 29, 2018 15:24
-
-
Save tomasc/f192749f7c52e5ad33dd501fcda119a5 to your computer and use it in GitHub Desktop.
dependent fields
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import _ from 'lodash' | |
export default class DependentFields extends Modulor.Plugin | |
@defaults = | |
debug: false | |
name: 'Modulor__DependentFields' | |
# add the <%= Bem.bems :modulor_dependent_fields, :scope %> class on parent element | |
# in order to prevent dependent fields influencing for example nested fields | |
scope_selector: "bems(modulor_dependent_fields, scope), bems(modulor_nested_fields, item), form" | |
@selector: "bems(modulor_dependent_fields)" | |
on_init: -> | |
@scope_element = @get_scope_element() | |
@form_update_handler = (e) => | |
return unless @$element.is(':visible') | |
return unless @is_dependent_on_input($(e.target)) | |
return unless $(e.target).closest(@options.scope_selector).is(@scope_element) | |
@update_dependent_fields() | |
@scope_element.on "change.#{@options.name}", 'input,select', @form_update_handler | |
on_destroy: -> | |
# we need to be specific to remove only handler of this updated fields | |
# otherwise we might accidentally remove all handlers of all dependent fields | |
# in the same form | |
@scope_element.off ".#{@options.name}", 'input,select', @form_update_handler | |
# --------------------------------------------------------------------- | |
depends_on: -> @$element.data('depends-on') | |
depends_on_any: -> @depends_on()['depends_on_any'] | |
depends_on_all: -> @depends_on()['depends_on_all'] | |
depends_on_none: -> @depends_on()['depends_on_none'] | |
# --------------------------------------------------------------------- | |
get_form: -> @$element.closest('form') | |
get_input: (name) -> @get_inputs().filter("[name$='[#{name}]']") | |
get_input_names: -> Object.keys(@depends_on_any() || {}).concat Object.keys(@depends_on_all() || {}).concat Object.keys(@depends_on_none() || {}) | |
get_inputs: -> @scope_element.find('input,select').not(':hidden').filter (i, el) => $(el).closest(@options.scope_selector).is(@scope_element) | |
get_scope_element: -> @$element.closest(@options.scope_selector) | |
# --------------------------------------------------------------------- | |
is_dependent_on_input: ($input) -> | |
_.filter(@get_input_names(), (name) -> $input.is("[name$='[#{name}]']")).length > 0 | |
# --------------------------------------------------------------------- | |
update_dependent_fields: -> if @is_condition_valid() then @show_content() else @hide_content() | |
show_content: -> | |
return if @$element.children().length > 0 | |
return unless html = @$element.data('template-html') | |
@$element.append($(html)) | |
hide_content: -> | |
template_html = @$element.children().remove() | |
@$element.data('template-html', template_html) unless @$element.data('template-html') | |
# --------------------------------------------------------------------- | |
is_condition_valid: -> | |
switch | |
when @depends_on_any() then @is_any_valid() | |
when @depends_on_all() then @is_all_valid() | |
when @depends_on_none() then @is_none_valid() | |
is_any_valid: -> | |
res = false | |
for name, values of @depends_on_any() | |
for value in _.flatten([values]) | |
input_value = @is_valid(name, value) | |
res = (res || input_value) | |
res | |
is_all_valid: -> | |
res = true | |
for name, values of @depends_on_all() | |
for value in _.flatten([values]) | |
input_value = @is_valid(name, value) | |
res = (res && input_value) | |
res | |
is_none_valid: -> | |
res = true | |
for name, values of @depends_on_none() | |
for value in _.flatten([values]) | |
input_value = @is_valid(name, value) | |
res = (res && !input_value) | |
res | |
is_valid: (name, value) -> | |
$input = @get_input(name) | |
value = value.toString() | |
switch value | |
when 'true' then value = '1' | |
when 'false' then value = '0' | |
switch $input.prop('type') | |
when 'radio' then $input.filter(':checked').val() == value | |
when 'checkbox' | |
switch value | |
when '1' then $input.filter(':checked').length > 0 | |
else $input.not(':checked').length > 0 | |
when 'select-one' then $input.val() == value | |
when 'select-multiple' then value in $input.val() | |
else $input.val() == value | |
DependentFields.register() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# = form.input :center, collection: MapModule::CENTER, as: :radio_buttons | |
# | |
# = form.dependent_fields depends_on_all: { center: :address } do | |
# = form.input :address | |
# | |
# = form.dependent_fields depends_on_all: { center: :coordinates } do | |
# = form.input :center_lat | |
# = form.input :center_lng | |
module ActionView | |
module Helpers | |
class FormBuilder | |
def dependent_fields(options = {}, &block) | |
Modulor::DependentFieldsBuilder.new(self, @template, options).dependent_fields(&block) | |
end | |
end | |
end | |
end | |
module Modulor | |
class DependentFieldsBuilder < Struct.new(:builder, :template, :options) | |
def initialize(builder, template, options = {}) | |
super(builder, template, options) | |
end | |
delegate :object, | |
to: :builder | |
def dependent_fields(&block) | |
html = template.capture(&block) | |
tag_html = condition_valid? ? html : nil | |
data_template_html = condition_valid? ? {} : { template_html: CGI.escapeHTML(html).html_safe } | |
template.content_tag :div, tag_html, class: [dom_class, Bem.bem(:modulor_dependent_fields)].reject(&:blank?).flatten, data: dom_data.merge(data_template_html) | |
end | |
private | |
def dom_class | |
options.fetch(:class, nil) | |
end | |
def dom_data | |
{ depends_on: options.slice(:depends_on_any, :depends_on_all, :depends_on_none) } | |
end | |
# --------------------------------------------------------------------- | |
def depends_on_any | |
options.fetch(:depends_on_any, nil) | |
end | |
def depends_on_all | |
options.fetch(:depends_on_all, nil) | |
end | |
def depends_on_none | |
options.fetch(:depends_on_none, nil) | |
end | |
# --------------------------------------------------------------------- | |
def condition_valid? | |
case | |
when depends_on_any.present? then any_valid? | |
when depends_on_all.present? then all_valid? | |
when depends_on_none.present? then none_valid? | |
end | |
end | |
def any_valid? | |
return unless depends_on_any.present? | |
depends_on_any.any? do |name, values| | |
Array(values).any? do |value| | |
is_valid?(name, value) | |
end | |
end | |
end | |
def all_valid? | |
return unless depends_on_all.present? | |
depends_on_all.all? do |name, values| | |
Array(values).all? do |value| | |
is_valid?(name, value) | |
end | |
end | |
end | |
def none_valid? | |
return unless depends_on_none.present? | |
depends_on_none.all? do |name, values| | |
Array(values).none? do |value| | |
is_valid?(name, value) | |
end | |
end | |
end | |
def is_valid?(name, value) | |
return false unless object.respond_to?(name) | |
value = nil if value == 'null' | |
object.send(name) == value | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment