SlideShare a Scribd company logo
Instant
Dynamic Forms
      with
             #states
Konstantin Käfer
                   2




    2006
#states   3
4
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)
      ),
   ),
);
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)
      ),
   ),
);
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)
      ),
   ),
);
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)
      ),
   ),
);
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)
      ),
   ),
);
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)
      ),
   ),
);
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)
      ),
   ),
);
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)
      ),
   ),
);
Declarative definition
                                                    6


'expanded' => array(
   '[name="payment"]' => array('checked' => TRUE)
),



 ➜ Expanded when the element “payment”
   is checked
Dependencies
                                                         7


$form['payment_information'] = array(...
   '#states' => array(
     'expanded' => array(
        '[name="payment"]' => array('checked' => TRUE)
      ),
   ),
);
                      <fieldset>

           Depends on           Influences


           <input type="checkbox">
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
States
                                           9


◆   Arbitrary names are possible
◆   visible    irrelevant      confirmed
    checked    valid           important


◆   Prefixing with ! negates
◆   visible = !invisible
    invisible = !visible
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
Drawbacks
                                 11


◆   Doesn’t support OR and XOR
Drawbacks
                                       11


◆   Doesn’t !
         chsupport OR and XOR
      Pat
              drupal.org/node/735528
AND operator
                                        12


'disabled' => array(

     '[name="ccv"]' => array(
       'invalid' => TRUE
     ),


     '[name="card_number"]' => array(
       'invalid' => TRUE
     ),

),
OR operator
                                        13


'disabled' => array(
   array(
     '[name="ccv"]' => array(
        'invalid' => TRUE
      ),
   ),
   array(
     '[name="card_number"]' => array(
        'invalid' => TRUE
      ),
   ),
),
OR operator
                                        13


'disabled' => array(
   array(
     '[name="ccv"]' => array(
        'invalid' => TRUE
      ),
   ),     Numeric keys
   array(
     '[name="card_number"]' => array(
        'invalid' => TRUE
      ),
   ),
),
XOR operator
                                        14


'disabled' => array('xor',
   array(
     '[name="ccv"]' => array(
        'invalid' => TRUE
      ),
   ),
   array(
     '[name="card_number"]' => array(
        'invalid' => TRUE
      ),
   ),
),
XOR operator
                                             14


'disabled' => array('xor',        Operator
   array(
     '[name="ccv"]' => array(
        'invalid' => TRUE
      ),
   ),
   array(
     '[name="card_number"]' => array(
        'invalid' => TRUE
      ),
   ),
),
Drawbacks
                                       15


◆   Doesn’t !
         chsupport OR and XOR
      Pat
              drupal.org/node/735528
Drawbacks
                                       15


◆   Doesn’t !
         chsupport OR and XOR
      Pat
              drupal.org/node/735528

◆   Doesn’t support radio buttons
Drawbacks
                                       15


◆   Doesn’t !
         chsupport OR and XOR
      Pat
              drupal.org/node/735528

◆   Doesn’t support radio buttons
          end!
      E xt
Triggers
Default Triggers
                                       17


Drupal.states.Trigger.states = {
   ...
   checked: {
     'change': function () {
        return this.attr('checked');
      }
   },
   ...
};
Default Triggers
                                       17


Drupal.states.Trigger.states = {
   ...
   checked: {   Native DOM event
     'change': function () {
        return this.attr('checked');
      }
   },
   ...
};
Default Triggers
                                       17


Drupal.states.Trigger.states = {
   ...
   checked: {
     'change': function () {
        return this.attr('checked');
      }
   },
   ...
};
Default Triggers
                                       17


Drupal.states.Trigger.states = {
   ...
   checked: {
     'change': function () {
        return this.attr('checked');
      }
   },
   ...
};           Value function
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() {
     ...                                    ...
   }                                      }
);                                     );
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() {
     ...                                    ...
   }                                      }
);                                     );
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() {
     ...                                    ...
   }                                      }
);                                     );
Multiple Triggers
                                   19


Drupal.states.Trigger.states = {
   ...
   value: {
     'keyup': function () {
        return this.val();
      },
     'change': function () {
        return this.val();
      }
   },
   ...
};
Multiple Triggers
                                     20


Drupal.states.Trigger.states = {
   ...
   value: {
     'keyup change': function () {
        return this.val();
      }
   },
   ...
};
Custom Triggers
                                                     21


'#states' => array(
   'disabled' => array(
     '[name="delayed"]' => array('value' => 'foo')
   ),
),
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;
       }
    };

    ...
  };
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;
       }
    };

    ...
  };
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;
       }
    };

    ...
  };
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;
       }
    };

    ...
  };
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;
       }
    };

    ...
  };
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);
  };
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);
  };
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);
  };
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);
  };
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);
  };
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);
  };
Comparisons
Comparisons
                                                         26


$form['payment_information'] = array(
   ...
   '#states' => array(
     'expanded' => array(
        '[name="payment"]' => array('checked' => TRUE)
      ),
   ),
);
Comparisons
                                                         26


$form['payment_information'] = array(
   ...
   '#states' => array(
     'expanded' => array(
        '[name="payment"]' => array('checked' => TRUE)
      ),
   ),                                      ===
);
Advanced Comparisons
                                               27


states.Dependant.comparisons = {
   'RegExp': function (reference, value) {
     return reference.test(value);
   },
   'Function': function (reference, value) {
     return reference(value);
   }
};
Advanced Comparisons
                                               27


states.Dependant.comparisons = {
   'RegExp': function (reference, value) {
     return reference.test(value);
   },
   'Function': function (reference, value) {
     return reference(value);
   }
};
       Prototype name
JSON only allows
strings and numbers   28
:(   29
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}$'),
   ),
),
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}$'),
   ),
),
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;
     }
  };
Advanced Comparisons
                                                      32




'invalid' => array(
   '[name="card_number"]' => array(
     '!value' => array('regex' => '^(d{4}[ -]*){4}$'),
   ),
),
Transitions
State changes
                                               34


◆   Transition an element from one state
    to another

 Direct                  Indirect
◆ Triggered by user     ◆ Triggered by other

◆ Notify listeners        element
                        ◆ Transition element
State changes
                                                  35




$(document).bind('state:checked', function(e) {
  if (e.trigger) {
    $(e.target).attr('checked', e.value);
  }
});
State changes
                                                  35




$(document).bind('state:checked', function(e) {
  if (e.trigger) {
    $(e.target).attr('checked', e.value);
  }
});
State changes
                                                  35




$(document).bind('state:checked', function(e) {
  if (e.trigger) {
    $(e.target).attr('checked', e.value);
  }
});
State changes
                                                  35




$(document).bind('state:checked', function(e) {
  if (e.trigger) {
    $(e.target).attr('checked', e.value);
  }
});
State changes
                                                  35




$(document).bind('state:checked', function(e) {
  if (e.trigger) {
    $(e.target).attr('checked', e.value);
  }
});
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
Future Work
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?
Copy & Paste support   39
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}
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}}
Questions?
                              42




kkaefer.com/2010/states.pdf
    mail@kkaefer.com

More Related Content

What's hot (20)

PDF
องค์ประกอบของดนตรีไทย ม.2 56
อำนาจ ศรีทิม
 
DOC
ใบงาน
tonsocial
 
PDF
เฉลยข้อสอบ หน้าที่พลเมือง ม 3
Teacher Sophonnawit
 
PDF
แบบฟอร์มรายงานโครงงานคอมพิวเตอร์
ณัฐพล บัวพันธ์
 
PDF
ข้อสอบ O net ศิลปะ ม.6 ชุด 1
ParattakornDokrueankham
 
PPT
การบริหารลูกเสือ
English for Thai students
 
PDF
การใช้รถใช้ถนน
wanchai sanprasit
 
PDF
การอ่านวรรณคดี ม.๕[1]
นิตยา ทองดียิ่ง
 
PPTX
โครงสร้างทางสังคม0
Parn Nichakorn
 
DOCX
คีตกวีเอกของไทย
leemeanshun minzstar
 
PDF
แบบทดสอบพร้อมเฉลยรายตัวชี้วัดร่วมสร้างสรรค์ ท.ศ..Docx 40 ข้อ
krupornpana55
 
PDF
วิกฤตการณ์ด้านทรัพยากรธรรมชาติ ทรัพยากรป่าไม้
พัน พัน
 
PDF
ใบความรุ้ การเคารพสิทธิของผู้อื่น ป.2+428+dltvsocp2+54soc p02f 26-4page
Prachoom Rangkasikorn
 
PDF
เทคนิคการใช้ REBT
Website_SEO _Boy
 
PDF
นาม และ อัพยยศัพท์
Prasit Koeiklang
 
PPSX
7 2 การท่องเที่ยวโดยชุมชน
Mint NutniCha
 
PDF
สามัคคีคำฉันท์.Ppt 34
Kittisak Chumnumset
 
DOC
วิธีการพูด
areemarketing
 
PDF
กาพย์เห่เรือ
วิศิษฏ์ ชูทอง
 
องค์ประกอบของดนตรีไทย ม.2 56
อำนาจ ศรีทิม
 
ใบงาน
tonsocial
 
เฉลยข้อสอบ หน้าที่พลเมือง ม 3
Teacher Sophonnawit
 
แบบฟอร์มรายงานโครงงานคอมพิวเตอร์
ณัฐพล บัวพันธ์
 
ข้อสอบ O net ศิลปะ ม.6 ชุด 1
ParattakornDokrueankham
 
การบริหารลูกเสือ
English for Thai students
 
การใช้รถใช้ถนน
wanchai sanprasit
 
การอ่านวรรณคดี ม.๕[1]
นิตยา ทองดียิ่ง
 
โครงสร้างทางสังคม0
Parn Nichakorn
 
คีตกวีเอกของไทย
leemeanshun minzstar
 
แบบทดสอบพร้อมเฉลยรายตัวชี้วัดร่วมสร้างสรรค์ ท.ศ..Docx 40 ข้อ
krupornpana55
 
วิกฤตการณ์ด้านทรัพยากรธรรมชาติ ทรัพยากรป่าไม้
พัน พัน
 
ใบความรุ้ การเคารพสิทธิของผู้อื่น ป.2+428+dltvsocp2+54soc p02f 26-4page
Prachoom Rangkasikorn
 
เทคนิคการใช้ REBT
Website_SEO _Boy
 
นาม และ อัพยยศัพท์
Prasit Koeiklang
 
7 2 การท่องเที่ยวโดยชุมชน
Mint NutniCha
 
สามัคคีคำฉันท์.Ppt 34
Kittisak Chumnumset
 
วิธีการพูด
areemarketing
 
กาพย์เห่เรือ
วิศิษฏ์ ชูทอง
 

Similar to Instant Dynamic Forms with #states (9)

PDF
D8 Form api
Samuel Solís Fuentes
 
PDF
Leveraging Symfony2 Forms
Bernhard Schussek
 
PDF
Dealing With Legacy PHP Applications
Viget Labs
 
PDF
Dealing with Legacy PHP Applications
Clinton Dreisbach
 
PPT
An Elephant of a Different Colour: Hack
Vic Metcalfe
 
PDF
Dutch php a short tale about state machine
Łukasz Chruściel
 
PDF
The new form framework
Bernhard Schussek
 
KEY
Fapi
Steven Rifkin
 
PDF
sfDay Cologne - Sonata Admin Bundle
th0masr
 
Leveraging Symfony2 Forms
Bernhard Schussek
 
Dealing With Legacy PHP Applications
Viget Labs
 
Dealing with Legacy PHP Applications
Clinton Dreisbach
 
An Elephant of a Different Colour: Hack
Vic Metcalfe
 
Dutch php a short tale about state machine
Łukasz Chruściel
 
The new form framework
Bernhard Schussek
 
sfDay Cologne - Sonata Admin Bundle
th0masr
 
Ad

More from Konstantin Käfer (6)

PDF
Front End Performance
Konstantin Käfer
 
PDF
Front End Performance
Konstantin Käfer
 
PDF
Developing JavaScript Widgets
Konstantin Käfer
 
PDF
Front End Performance
Konstantin Käfer
 
PDF
Typography on the Web
Konstantin Käfer
 
PDF
What's New in Web Development
Konstantin Käfer
 
Front End Performance
Konstantin Käfer
 
Front End Performance
Konstantin Käfer
 
Developing JavaScript Widgets
Konstantin Käfer
 
Front End Performance
Konstantin Käfer
 
Typography on the Web
Konstantin Käfer
 
What's New in Web Development
Konstantin Käfer
 
Ad

Recently uploaded (20)

PDF
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
PDF
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PPTX
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
PDF
What Makes Contify’s News API Stand Out: Key Features at a Glance
Contify
 
PPTX
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
PDF
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PDF
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
PPTX
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
PPTX
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
PDF
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
PDF
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PDF
July Patch Tuesday
Ivanti
 
PDF
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
PDF
IoT-Powered Industrial Transformation – Smart Manufacturing to Connected Heal...
Rejig Digital
 
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
What Makes Contify’s News API Stand Out: Key Features at a Glance
Contify
 
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
Exolore The Essential AI Tools in 2025.pdf
Srinivasan M
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
July Patch Tuesday
Ivanti
 
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
IoT-Powered Industrial Transformation – Smart Manufacturing to Connected Heal...
Rejig Digital
 

Instant Dynamic Forms with #states

  • 1. Instant Dynamic Forms with #states
  • 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
  • 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); };
  • 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}$'), ), ),
  • 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
  • 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 [email protected]