Thinking in components

Why, what and how

Alex Peattie

CTO & co-founder Peg

alex@peg.co

Let's talk about

  • Why use components in the first place?
  • What are components (and what are they not)?
  • How do we use them in practice?

Why?

 

 

  • All major front-end frameworks embracing components (Angular, React, Backbone, Vue etc. etc.)
  • Web components to be added to all browsers by ~2017
  • They will make your life as a dev easier 😄!

Addy Osmani's rules for components: FIRST

  • Focused
  • Independent/Isolated
  • Reusable
  • Small
  • Testable
     

Also a great manifesto for why to use components!

  • Changed code and had it break a different part of the application
  • Tried to add a "minor" improvement only having to unravel a bunch of code you didn't understand
  • Run into a brick wall trying to test your front-end
  • Wanted to open-source a cool part of your app

If you've ever

Components might be what you need!

Case study: Peg

10K lines of JS, large "God" controllers

What?

<ng-include src='navbar.html'></ng-include>
<ng-include src='search-area.html'></ng-include>

<section class='results'>
  <ng-include src='creator.html'
    ng-repeat='creator in creators'>
  </ng-include>
</section>

Components != partials

  • Act like normal HTML elements:

     
  • All the code (HTML, CSS & JS) is packaged up together
     
  • They have inputs & outputs, just like methods
<payment-button>Pay Now!</payment-button>

How?

function starsignForBirthday(day, month) {
  // some clever stuff
  return starsign;
}
starsignForBirthday(24, 8) // "Virgo"
starsignForBirthday(1, 1) // "Capricorn"
starsignForBirthday(10000) // Error
<input id='cc' type='number' pattern='[0-9]{16}'>
<button class='payment-button disabled' type='submit'>
.payment-button {
  background: blue;

  &.success {
    background: green;
  }

  &.failed {
    background: red;
  }

  &.disabled {
    cursor: disabled;
    background: grey;
  }
}
$('#cc').on('keydown', function() {
  var isValid = $(this).valid()
  $('.payment-button').toggleClass('disabled', !isValid)
})

$('.payment-button').on('click', function() {
  var ccNum = $('#cc').val()
  $('.payment-button').text('Paying')

  $.post('https://api.stripe.com/pay', { num: ccNum }, function() {
    $('.payment-button').text('Paid').addClass('success')
  }, function() {
    $('.payment-button').text('Failed').addClass('failed')
  })
})

Credit card valid

No

Yes

Payment
status

Waiting

Processing

Success

Failure

function starsignForBirthday(day, month) {
  return starsign;
}
<payment-button credit-card='...' payment-status='...'>
</payment-button>
paymentButton = {
  bindings: {
    cardNumber: '<',
    paymentStatus: '<'
  }
}

this.paymentStatus = this.paymentStatus || 'waiting'
this.pay = function() {
  this.paymentStatus = 'processing'

  $http.post('http://api.stripe.com/pay', { num: this.cardNumber },
  function() {
    this.paymentStatus = 'success'
  }, function() {
    this.paymentStatus = 'failed'
  })
}

angular.component('<payment-button>', paymentButton) // <- pseudocode
<button ng-class='{ disabled: !component.creditCard.$valid }'
  ng-switch='component.paymentStatus' ng-click='component.pay()'>
  <span ng-switch-when='waiting'>Pay</span>
  <span ng-switch-when='processing'>Paying</span>
  <span ng-switch-when='success'>Paid</span>
  <span ng-switch-when='failure'>Failure</span>
</button>
  • Focused ✅
  • Independent/Isolated
  • Reusable
     
  • Small
  • Testable
     
<payment-button></payment-button>
<payment-button payment-status='success'></payment-button>

expect($('payment-button').text()).toEqual('Paid')

Credit card valid

No

Yes

Payment
status

Waiting

Processing

Success

Failure

<button ng-class='{ disabled: !component.creditCard.$valid }'
  ng-switch='component.paymentStatus'>
$('#cc').on('keydown', function() {
  var isValid = $(this).valid()
  $('.payment-button').toggleClass('disabled', !isValid)
})
<button
  ng-class='{
    disabled: !component.creditCard.$valid && component.paymentStatus === "waiting"
  }'
  ng-switch='component.paymentStatus'>
$('#cc').on('keydown', function() {
  var isValid = $(this).valid()

  // Horrible hack ⬇
  var paymentWaiting = $('.payment-button').text() === "Pay"
  $('.payment-button').toggleClass('disabled', !isValid && paymentWaiting)
})

$('.payment-button').on('click', function() {
  $(this).removeClass('disabled')
})
<favourite-button creator='zoella'>
  <button-toggler toggled='creator.favourited'>
    <button ng-click='add()'>Favourite</button>
    <button ng-click='remove()'>Favourited</button>
  </button>

  <notification position='bottom-center' notify-status='request.status'
   notify-status-when='200'>
    {{ creator.name }} has been favourited
  </notification>

  <notification position='bottom-center' notify-status='request.status'
   notify-status-when='4xx'>
    Oops something went wrong!
  </notification>
</favourite-button>
<favourite-button creator='zoella'>
  <button-toggler toggled='creator.favourited'>
  </button>

  <notification position='bottom-center'
   notify-status='request.status'
   notify-status-when='200'>

Favourite button
Responsible for favouriting/unfavouriting creators
Inputs: the creator in question

 

Button toggler
Responsible for toggling the visibilty of its child buttons
Inputs: is it currently toggled?

Notification
Responsible for notifying the user about successful/failed HTTP requests
Inputs: the HTTP request status it's watching, which code triggers the notification to appear

Conclusion

  • Components are the future
  • They help you avoid spaghetti code
  • Good components should be FIRST (focused, isolated, reusable, small, testable)
  • Think about components like methods (thinking about inputs and outputs)
  • Keep components small by making them work together (composing them)

Thank you!

any questions?

PS - slides are online at:

https://slides.com/alexpeattie/components

Thinking in components

By alexpeattie

Thinking in components

"Reusable component" has become a bit of buzzword - it's the philosophy driving popular frameworks like React, Angular 2 and Vue. And they're set to become part of a web devs' bread-and-butter with the imminent arrival of Web Components to HTML5 (ETA 2017). But what are components, and why are they useful? How do we shift our minds to start "thinking in components"? I'll look at these questions in the talk, drawing on my experience moving our large-scale app (peg.co) to a component-based approach.

  • 2,146