Ractive.extend()

edit this page

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 = 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' proxy-tap='close' intro='fadeIn' outro='fadeOut'>
  <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 ( el, event ) {
      if ( !this.modal.contains( event.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.background.clientHeight;
    modalHeight = this.modal.clientHeight;

    verticalSpace = ( backgroundHeight - 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" proxy-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" proxy-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.