angular-formly

JavaScript powered forms

images provided for your amusement by characterslist.com

by Kent C. Dodds

@kentcdodds

@kentcdodds

Kent! Don't forget to Selfie!

Time to wake up

@kentcdodds

Air Squats

Kent C. Dodds

@kentcdodds

@kentcdodds

What
Is
Formly?

@kentcdodds

Change this

To this

<form name="vm.userForm" ng-submit="vm.onSubmit(vm.user)">
      <div class="form-group" has-error="vm.userForm.firstName">
        <label for="firstName">First Name</label>
        <input type="text" ng-model="vm.user.firstName" name="firstName" class="form-control" id="firstName" placeholder="First Name" required>
        <div my-messages="vm.userForm.firstName"></div>
      </div>
      <div class="form-group" has-error="vm.userForm.lastName">
        <label for="lastName">Last Name</label>
        <input type="text" ng-model="vm.user.lastName" name="lastName" class="form-control" id="lastName" placeholder="Last Name" required>
        <div my-messages="vm.userForm.lastName"></div>
      </div>
      <div class="form-group" has-error="vm.userForm.email">
        <label for="emailAddress">Email Address</label>
        <input type="email" ng-model="vm.user.email" name="email" class="form-control" id="emailAddress" placeholder="Email address" required>
        <div my-messages="vm.userForm.email"></div>
      </div>
      <div class="form-group" has-error="vm.userForm.confirmEmail">
        <label for="emailAddressConfirm">Confirm Email Address</label>
        <input type="email" ng-model="vm.user.confirmEmail" name="confirmEmail" must-match="vm.user.email" class="form-control" id="emailAddressConfirm" placeholder="Confirm Email address" required>
        <div my-messages="vm.userForm.confirmEmail"></div>
      </div>
      <div class="form-group" has-error="vm.userForm.password">
        <label for="password">Password</label>
        <input type="password" ng-model="vm.user.password" name="password" class="form-control" id="password" placeholder="Password" required>
        <div my-messages="vm.userForm.password"></div>
      </div>
      <div class="form-group" has-error="vm.userForm.confirmPassword">
        <label for="passwordConfirm">Confirm Password</label>
        <input type="password" ng-model="vm.user.confirmPassword" must-match="vm.user.password" class="form-control" id="passwordConfirm" placeholder="Password" required>
        <div my-messages="vm.userForm.confirmPassword"></div>
      </div>
      <div class="radio-group" has-error="vm.userForm.genderRadios">
        <label>Your Gender?</label>
        <div class="radio">
          <label>
            <input type="radio" ng-model="vm.user.model" name="genderRadios" id="genderRadios1" value="male" required>
            Male
          </label>
        </div>
        <div class="radio">
          <label>
            <input type="radio" ng-model="vm.user.model" name="genderRadios" id="genderRadios2" value="female" required>
            Female
          </label>
        </div>
        <div class="radio">
          <label>
            <input type="radio" ng-model="vm.user.model" name="genderRadios" id="genderRadios3" value="unspecified" required>
            Prefer not to say
          </label>
        </div>
        <div my-messages="vm.userForm.genderRadios"></div>
      </div>
      <div class="checkbox" has-error="vm.userForm.agree">
        <label>
          <input type="checkbox" name="agree" ng-model="vm.user.agree" required> Do you sign your soul away to our TOS?
        </label>
        <div my-messages="vm.userForm.agree"></div>
      </div>
      <button type="submit" class="btn btn-default" ng-disabled="vm.userForm.$invalid">Submit</button>
    </form>
<form name="vm.userForm" ng-submit="vm.onSubmit(vm.user)">
  <formly-form model="vm.user" fields="vm.fields" form="vm.userForm">
    <button type="submit" class="btn btn-default" ng-disabled="vm.userForm.$invalid">
      Submit
    </button>
  </formly-form>
</form>

... and a little JS :-)

@kentcdodds

It brings:

  1. Maintainability
  2. Consistency
  3. Flexibility
  4. Simplicity
  5. Sanity
  6. etc....ity :-)

​To forms in AngularJS Apps

@kentcdodds

But I
❤️
HTML!

@kentcdodds

No
You
Don't...

@kentcdodds

<input ng-model="vm.user.firstName" />

@kentcdodds

<div>
  <label>First Name</label>
  <input ng-model="vm.user.firstName" />
</div>

@kentcdodds

<div>
  <label for="name">First Name</label>
  <input ng-model="vm.user.firstName" id="name" />
</div>

@kentcdodds

<div class="form-group">
  <label for="name">First Name</label>
  <input ng-model="vm.user.firstName"
         id="name"
         class="form-control"
  />
</div>

@kentcdodds

<div class="form-group">
  <label for="{{::vm.id}}">{{vm.label}}</label>
  <input ng-model="vm.model"
         id="{{::vm.id}}"
         class="form-control"
  />
</div>
angular.module('app').directive('nlInput', function() {
  var unique = 0;
  return {
    restrict: 'E',
    template: require('./nl-input.html'), // webpack ftw
    replace: true,
    scope: {
      label: '@',
      model: '='
    },
    bindToController: true,
    controllerAs: 'vm',
    controller: function() {
      var vm = this;
      vm.id = `field${Math.floor(Math.random() * 999)}${unique++}`; // es6 ftw
    }
  };
});

@kentcdodds

<div class="form-group">
  <label for="{{::vm.id}}">{{vm.label}}</label>
  <input ng-model="vm.model"
         id="{{::vm.id}}"
         class="form-control"
         ng-required="vm.required"
  />
</div>
angular.module('app').directive('nlInput', function() {
  var unique = 0;
  return {
    restrict: 'E',
    template: require('./nl-input.html'), // webpack ftw
    replace: true,
    scope: {
      label: '@',
      model: '='
    },
    bindToController: true,
    controllerAs: 'vm',
    controller: function() {
      var vm = this;
      vm.id = `field${Math.floor(Math.random() * 999)}${unique++}`; // es6 ftw
    },
    link: function(scope, el, attrs) {
      scope.vm.required = attrs.hasOwnProperty('required');
    }
  };
});
<div class="form-group">
  <label for="{{::vm.id}}">{{vm.label}}</label>
  <!-- but let's be honest... this is getting ridiculous (-‸ლ) -->
  <input ng-model="vm.model"
         id="{{::vm.id}}"
         class="form-control"
         ng-required="vm.required"
         ng-disabled="vm.disabled"
         ng-maxlength="vm.maxlength"
         ng-minlength="vm.minlength"
  />
</div>
angular.module('app').directive('nlInput', function() {
  var unique = 0;
  return {
    restrict: 'E',
    template: require('./nl-input.html'), // webpack ftw
    replace: true,
    scope: {
      label: '@',
      model: '='
    },
    bindToController: true,
    controllerAs: 'vm',
    controller: function() {
      var vm = this;
      vm.id = `field${Math.floor(Math.random() * 999)}${unique++}`; // es6 ftw
    },
    link: function(scope, el, attrs) {
      // I'm not a fool, let's JavaScript here
      var extraAttrs = ['required', 'disabled', 'maxlength', 'minlength'];
      angular.forEach(extraAttrs, attr => {
        scope.vm[attr] = attrs.hasOwnProperty(attr);
      });
    }
  };
});

Oh, yeah, validation please?

Sure, yeah... cool... uh...

Give me a sec to write a directive and update everything...

done!

@kentcdodds

So uh... yeah, we're changing... everything

@kentcdodds

@kentcdodds

It doesn't
have to be
this way...

@kentcdodds

angular-formly

JavaScript powered forms for AngularJS

[coding forms without angular-formly] leads to anger

anger leads to hate

hate... leads to suffering

 

- Yoda (presenter liberties taken)

@kentcdodds

Live-code time

(ó ì_í)=óò=(ì_í ò)

@kentcdodds

Expression Properties

Total Control

@kentcdodds

Auto-magical attributes

@kentcdodds

Validation

consistent & simple

@kentcdodds

Wrappers

DRY HTML

@kentcdodds

Custom Templates

Dead simpler than dead simple

@kentcdodds

But Wait! There's More!

but there's not enough time...

bit.ly/ngFormly

@kentcdodds

Thanks!

Give it a go!

@kentcdodds

Feedback please: bit.ly/ng-formly-feedback