JavaScripting

The definitive source of the best
JavaScript libraries, frameworks, and plugins.


  • ×

    Mag.js

    MagJS - Modular Application Glue
    Filed under  › 

    • 🔾22%Overall
    • 156
    • 7.6 days
    • 🕩12
    • 👥3

    Mag.JS - Elegant DOM Bindings

    Intuitive, tiny, fast, JavaScript 2 HTML component templating library.


    Features

    • Changes to state are immediately reflected in the dom by their element matchers. Super crazy fast & 8KB Gzip!
    • Valid HTML templates - No virtual/shadow dom or new templating language!
    • Semantic data binding - Use normal HTML as a template and a related module (plain JS object) as instructions for transpiling/interpolations.
    • Module has a constructor, called once and a viewer called on every change to the state of that module.
    • Collection rendering - No need for hand-written loops. Write templates as a part of the HTML, in plain HTML
    • View logic in JavaScript - No crippled micro-template language, just plain JavaScript functions
    • Native events & attributes, full life cycle events control, Hookin to modify and create custom attributes
    • Built-in observables on existing data structures like objects, arrays for state and props reactivity

    Introduction - Getting started ::: Examples - Tutorials - Api - Tests - Performance

    Mag.JS - Elegant DOM Bindings

    "There is no JavaScript code in the HTML and there is no HTML code in the JavaScript!"

    Hello MagJS!

    Initial dom:
    <div id="hello">
      <h1></h1>
    </div>
    
    Module:
    mag.module('hello', {
      view: function(state) {
        state.h1 = 'Hello Mag.JS!'
      }
    })
    
    Mag.JS dom!:
    <div id="hello">
      <h1>Hello Mag.JS!</h1>
    </div>
    

    View receives 2 arguments, "state" & "props"

    1. state is the DOM element(s) we want to set/get - the element Matchers and their controls
      1. Any change to the state object will trigger a redraw of the view - it is observed.
    2. props is what we want the DOM element(s) to be set to - the data
      1. If the props have changed a new view redraw will run if triggered.
      2. props are passed from the parent and is set by mag.module() or an instance instance (props)
      3. Components can reference others Components in props to pass children elements directly into their output: props.children = mag()

    Dynamic node example with controller

    mag.module(document.body, {
      controller: function() {
        this.h2 = 'Hello MagJS World!';
      }
    });
    

    Controllers are only called once vs views which are re-run on every change to the state. Example - Prevent initial placeholder flicker

    Two way binding

    var Component = {
      controller: function() {
        this.yourName = '';
      }
    }
    

    Just define the default value of the selector to bind.

    Tutorials

    Hello world!

    JSBin - Ternary - Controller - Handler - Merge props - Subscribe - Proxy - No view only controller - Component prop - Parent Child Controller Comps - Componentized - Dynamic templates

    Initial html

    <div id="hello">
      <label>Name:</label>
      <input type="text" placeholder="Enter a name here" />
      <hr/>
      <h1>Hello <span class="name"></span>!</h1>
    </div>
    

    Module:

    mag.module("hello", {
      view: function(state) {
        state.input = {
          _oninput: function(e) {
            state.name = e.target.value
          }
        }
      }
    })
    

    Boilerplates

    Blank JSBin - Blank CodePen - Event Module - JSbin Addons - CodePen Addons (Simple Modal) - Plunker - Plunker Namespace - Plunker Templating - Stateless

    Examples

    Hello world - Hello world, take2 - With dynamic Node

    Components

    Simple Component Container - Reusable Clock component - Master detail - Stateless components perf - Clocks

    Component Composition containment - Component composition specialization

    props.children accessor - Access static children of placeholder from inner module - Specialization with props.children - Shared state

    Reusable Tooltip Component - Controller only

    Simple Wrapped component(HoC) - Reusable Container(HoC) - Switch and Remove Comps

    Simple tabbed content - Initialization

    Toggle Button - Custom Component - Counter Component

    Async

    SetTimeout

    Native promise

    Using fetch to render json data

    Stateless

    Timer - Counter - Interaction with state

    Simple Todos - Stateless counter

    Icons as Components - Deep Nesting (keys, children, create)

    Recursive Error

    App State

    Shared across modules - Synchronous state

    Forms

    Select menu - Linked menus

    Multiple Radio selection - Multiple Select Menu

    Transform Input - Concatenate - To Upper Case

    Slider - Textarea - Remove elements temporarily

    Dynamic Radio Group - Pre-selected - Static group - MagJS Radio Button / Pads

    Basic Math: addition - Basic Math: addition (no auto binding) - Take 3 - Take 4 - V0.12 auto wiring - Video tutorial - Nested data auto wiring

    Auto wiring - select menu - Single form create/edit

    Quiz - Instant validation - Field valid - Simple

    Forms - input handling - AutoComplete w/spinner - Select menu - Select addon

    Forms - passFail component - Search Highlight

    Form & list - model - comps - boilerplate

    Simple messaging component example - Video tutorial - Take 2, w/Reusable child component - Nested components

    Hello world with passFail reusable component - Modal component w/CSS Transitions

    Hello world (proxy w/Config) - Without controller - Without config or controller - Take2

    Events

    Hello array lifecycle event

    Lifecycle Disable & re-render

    Parent Eventing - Node switching - Event handler in Child

    Count - Dynamic reusable counter component - With shared state

    Lists

    Simple List - Reverse list

    More lists

    Sortable List

    Tiny filter

    Filter list - Leaderboard

    Filter list sort

    Filter list components

    List of Lists of items

    Loading and using external data

    Modals

    Tab component - Take2

    Modal component - Reusable Modal Component

    Modal with select menu - Modal Child Component

    Modal with external template shared appState - Alternate with mag - Alternate with create - Addons vs Extends - mag.template

    Forms - composable components - link manager

    Todos Take2 - Take3 - Simple v0.2 - Simple v0.2 component, Take2 - v0.24

    Todos

    Todo Proxy

    Todos (expanded)

    Contacts - Take 2

    Async

    Async - Geo Location

    Infinity scroll

    Animation

    CSS transitions - Animation, FadeIn, FadeOut - Message Fader Component

    Movie plotter service - Plot selection

    Mithril 2 Mag

    Rotate Links - Alternate - Web service - Loader

    Pagination

    Function composition - Todos

    Volunteer form application

    StopWatch

    Shopping Cart

    Ajax Github Api

    REM API - User list

    Simple Application - Tutorial

    React 2 Mag

    Getting started with React example - Affix - Controller default

    Navigation menu - As Component

    Timer - Timer components

    TabList - key components - Without Binding

    TabList module pattern - dynamic children keys - Video Tutorial

    Real-time search Same with different code style - creative Mag.JS!

    FilterableProductTable/static version (Thinking in React tutorial)

    FilterableProductTable (Thinking in React tutorial)

    Occlusion culling - Latest

    Tab state (From Why React is Awesome)

    Board Switcher - Stateless board

    Weather App Take 2

    Comment Box - Video tutorial - Take1, Take 2 - Take3 - MagJS v0.14 - Module Pattern Video tutorial

    Image app with AJAX

    Employee Directory with tiny Router - Take2 - Latest

    News feed with undo state

    Mag Redux implementation

    Mag Redux Async

    Angular 2 Mag

    Todos

    Order form

    Navigation menu

    Switchable Grid

    Phone Gallery

    Contact Manager application - Take 2

    Country App - JSON/Routing

    More advanced examples

    Jasmine Specs

    State Scope

    The scope of a components state/selectors is limited to its template elementId|Node

    A nested component, child of a parent, will not be effected by the parent's state element selectors.

    Statefull Example - Stateless Example

    Statefullness

    When redrawing the view method is called. To maintain statefulness we can use the controller method. Plainly these are default values.

    HTML for below examples:

    <div id="lister">
      <h2></h2>
      <ul>
        <li class="item"></li>
      </ul>
    </div>
    

    Example without controller

    mag.module('lister', {
      view: function(state, props, element) {
      state.item = [1, 2, 3]
      state.title = 'Lister'
        state.h2 = {
          _text: state.title,
          _onclick: function() {
            state.show = state.show ? !state.show : true
            state.item.reverse()
            state.title = 'Gister' + state.show
          }
        }
      }
    })
    

    Example with controller

    mag.module('lister', {
      controller: function(props) {
        this.item = [1, 2, 3]
        this.title = 'Lister'
      },
      view: function(state, props, element) {
        state.h2 = {
          _text: state.title,
          _onclick: function() {
            state.show = state.show ? !state.show : true
            state.item.reverse()
            state.title = 'Gister' + state.show
          }
        }
      }
    })
    

    This link displays both for comparison: http://jsbin.com/fopunubogi/edit?html,output

    You can see that the first one when clicked nothing is changed while the second is dynamic. The reasons is simply because the controller is called once while the view is called on every redraw/action/state change.

    Here's an alternative approach to the above that only uses a view method and no controller for a similar result: http://jsbin.com/xayobawuxo/edit?html,output

    Example with config and without controller

    mag.module("lister", {
      view: function(state) {
        var name1 = 'Yo!',
          name2 = 'Joe!'
        state.h2 = {
          _config: function(node, isNew) {
            if (isNew) {
              state.span = name1
              state.item = [1, 2, 3]
            }
          },
          _onclick: function() {
            state.item.reverse()
            state.span = state.span == name1 && name2 || name1;
          }
        }
      }
    })
    

    This is similar to using a controller or onload. Every element has a _config to act as onload for hookins. It receives 4 arguments:

    1. is the element itself
    2. is a boolean stating if this is attaching or not, first run is always true, subsequent executions are always false
    3. context is an object that can be used to pass values to the method itself on every iterative call
      • a. one available sub method of context is onunload e.g. context.onunload = fun is called when the element is removed from the dom.
        • context.onunload (configContext, node, xpath)
    4. Index- the x path based index of the element

    Simple API

    mag.module (String domElementID|Element Node, Object ModuleDefinition, Optional Object DefaultProperties )

    This is the core function to attach a object of instructions to a dom element, when called it is executed.

    ModuleDefinition is the instructions it can have a controller and a view function.

    var component = {
      view: function (state, props, element) {
      }
    }
    

    view receives three arguments: state, props and element

    • State is the object used to transpile the dom
      • e.g. state.h1 ='Hello' converts the first h1 tag in the element to that value
    • Props is the optional properties object passed to its mag.module definition
    • Element is the node itself whose ID/Node was passed to its mag.module definition

    The controller function has access to the original props as well as all life cycle events, it is only called once.

    var component = {
      controller: function (props) {
        this.didupdate = function (Element, currentProps, instanceId) {}
      },
      view: function (state, props, Element) {
       this.state, this.props, this.element
      }
    }
    

    this in the view has access to the 3 main properties of this.state, this.props and this.element All _on events context is this

    mag (String domElementID|Element Node, Object ModuleDefinition, Optional Object DefaultProperties)

    This is a shortcut method to the internal makeClone function returned by mag.module

    returns a function to run the module and template with given props.

    //Define Component:
    var CounterComponent = {
      view: function(state, props) {
        state.div = "Count: " + props.count;
      }
    }
    
    //Wire it:
    var Counter = mag('counter', CounterComponent);
    
    //Run:
    var Element = Counter({count: state.count});
    
    //Attach to state:
    state.counter = Counter({count: state.count});
    
    //Reflect on the component
    Counter.getProps() ..
    

    Example - Stateless

    mag can also be used to create stateless components
    Which are helpful in constructing the UI.

    Note: There are subtle differences between mag() and mag.module.

    • Skips mag.module setup
      • The major difference is that the normal setup in mag.module is not run on the template node. Therefore, the instance does not exist until it is called.
      • This means there is no pre-loading and caching in the UI and that it only runs on the template clone not the template itself. Example with mag - Direct with mag.module - Same with mag.module - Clone with key
    • Each call to mag() with the same ID/Node reuses it by default
      • mag() defaults to a reference and does not create unique keys for you automatically. Example
      • In order to reuse one instance uniquely you must pass a key via props. Example or use mag.create from the AddOns.
      • Or simply call mag() again. Example - List - Defined - Button Factory

    Lifecycle methods

    There are 8 life cycle events: willload, willgetprops, didload, willupdate, didupdate, isupdate, onbeforeunload, onunload

    They each get the same 3 parameters, their context is the Object no need to bind to this:

    • Element is the original module definition ID element
    • newProps is the active state of the props, since the controller is only called once, the original props parameter contains the original default values.
    • instance ID - Internal Mag.JS ID, can be used for reflection
    • [nextProps (4th argument in willgetprops, contains the next props)]
    • [done() (4th argument in onbeforeunload, function to call when completed)]

    To prevent default from any Life Cycle method- stop continued processing return false

    this.willgetprops = function(node, currentProps, instanceId, nextProps) {
      if (currentProps == nextProps) {
        return false
      }
    }
    

    Optionally, all life cycle methods can also be Object methods

    var Component = {
      willload: function() {
        mag.merge(this.state, this.props)
        this.state.input = {
          _oninput: () => {
            this.state.name = this.state.hello ? ' ' + this.state.hello + '!' : '?'
          }
        }
      }
    }
    

    Try it on JSBin - With Props Update

    Reflection

    Use the live instance or an instance ID

    var instance = mag.module ('myElementId'|Element Node, component);
    

    Returns a function Object that can be used to create a clone of the instance and the instances information such as InstanceID.

    The function object to create a clone instance requires an index/key in its only parameter. When assigned to a state elementMatcher, MagJS does that for you.

    These 8 methods are bound to the exact instance

    getId draw getState getProps clones destroy subscribe - multiple subscribers allowed! returns a remove function rafBounce([Optional Boolean]) - returns Boolean. Use to change the rendering default engine of rAF throttle to rAF debounce

    Inner Reflection

    removeSelfFunc = mag.mod.onLCEvent('didupdate', instanceId, handlerFunc)

    Available on all life cycle methods for any instanceId multiple handlers per event and instanceID are accepted.

    returns a remover function, call to stop the handler from being executed.

    For inner reflection the instanceID is available in all lifecycle methods, Note that this is not the elementID but instead the internal MagJS ID for each component includes clones, example:

    mag.redraw(mag.getNode(mag.getId(instanceID)), instanceID, 1);

    mag.create (String elementID|Element Node, Object ModuleDefinition, Optional Object props) - In the Addons

    Wraps around mag.module to return a reference instance you can call later. The reference function can also over write the defaults given in create usually it will only over write the props

    var myComponent = mag.create('mydomId', {}) // not executed
    
    var instance = myComponent({props:[]}) // executed 
    //add a props.key for a unique component instance or else each call reuses existing.
    
    // instance contains 7 sub methods 
    
    instance.getId();
    //returns instance UID for MagJS
    //Use mag.getId(instanceId) to get the Node id and mag.getNode(ID) to get the Node itself
    
    instance.draw() // redraws that unique instance, wrap in setTimeout for async
    // optional boolean to force redraw i.e. clear the instance's cache instance.draw(true)
    // `returns` a Promise resolved on rAF
    
    instance.getState();
    //Returns a copy of the current state values of that instance - state is async 
    
    instance.getProps();
    //Returns a copy of the current props values of that instance, defaults to bound instance
    
    instance.clones();
    //v0.22.6 returns list of any clones with their associated instanceId, and its own subscribe handler.
    
    instance.subscribe(function(state, props, node, previous){});
    //v0.22.1 assign handler to an instance to be notified on unqiue changes after life cycle event `didupdate`
    
    instance.destroy([Optional RemoveBoolean]);
    //v0.23.5 - if optional remove boolean is true the entire node is removed.
    // this calls all nodes config unloaders and the controllers onunload event which can preventDefault.
    
     instance.rafBounce([Optional  Boolean]);
     //v0.27.2 - returns Boolean flag - used to change the rendering default engine of raf throttle to raf debounce
    
    // instance can be called directly with an index/key to clone the instance, usefull in data arrays
    instance('myUniqueKeyIndex') // Usually not called directly, MagJS will create index when attached to state
    
    // returns the live node clone
    

    Normally there's no need to call the instance constructor function directly. When passed to a state object MagJS will create the index for you with or without a key provided in props.

    state.myELementMatcher = myComponent({
      props: []
    })
    
    // array
    state.myELementMatcher = [myComponent({
      props: [3, 2, 1]
    }), myComponent({
      props: [1, 2, 3]
    })]
    
    //Array object
    state.myELementMatcher = [{
      item: myComponent({
        props: [3, 2, 1]
      })
    }, {
      item: myComponent({
        props: [1, 2, 3]
      })
    }]
    

    JSBin example

    Control redrawing flow

    mag.redraw (node Element, idInstance magId, optional force Boolean)

    initiate a redraw manually

    Optional boolean argument to force cache to be cleared

    returns a Promise which is resolved when the requestAnimationFrame is run.

    mag.begin ( int MagJS uid)

    var instance = mag.module('app', module)
    mag.begin(instance.getId())
    // run some long standing process without redrawing the module no matter what the state changes are
    

    Once called the module will not run a redraw until the corresponding mag.end(id) is called even if instance.draw() is called and even with the optional instance.draw(force true)it will not run.

    mag.end ( int MagJS uid)

    // run the redraw for the module
    mag.end(instance.getId())
    

    This will run the last redraw for the instance assuming the number of begins match the number of ends called.

    If you call mag.begin(id) for the same instance ID twice you must call mag.end(id) the same number of times before it will run the redraw.

    This is typically not necessary especially since MagJS runs updates to the module state very efficiently via the rAF (requestAnimationFrame)

    rAF mag.rafRate, mag.rafBounce AND instance.rafBounce()

    Option to select the requestAnimationFrame rendering strategy.

    There is an optional global mag integer (defaults to undefined) mag.rafRate this will effect the utils.scheduleFlush rAF refresh rate.

    There is an optional global mag boolean (defaults to undefined) mag.rafBounce this will effect the utils.scheduleFlush

    If set to true performance/speed in rendering is enhanced but there can be a loss of smoothness in the dom painting such as jerky rendering.

    You can also set per instance.rafBounce(Boolean) the desired rAF, true is the debounce, false (default) is to throttle.

    Returns the current instance's boolean value.

    Try it on JSBin

    state object

    State is the object that is watched for changes and is used to transpile the related dom parent element ID

    there are 5 ways to reference an element within a module

    • class name
    • tag name
    • data-bind attribute value
    • id
    • or name attribute value

    state.h1 will match the first h1 element within a module (element id or parent node)

    This: <h1></h1>
    With: state.h1 = 'Hello!'
    Makes: <h1>Hello!</h1>
    

    state.$h1 will match all h1s - greedy matcher, default only selects the first

    To change the class for an element

    This: <h1></h1>
    With: state.h1 = { _class: 'header', _text : 'Hello!'} 
    Makes: <h1 class="header">Hello!</h1>
    

    _text and _html are used to fill an elements text node and not as an attribute below.

    any prefix underscore will be an attribute except for _on that will be for events such as

    state.h1 = { _onclick: function() { state.h1='clicked!' } }
    
    • Events are bound to the module instance this.
    • this has this.props, this.state and this.element
    • Events receive arguments in this order f(event, index, node, data)

    Lists

    Dealing with lists are simple and intuitive, including nested lists with dynamic user based values.

    The first list element is used as the template for all new items on the list For example:

    <ul><li class="item-template"></li></ul>
    
    state.li = [1,2]
    

    Will render

    <ul>
      <li class="item-template">1</li>
      <li class="item-template">2</li>
    </ul>
    

    Lists of Objects

    <ul><li class="item-template">People: <b class="name"></b></li></ul>
    
    state.li = [{name:'Joe'},{name:'Bob'}]
    

    Will render

    <ul>
      <li class="item-template">People: <b class="name">Joe</b>
      </li>
      <li class="item-template">People: <b class="name">Bob</b>
      </li>
    </ul>
    

    Nested Lists

    <ul>
      <li class="item-template">Project: <b class="projectName"></b>
        <ul>
          <li class="doneBy">
            <name/>
          </li>
        </ul>
        <tasks/>
      </li>
    </ul>
    
    state['item-template'] = [{
        projectName: 'house cleaning',
        doneBy: [{
          name: 'Joe'
        }, {
          name: 'Bob'
        }],
        tasks: ['wash', 'rinse', 'repeat']
      }, {
        projectName: 'car detailing',
        doneBy: [{
          name: 'Bill'
        }, {
          name: 'Sam'
        }],
        tasks: ['wash', 'rinse', 'repeat']
      }]
    

    Will render

    <ul>
      <li class="item-template">Project: <b class="projectName">house cleaning</b>
        <ul>
          <li class="doneBy">
            <name>Joe</name>
          </li>
          <li class="doneBy">
            <name>Bob</name>
          </li>
        </ul>
        <tasks>wash</tasks>
        <tasks>rinse</tasks>
        <tasks>repeat</tasks>
      </li>
      <li class="item-template">Project: <b class="projectName">car detailing</b>
        <ul>
          <li class="doneBy">
            <name>Bill</name>
          </li>
          <li class="doneBy">
            <name>Sam</name>
          </li>
        </ul>
        <tasks>wash</tasks>
        <tasks>rinse</tasks>
        <tasks>repeat</tasks>
      </li>
    </ul>
    

    Try it on JSBin

    Data binding List with user input

    This is the power and intuitive nature of MagJS. This is what allows for effortless and rapid HTML template prototyping.

    With a minimal amount of code and a single row HTML template we can create a dynamic data table list that automatically stays up to date with dynamic values such as user input.

    We start with our pure HTML template:

    <div id="tickets">
        <h2>How many tickets?</h2>
        <table>
          <tbody>
            <tr class="ticketTypeRow">
              <th class="ticketType">
                Senior
              </th>
              <td class="numberofTickets">
                <input name="quantity" type="number" min="0" maxlength="2" size="1">
              </td>
              <td class="timesX">x</td>
              <td>$ <span class="pricePerTicket"></span>
              </td>
              <td class="equals">= $</td>
              <td class="rowTotal">
                <input name="total" size="8" readonly="readonly" tabindex="-1" value="0.00">
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    

    Next we have our JavaScript data list:

    var defaultProps = {
      ticketTypeRow: [{
        quantity: 0,
        ticketType: 'senior',
        total: 0.00,
        pricePerTicket: 5.99
      }, {
        quantity: 0,
        ticketType: 'adult',
        total: 0.00,
        pricePerTicket: 5.99
      }, {
        quantity: 0,
        ticketType: 'child',
        total: 0.00,
        pricePerTicket: 3.99
      }]
    }
    

    Where this data comes from or how it is loaded does not effect MagJS in any way. It can be async, iframe, web service, push, io sockets etc...

    Normally we would mutate the data in some way through the native Array.map function to return a new Array that is bound by MagJS to our HTML template. In this example we are showing how that is not necessary.

    Next, we create our module.

    var Tickets = {}
    
    Tickets.controller = function(props) {
      // merge the props with the module's state/html
      mag.utils.merge(this, props);
    }
    
    Tickets.view = function(state, props) {
    
      state.$quantity = {
        _onInput: function(event, index, node, data) {
          var total = state.ticketTypeRow[data.index].quantity * state.ticketTypeRow[data.index].pricePerTicket
          state.ticketTypeRow[data.index].total = total
        }
      }
    }
    

    As you can see we are not changing the props data array instead we are merging it directly into our state selectors.

    Lastly we will now load the module for MagJS to do the DOM bindings:

    mag.module("tickets", Tickets, defaultProps)
    

    Try it on JSBin: Movie ticket quantity selection - Nested math input - Nested messaging components

    Attributes

    _html, _text, _on[EVENT], _config->context.onunload

    to not overwrite an existing attribute use:

    state.name._value = state.name._value + ''

    event (e, index, node, data) default context is the target element

    • index is the xpath index of the node -1
    • data is the index data of the parent if in a list (map{path,data,node,index})
    • if promise is returned it will defere redraw until resolved

    Events

    Life cycle events in controller:

    • willload (node, props, instanceID)
    • willgetprops (node, props, instanceID, nextProps)
    • didload (node, props, instanceID)
    • willupdate (node, props, instanceID)
    • didupdate (node, props, instanceID)
    • isupdate (node, props, instanceID)
    • onbeforeunload (node, props, instanceID, done)
    • onunload (node, props, instanceID)

    return false - will skip further execution.

    It will call any onunload handlers in the current module (includes inner modules and _config onunloaders that are currently assigned)

    controller -> this.willload

    Native events: parameters -

    state.matcher._onclick = function(e, index, node, data)

    • the event
    • the x path based 0 index
    • the node itself (default context)
    • the data of the closest parent list item (In nested lists, the first parent with data).

    Config (DOM hookin)

    _config (node, isNew, context, index)

    Available on all matchers to hookin to the DOM

    arguments :

    • node - the element itself

    • isNew is true initially when first run and then is false afterwards

    • context is a empty object you can use to pass to itself

      • context.onunload - will be attached to the current modules onunloaders and called if any lifecycle event triggers a stop by returning false
    • index is 0 based on xpath of the matcher

    mag.hookin (type, key, handler)

    Allows for custom definitions, see examples below Examples: Promise, binding, custom attributes and elements.. Hookins

    Mag.JS AddOns!

    Tiny sub library of reusable simple tools can be found here

    • router
    • ajax
    • Reusable utilities (copy, merge .. )
    • namespace
    • hookins

    mag.namespace (String namespace, [Optional object Context])

    //module library creation with single global namespace / package names
    (function(namespace) {
      var mod = {
        controller:function(props){
        },
        view: function(state, props) {
        }
      }
      namespace.CommentBox = mod;
    })(mag.namespace('mods.comments'));
    
    
    var CommentsComponent = mag.create("CommentBox", mag.mod.comments, props);
    CommentsComponent();
    

    Allows you to easily add new namespaces to your composable components, useful in the module pattern.

    Example of component Module Pattern - Video tutorial

    Custom plugins

    The ability to register handlers for attribute or value trans compilation.

    For example, allow the attribute _className. Register a handler that on every definition will modify both the final attribute name and or the value.

    mag.hookin('attributes', 'className', function(data) {
      var newClass = data.value
      data.value = data.node.classList + ''
      if (!data.node.classList.contains(newClass)) {
        data.value = data.node.classList.length > 0 ? data.node.classList + ' ' + newClass : newClass
      }
      data.key = 'class'
    })
    

    The above is in the MagJS addons library

    Another example

    Hookin when a specific elementMatcher is not found and return a set of element matches

    // hookin to create an element if does not exist at the root level
    mag.hookin('elementMatcher', 'testme', function(data) {
      // data.key, data.node, data.value
    
      var fragment = document.createDocumentFragment(),
        el = document.createElement('div');
    
      el.setAttribute('class', data.key)
      fragment.appendChild(el);
    
      var nodelist = fragment.childNodes;
      data.node.appendChild(fragment)
    
      data.value = nodelist
    })
    

    Other hookins such as key/node value!

    Check out the Hookins

    Extends

    Example of extending the core Mag.JS functionality seamlessly

    Allow for external template loading:

    //Syntax via mag.template extends
    mag.module('template.html', {view: ()});
    mag('template.html', {view: ()});
    
    mag.module({templateUrl: 'template.html', view: ()});
    mag({templateUrl: 'template.html', view: ()});
    

    Check out the core Extends

    Notes

    • config attribute won't be called with inner id element matchers, use other element matcher selectors. Fixed in v0.25.5 Example

    • careful with module instance constructor, can stack overflow if circular reference. Don't call instance from within itself or on state, use separate module. See examples. Fixed: MagJS will throw a Error if it detects recursivity (a instance call within an instance call) - Try it on JSBin

    • object observe support for browsers (v0.22 uses native Proxy to observe)

    <script src="//cdn.rawgit.com/MaxArt2501/object-observe/master/dist/object-observe-lite.min.js"></script>
    
    • Promise support for IE
    <!--[if IE]><script src="https://cdn.rawgit.com/jakearchibald/es6-promise/master/dist/es6-promise.min.js"></script><![endif]-->
    

    Performance

    JSBin - dynamic re-rendering - v0.20.7 - v0.21.3 - v0.22 - Latest - Toggle rAF (Throttle vs Debounce)

    Occlusion culling - v0.22 - Latest - Throttle rAF rate - Debounce

    JSBin - reversing 1000s of rows - v0.22 - Latest - Optimized - Componentized

    Dbmon Repaint rate - v0.23 - Latest

    JsPerf v0.20.2 - JsPerf v0.20.2

    JsPerf v0.20.3 - JsPerf v0.20.3

    JSPerf v0.14.4

    JSPerf v0.14.9

    JSPerf v0.15

    JSPerf v0.15.1

    Inspired By & cloned from

    Mithril.js, Fill.js, React.js, Angular.js, Fastdom

    Show All