Ractive.extend()

Ractive is more than a library; it is a platform. It makes it easier to create reusable, but specialised, blocks of functionality such as to-do lists, slideshows, bar charts, text editors, and so on.

Default options can also be provided using Ractive.extend(). While the result is a fully instantiable on its own, the main value is that they can then be used as components within your templates:

var MyRactive = Ractive.extend({
  template: 'I can be instantiated _or_ be a component!'
});
var ractive = new MyRactive({
  el: 'container'
});
var ractive2 = new Ractive({
  el: 'container2',
  template: '<widget/>',
  components: {
    widget: MyRactive
  }
});

Though it's technically incorrect, we'll refer to these as subclasses of the Ractive base class. In order to create them, we use Ractive.extend().

Here's a simple example - we'll create a Modal subclass which has a default template, and which always stays centred on the page:

.modal-background {
  position: fixed;
  top: 0; left: 0; width: 100%; height: 100%;
  background-color: rgba(0,0,0,0.5);
  padding: 0.5em;
  text-align: center;
  -moz-box-sizing: border-box; box-sizing: border-box;
}
.modal-outer {
  position: relative;
  width: 100%;
  height: 100%;
}
.modal {
  position: relative;
  background-color: white;
  padding: 2em;
  box-shadow: 1px 1px 3px rgba(0,0,0,0.1);
  margin: 0 auto;
  display: inline-block;
  max-width: 100%;
  max-height: 100%;
  overflow-y: auto;
  -moz-box-sizing: border-box; box-sizing: border-box;
}
.modal-button {
  text-align: center;
  background-color: rgb(70,70,180);
  color: white;
  padding: 0.5em 1em;
  display: inline-block;
  cursor: pointer;
}
<div class='modal-background' on-tap='close' intro='fade' outro='fade'>
  <div class='modal-outer'>
    <div class='modal'>
      {{>modalContent}}
    </div>
  </div>
</div>
// Create our Modal subclass
Modal = Ractive.extend({
  // by default, the modal should sit atop the <body>...
  el: document.body,
  // ...but it should append to it rather than overwriting its contents
  append: true,
  // all Modal instances will share a template (though you can override it
  // on a per-instance basis, if you really want to)
  template: modalTemplate,
  // the init function will be called as soon as the instance has
  // finished rendering
  init: function () {
    var self = this, resizeHandler;
    // store references to the background, and to the modal itself
    // we'll assume we're in a modern browser and use querySelector
    this.outer = this.find( '.modal-outer' );
    this.modal = this.find( '.modal' );
    // if the user taps on the background, close the modal
    this.on( 'close', function ( event ) {
      if ( !this.modal.contains( event.original.target ) ) {
        this.teardown();
      }
    });
    // when the window resizes, keep the modal horizontally and vertically centred
    window.addEventListener( 'resize', resizeHandler = function () {
      self.center();
    }, false );
    // clean up after ourselves later
    this.on( 'teardown', function () {
      window.removeEventListener( 'resize', resizeHandler );
    }, false );
    // manually call this.center() the first time
    this.center();
  },
  center: function () {
    var outerHeight, modalHeight, verticalSpace;
    // horizontal centring is taken care of by CSS, but we need to
    // vertically centre
    outerHeight = this.outer.clientHeight;
    modalHeight = this.modal.clientHeight;
    verticalSpace = ( outerHeight - modalHeight ) / 2;
    this.modal.style.top = verticalSpace + 'px';
  }
});
// We can now instantiate our modal
basicModal = new Modal({
  partials: {
    modalContent: '<p>This is some important content!</p><a class="modal-button" on-tap="okay">Okay</a>'
  }
});
basicModal.on( 'okay', function () {
  this.teardown();
});

Subclasses can themselves be extended. Lets say we wanted to be able to create lots of modals similar to this one:

BasicModal = Modal.extend({
  partials: {
    modalContent: '<p>This is some important content!</p><a class="modal-button" on-tap="okay">Okay</a>'
  },
  init: function ( options ) {
    // wherever we overwrite methods, such as `init`, we can call the
    // overwritten method as `this._super`
    this._super( options );
    this.on( 'okay', function () {
      this.teardown();
    });
  }
});
basicModal = new BasicModal();

The init method is able to call this._super because when Ractive detects (with regex, essentially by calling init.toString()) that a child method requires this._super it wraps it in a function that sets this._super to the parent method of the same name. This wrapping only happens where necessary.

Ractive.extend() can be used with multiple prototypes, which will be applied from left to right.

var Foo = Ractive.extend({
    template: 'I am effectively overridden'
  }, {
    template: 'I win'
  });