1.2.59 • Published 9 years ago

spadmin v1.2.59

Weekly downloads
6
License
ISC
Repository
github
Last release
9 years ago

A wrapper around barebone npm-modules to easify framework-agnostic SPA rest-to-admin interface:

  • Spadmin.page: Single Page Application (SPA) using page
  • Spadmin.template: reactive templates using transparency
  • Spadmin.api: RESTapi-to-object mapping using restglue
  • Spadmin.loader: hipster toploaderbar using nano
  • Spadmin.fetch: fetch http request polyfill fetch
  • Spadmin.bus a stateful pubsub bus using stateful-event
  • Spadmin.registerElement: Custom html-elements using polyfill document-register-element

Usage

<script type="text/javascript" src="dist/spadmin.min.js"></script>
<style type="text/css"> .template { display: none } </style>

<!-- menu -->
<a href="/foo">foo</a> 

<!-- page -->
<div id="page"></div>

<!-- template -->
<div id="foo" class="template">
  <h1 class="title"></h1>
</div>

<script>
  var spadmin = new Spadmin()
  spadmin.init({
    content: '#page',   
    apiurl: 'http://localhost:3000'
  })

  // render template into #page
  spadmin.page('/foo', function(){
    spadmin.renderPage("#foo", {title: "My Title"}) 
  })

  // render remote template into #page
  spadmin.page('/bar', function(){
    spadmin.renderPage("/bar.html", {title: "My Title"}) 
  })
</script>

For a full example see simple.html

Template engine: rendering a menu

<ul id="#menu">
  <div class="items">
    <li><a class="item"></a></li>
  </div>
</ul>

<script>
  spadmin.render( '#menu', [{
    title: "Menu", 
    items: [
      {item: "/user/123",  foo: "#"}, 
      {item: "/users",  foo: "#"} 
    ]
  },{
    items: {
      item: {
        href: function(params){ return this.foo + params.element.innerHTML; }, 
        onmouseover: "blink(this)"
      }
    }
  }])
</script>

Functions:

Spadmin.prototype.init(opts)                             // initializes spadmin
Spadmin.prototype.executeScripts( el )                   // evaluates scripttags found in el.innerHTML
Spadmin.prototype.loadScript(url)                        // loads js-url and evaluates (synchronously)
Spadmin.prototype.renderPage(template, data)             // evaluates transparency-template+data into spadmin.pageid
Spadmin.prototype.render(template, data, targetid,  cb)  // evaluates transparency-template (url or domid) + data into targetid (dom id string)
Spadmin.prototype.renderDOM(domel, data,  targetid,  cb) // evaluates transparency-domtemplate+data into (dom) targetid-string (or replaces domtemplate)
Spadmin.prototype.renderHTML(domel, data)                // evaluates transparency-data into domelement (and returns html-string)
Spadmin.prototype.update(target, opts)                   // monkeypatchable function to control transitions between renderPage()-calls

Page Transitions

You can override the 'update' function like so :

Spadmin.prototype.update = function (target, opts){
  this.bus.publish("update",arguments.true)
  if( opts && opts.show != undefined){
    if( opts.show === false) this.loader.go(0)
    if( opts.show === true ) this.loader.go(100)
  }
  this.bus.publish("update/post",arguments)
  this.bus.state("normal")
}

REST / api

You can easily setup an api like this:

spadmin.api.addEndpoint("foo")

Voila! Now spadmin.api.foo gives you these functions:

getAll(payload, headers)
get(id, payload, headers)
post(id, payload, headers)
put(id, payload, headers)
patch(id, payload, headers)
options(id, payload, headers)

Example:

// request items from api
spadmin.api.foo.getAll().then(function(items){    // GET {apiurl}/foo

})

spadmin.api.foo.get('134').then(function(item){   // GET {apiurl}/foo/134

})

NOTE: pass the apiurl using spadmin.init()

Global headers & hooks could be used for api-specific purposes:

api.headers['Authorization'] = 'Basic '+btoa( me.data.ui.api.login+":"+me.data.ui.api.password )
api.headers['Accept']        = 'application/json'

api.beforeRequest( function(config){
  // do something
})

api.afterRequest( function(config){
  // do something
})

Multiple api's

spadmin.otherapi = new api("http://otherapi.com/v1")
spadmin.otherapi.addEndpoint("foo")
spadmin.otherapi.foo.getAll().then( function(data){
  // voila
})

Don't use spadmin.api.request or superagent directly: you'll lose the sandbox-, beforeRequest() and afterRequest() features.

States / events / channels

An eventbus is handy to easily distribute data/events:

spadmin.bus.state("normal")

spadmin.bus.debug = true                        // prints publish()-calls to console

var fooHandler = spadmin.bus.subscribe("foo", function(data,state){
  if( navigator.onLine && state == "normal") spadmin.bus.state("offline")
})

spadmin.bus.subscribe("cleanup", function(data,state){
  spadmin.bus.unsubscribe(fooHandler)
})

spadmin.bus.publish("foo", {foo:"bar"})

var mybus = new spadmin.bus // or roll your own stateful eventbus

Why state/bus is handy in an applications:

  • unregister/ignore code execution
  • merging datastreams

Example server

$ npm install restful-admin-spa request compression express body-parser
$ ln -s restful-admin-spa/app.js .
$ node app.js

now surf to http://localhost:3000

FP / FRP / Reactive programming

FP/FRP is hot atm, and it promotes (arguably) clean code. Instead of including frameworks like RSJX/Baconjs, Spadmin.fp includes some barebone fp functions to facilitate reactive/streams:

createEventStream('.button', ['click','mouseover'] )(
  chain( 
    pick('target.innerHTML'), 
    haltOnEmptyString, 
    handleButton
  ) 
)

The snippet above will fire the chain on any click or mouseover events from all buttons with classname '.button'

The Functions:

spadmin.fp.chain()                                      // pass functions as arguments and have them returned as one function
spadmin.fp.ncurry(n, f, as)                             // finite curry, ncurry(2, add)(1)(2) will output 3
spadmin.fp.eq(str, path)                                // eq("foo") or eq("foo", 'path.to.value') does stringcompare on function input
spadmin.fp.curry(f)                                     // anonymous curry, curry(add)(1)(2) will output 3                
spadmin.fp.pick(x)                                      // pick value from input
spadmin.fp.createEventStream(selector, event_or_events) // allows barebones DOM eventstreams (think baconjs/rxjs)
spadmin.fp.mapAsync(arr, done, next)                    // async loop over array
spadmin.fp.flipargs(function)                           // returns same function (but with reversed argument-order)
spadmin.fp.delay(ms)                                    // will execute 2nd argument (=function) with setTimeout(function,ms)
spadmin.fp.throttle(ms)                                 // will execute call, but ignore calls within x milliseconds 
spadmin.fp.throttleDelay(ms)                            // will only execute last call, and ignore calls within x milliseconds 

An example:

// your own fp-function
var continueWhenStateIsNormal = spadmin.fp.continueWhenStateIsNormal = function(e){
  if( e && spadmin.bus.state() == "normal" ) return e
}

var handleButton = function(value){
  if( !value ) return
  // do stuff 
}

createEventStream('.button', ['click','mouseover'] )(
  chain( 
    continueWhenStateIsNormal,
    pick('target.textContent'), 
    handleButton
  ) 
)

spadmin.bus.state("normal")     // event will cascade into handleButton
//spadmin.bus.state("offline")  // event will not cascade into handleButton

Offline sandbox

You can fake responses (for offline development etc) in 2 ways, like so:

spadmin.api.addEndpoint("foobar")
spadmin.api.addEndpoint("foo")

spadmin.api.sandboxUrl('/foobar',       {'data':{"foo":true}}  ) 
spadmin.api.sandboxUrl('/myapi',        {'path':"/js/sandbox"} )
spadmin.api.sandboxUrl( /some.*regex/,  "/js/foo" )

spadmin.api.foobar.getAll().then(function(data){    
  // data = {"foo":true}
})

spadmin.api.foo.getAll().then(function(data){    
  // data = /js/sandbox/foo/get.json instead of GET {apiurl}/myapi/foo 
})

NOTE: {apiurl} is passed using spadmin.init()

Custom HTML elements

G Example:

<-- include this after spadmin.min.js -->
<script type="text/javascript" src"js/element/my-element.js"/>

<my-element></my-element>

Now put this inside js/element/my-element.js :

var myElement = function(){
  this.createdCallback          = function(){}
  this.attachedCallback         = function(){
    this.innerHTML        = "Foobar"       // set HTMLElement properties
    this.somevariable  = "Hello world"     // store/reference data 
  }
  this.detachedCallback         = function(){}
  this.attributeChangedCallback = function( name, previousValue, value ){}
}
Spadmin.prototype.registerElement("my-element", new myElement )

Javascript modules / Lazy loading

Instead of cool libs like requirejs, you can just register to the bus which is global state

main.js:

  var spadmin = new Spadmin()
  spadmin.init({
    content: '#page',   
    apiurl: 'http://localhost:3000'
  })

module.js: Spadmin.prototype.bus.subscribe("init/post", function(spadmin){ // do stuff })

  Spadmin.prototype.bus.subscribe("init/post",  function(spadmin){
    // do stuff
  }) 

Initializing & Global state

By default spadmin will have a bus which is initialized in global state. If you don't want this (you are running 2 spadmins etc), then initialize like this:

  var spadmin = new Spadmin()
  spadmin.init({
    bus: new bus({defaultstate:"notloggedin"}) // defaulstate is 'normal' by default
    content: '#page',   
    apiurl: 'http://localhost:3000'
  })

What happened here is that this spadmin got its own bus (instead of the global one)

Philosophy

  • framework-agnostic javascript micro-framework
  • Theme/CSS agnostic so you can roll your own (or use Metronics/AdminLTE/SB Admin)
  • easy setting up api's
1.2.59

9 years ago

1.2.58

9 years ago

1.2.57

9 years ago

1.2.56

9 years ago

1.2.5

9 years ago

1.2.4

9 years ago

1.2.3

9 years ago

1.2.2

9 years ago

1.2.1

9 years ago

1.1.9

9 years ago

1.1.8

9 years ago

1.1.7

9 years ago

1.1.6

9 years ago

1.1.5

9 years ago

1.1.4

9 years ago

1.1.3

9 years ago

1.1.2

9 years ago

1.1.1

9 years ago

1.0.12

9 years ago

1.0.10

9 years ago

1.0.8

10 years ago

1.0.7

10 years ago

1.0.6

10 years ago

1.0.5

10 years ago

1.0.4

10 years ago

1.0.3

10 years ago

1.0.2

10 years ago

1.0.1

10 years ago

1.0.0

10 years ago