Your SlideShare is downloading. ×
  • Like
Instant Dynamic Forms with #states
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Instant Dynamic Forms with #states

  • 2,542 views
Published

 

Published in Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • Hey! thank you for the slide, was really useful for this patch to Conditional Fields for making the value as Regex Expression for comparing, take a look! :)
    http://drupal.org/node/1340616
    Cheers!
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
2,542
On SlideShare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
22
Comments
1
Likes
3

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Instant Dynamic Forms with #states
  • 2. Konstantin Käfer 2 2006
  • 3. #states 3
  • 4. 4
  • 5. Anatomy of a state 5 $form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 6. Anatomy of a state 5 $form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 7. Anatomy of a state 5 $form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 8. Anatomy of a state 5 $form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 9. Anatomy of a state 5 $form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 10. Anatomy of a state 5 $form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 11. Anatomy of a state 5 $form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 12. Anatomy of a state 5 $form['payment_information'] = array( '#type' => 'fieldset', '#title' => t('Payment information'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 13. Declarative definition 6 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ➜ Expanded when the element “payment” is checked
  • 14. Dependencies 7 $form['payment_information'] = array(... '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), ); <fieldset> Depends on Influences <input type="checkbox">
  • 15. Targeting elements 8 ◆ Uses plain CSS selectors with jQuery ◆ [name="payment"] #edit-payment .payment :checkbox, #edit-payment ◆ Don’t use #selector for auto-assigned IDs
  • 16. States 9 ◆ Arbitrary names are possible ◆ visible irrelevant confirmed checked valid important ◆ Prefixing with ! negates ◆ visible = !invisible invisible = !visible
  • 17. State aliases 10 ◆ Associate custom aliases Primary name ◆ Drupal.states.State.aliases ['unimportant'] = '!important'; ◆ enabled = !disabled invisible = !visible invalid = !valid untouched = !touched optional = !required filled = !empty unchecked = !checked irrelevant = !relevant expanded = !collapsed readwrite = !readonly
  • 18. Drawbacks 11 ◆ Doesn’t support OR and XOR
  • 19. Drawbacks 11 ◆ Doesn’t ! chsupport OR and XOR Pat drupal.org/node/735528
  • 20. AND operator 12 'disabled' => array( '[name="ccv"]' => array( 'invalid' => TRUE ), '[name="card_number"]' => array( 'invalid' => TRUE ), ),
  • 21. OR operator 13 'disabled' => array( array( '[name="ccv"]' => array( 'invalid' => TRUE ), ), array( '[name="card_number"]' => array( 'invalid' => TRUE ), ), ),
  • 22. OR operator 13 'disabled' => array( array( '[name="ccv"]' => array( 'invalid' => TRUE ), ), Numeric keys array( '[name="card_number"]' => array( 'invalid' => TRUE ), ), ),
  • 23. XOR operator 14 'disabled' => array('xor', array( '[name="ccv"]' => array( 'invalid' => TRUE ), ), array( '[name="card_number"]' => array( 'invalid' => TRUE ), ), ),
  • 24. XOR operator 14 'disabled' => array('xor', Operator array( '[name="ccv"]' => array( 'invalid' => TRUE ), ), array( '[name="card_number"]' => array( 'invalid' => TRUE ), ), ),
  • 25. Drawbacks 15 ◆ Doesn’t ! chsupport OR and XOR Pat drupal.org/node/735528
  • 26. Drawbacks 15 ◆ Doesn’t ! chsupport OR and XOR Pat drupal.org/node/735528 ◆ Doesn’t support radio buttons
  • 27. Drawbacks 15 ◆ Doesn’t ! chsupport OR and XOR Pat drupal.org/node/735528 ◆ Doesn’t support radio buttons end! E xt
  • 28. Triggers
  • 29. Default Triggers 17 Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ... };
  • 30. Default Triggers 17 Drupal.states.Trigger.states = { ... checked: { Native DOM event 'change': function () { return this.attr('checked'); } }, ... };
  • 31. Default Triggers 17 Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ... };
  • 32. Default Triggers 17 Drupal.states.Trigger.states = { ... checked: { 'change': function () { return this.attr('checked'); } }, ... }; Value function
  • 33. Default Triggers 18 Initialization Execution Drupal.states.Trigger.states = { Drupal.states.Trigger.states = { ... ... checked: { checked: { 'change': function () { 'change': function () { return this.attr('checked'); return this.attr('checked'); } } }, }, ... ... }; }; $('#element').bind('change', $('#element').bind('change', function() { function() { ... ... } } ); );
  • 34. Default Triggers 18 Initialization Execution Drupal.states.Trigger.states = { Drupal.states.Trigger.states = { ... ... checked: { checked: { 'change': function () { 'change': function () { return this.attr('checked'); return this.attr('checked'); } } }, }, ... ... }; }; $('#element').bind('change', $('#element').bind('change', function() { function() { ... ... } } ); );
  • 35. Default Triggers 18 Initialization Execution Drupal.states.Trigger.states = { Drupal.states.Trigger.states = { ... ... checked: { checked: { 'change': function () { 'change': function () { return this.attr('checked'); return this.attr('checked'); } } }, }, ... ... }; }; $('#element').bind('change', $('#element').bind('change', function() { function() { ... ... } } ); );
  • 36. Multiple Triggers 19 Drupal.states.Trigger.states = { ... value: { 'keyup': function () { return this.val(); }, 'change': function () { return this.val(); } }, ... };
  • 37. Multiple Triggers 20 Drupal.states.Trigger.states = { ... value: { 'keyup change': function () { return this.val(); } }, ... };
  • 38. Custom Triggers 21 '#states' => array( 'disabled' => array( '[name="delayed"]' => array('value' => 'foo') ), ),
  • 39. Custom Triggers 22 Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } }; ... };
  • 40. Custom Triggers 22 Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } }; ... };
  • 41. Custom Triggers 22 Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } }; ... };
  • 42. Custom Triggers 22 Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } }; ... };
  • 43. Custom Triggers 22 Drupal.states.Trigger.states.delayedValue = function(element) { var value = element.val(), oldValue, timeout; var trigger = function() { if (oldValue !== value) { element.trigger({ type: 'state:delayedValue', value: value, oldValue: oldValue }); oldValue = value; } }; ... };
  • 44. Custom Triggers 23 Drupal.states.Trigger.states.delayedValue = function(element) { ... element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };
  • 45. Custom Triggers 23 Drupal.states.Trigger.states.delayedValue = function(element) { ... element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };
  • 46. Custom Triggers 23 Drupal.states.Trigger.states.delayedValue = function(element) { ... element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };
  • 47. Custom Triggers 23 Drupal.states.Trigger.states.delayedValue = function(element) { ... element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };
  • 48. Custom Triggers 23 Drupal.states.Trigger.states.delayedValue = function(element) { ... element.bind('keyup change', function (e) { if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { value = element.val(); trigger(); }, 1000); }); Drupal.states.postponed.push(trigger); };
  • 49. Custom Triggers 24 Drupal.states.Trigger.states.toggle = function(element) { var value = true, oldValue = undefined; var trigger = function() { value = !value; element.trigger({ type: 'state:toggle', value: value, oldValue: oldValue }); oldValue = value; }; setInterval(trigger, 1000); Drupal.states.postponed.push(trigger); };
  • 50. Comparisons
  • 51. Comparisons 26 $form['payment_information'] = array( ... '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), );
  • 52. Comparisons 26 $form['payment_information'] = array( ... '#states' => array( 'expanded' => array( '[name="payment"]' => array('checked' => TRUE) ), ), === );
  • 53. Advanced Comparisons 27 states.Dependant.comparisons = { 'RegExp': function (reference, value) { return reference.test(value); }, 'Function': function (reference, value) { return reference(value); } };
  • 54. Advanced Comparisons 27 states.Dependant.comparisons = { 'RegExp': function (reference, value) { return reference.test(value); }, 'Function': function (reference, value) { return reference(value); } }; Prototype name
  • 55. JSON only allows strings and numbers 28
  • 56. :( 29
  • 57. Advanced Comparisons 30 'invalid' => array( '[name="card_number"]' => array( '!value' => '0000 0000 0000 0000', ), ), 'invalid' => array( '[name="card_number"]' => array( '!value' => array('regex' => '^(d{4}[ -]*){4}$'), ), ),
  • 58. Advanced Comparisons 30 'invalid' => array( '[name="card_number"]' => array( '!value' => '0000 0000 0000 0000', ), ), 'invalid' => array( '[name="card_number"]' => array( '!value' => array('regex' => '^(d{4}[ -]*){4}$'), ), ),
  • 59. Advanced Comparisons 31 Drupal.states.Dependant.comparisons.Object = function(reference, value) { if ('regex' in reference) { return RegExp(reference.regex, ↵ reference.flags).test(value); } else { return reference.indexOf(value) !== false; } };
  • 60. Advanced Comparisons 32 'invalid' => array( '[name="card_number"]' => array( '!value' => array('regex' => '^(d{4}[ -]*){4}$'), ), ),
  • 61. Transitions
  • 62. State changes 34 ◆ Transition an element from one state to another Direct Indirect ◆ Triggered by user ◆ Triggered by other ◆ Notify listeners element ◆ Transition element
  • 63. State changes 35 $(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); } });
  • 64. State changes 35 $(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); } });
  • 65. State changes 35 $(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); } });
  • 66. State changes 35 $(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); } });
  • 67. State changes 35 $(document).bind('state:checked', function(e) { if (e.trigger) { $(e.target).attr('checked', e.value); } });
  • 68. document <html> ◆ Event bubbling allows overwriting handlers <body> for specific regions <div id="body"> ◆ CSS selectors allow overwriting handlers <div class="element"> for specific elements 36 <input type="text"> State changes
  • 69. Future Work
  • 70. Domain-specific language 38 state_of('[name="baz"]') ->is('checked') ->when('[name="bar"]')->checked() ->and('[name="foo"]')->value('foo') ->orWhen('[name="bar"]')->unchecked() ->is('disabled') ->when('[name="bar"]')->unchecked() ->is('invisible') ->when('[name="foo"]')->empty(); Ideas?
  • 71. Copy & Paste support 39
  • 72. Multi-value support 40 // The value of at least one element is true. {'any': true} // At least two elements are true. {'n > 2': true} // The third element is false. {'2': false}
  • 73. Extended values 41 // The value is greater than 8 or smaller than 5. [ {'>': 8}, {'<': 5} ] // At least two elements are between 5 and 8. {'n > 2': {'>': 5, '<': 8}} // The sum of the values of all elements is // greater than 10. {'sum': {'>=': 10}}
  • 74. Questions? 42 kkaefer.com/2010/states.pdf mail@kkaefer.com