Neck is a library that adds number of features to Backbone.js:
- Event driven data-binding between controller and view (no dirty checking)
- Flexible dependeny injection module working with CommonJS out of the box
- Support for any JavaScript template engine
- Over a dozen helpers for view logic:
- collections/lists management
- showing/hiding elements
- triggering events
- routing (by refrencing and url based) to nested views (working like Android Activity Stack)
- and more...
Library is inspired (in convention and code) by frameworks Angular and Batman. Neck is not separete framework. It extends Backbone.js functionality. You can use it with many other plug-ins and libraries created for Backbone.js.
Simple todo application could looks like this:
Controller:
class MyController extends Neck.Controller
constructor:->
super
@scope.models = new Backbone.Collection()
View:
div(ui-neck="'MyController'")
ul(ui-collection="models")
li
span(ui-value="item.get('name')")
button(ui-event-click="item.destroy()") remove
input(ui-bind="newItemName")
button(
ui-event-click="models.add({name: newItemName}); newItemName = ''"
ui-attr="{ disabled: !newItemName }"
) Add todo
bower install --save neck
Library depends on Backbone.js and Underscore.js. They have to be included before Neck.
<script src="/path/to/underscore.js"></script>
<script src="/path/to/backbone.js"></script>
<script src="/path/to/neck.js"></script>
- Current version of all modern browsers, IE9+
To take full potencial of Neck, You should use it with CoffeeScript and one of JavaScript template engines.
I encourge to use Neck on Brunch skeleton, which can be great starting point for working with Neck. It includes tools like Brunch building tool, CommonJS, CoffeeScript, Stylus and Jade. It is working example of simple Neck application.
Extends Backbone.View
, controls data passed to template. It can be also container for callback actions
from view (Initialized template with controller is called as a view).
Neck.Controller
can be initialized directly or by ui-neck
helper (read ui-neck
section):
class MyController extends Neck.Controller
template: 'myControllerTemplate'
constructor:->
super
# Your setup actions
@$el.addClass 'myController'
myController = (new MyController el: $('#myElement')).render()
When you initialize controller directly, you have to call render
method to start parsing process and connect
template to controller el
object.
Everything that you set inside scope
property will be available in a view.
class MyController extends Neck.Controller
constructor:->
super
@scope.property = 'Some text'
@scope.something = 'Some other text'
h1 My View
p!= property
ul
li!= something
li(ui-value="something")
You can use scope
properties directly and put them in template (they will refresh on every render
call),
or put them into helpers
using accessors.
scope
is passed as first argument to templating function. Many templating engines use passed object as context (like Jade). Then scope
properties can be accessed by thier name directly.
Properties can be predefined (you can omit constructor
method):
class MyController extends Neck.Controller
scope:
property: 'first property text'
someAction: ->
console.log @scope.property # Will write 'first property text'
scope
has special _context
and _resolves
properties. They should not be touched or used directly.
Controller can be initialized with parent
parameter which should points to other Neck.Controller
instance:
myController = new Neck.Controller parent: ohterController
Child controller inherits scope
property from parent (as its prototype
). Then child controller will
have access to parent scope
properties by prototype chain (This is used by helpers that inherits scope from main controller).
Use it to safely pass arguments from one controller to another.
For example:
myController = new Neck.Controller(params: myParam: 'some text')
Consider using params
with higher priority than inheriting scope
. Passing params to
controller is safer and more useful. For example you can create generic contollers
that react to given params.
Params are not pushed to scope
property. There is no scope.params
in controller.
If you want to some param to be in view, You have to set it in scope
when initializing controller:
class myController extends Neck.Controller
constructor:->
super
@scope.myProperty = @params.passedObject
Property used for rendering body of view. Expected values:
string
: id or template body for dependency injector (read Neck.DI section). Injector should returnfunction
(asfunction
value below) orstring
containing template.boolean
: When set tofalse
controllerel
body will be used as template but not removed from DOM. When set totrue
DOM tree is empty before rendering. This is important for example when helper will reuse own body to create list of elements (read ui-collection or ui-list sections).function
: Set withcontroller.scope
property as first argument. Should returnstring
value containg template body
When is not set (default undefined
value) template is not used, but can be overwritten by options pushed to constructor
method:
# This will work. template will be set to 'myTemplatePath'
myController = new Neck.Controller template: 'myTemplatePath'
class Controller extends Neck.Controller
template: 'myWorkingTempalatePath' # can be also false / true / templateBody[string]
# This won't change template, and it still will be 'myWorkingTemplatePath'
myController = new Controller tempalte: 'myTemplatePath'
Some helpers use this functionality to automatically connects controller with corresponding view (RoR way).
Initializing controller with set template
param will point the same path for view as for controller
(but with template
type for Neck.DI
).
Controller el
object (read Backbone.View
docs) for default is created as empty div
. Template will be
placed inside this div
element. divWrapper
gives you option to change this behavior. If it is set to false
,
el
object will be replaced by view and appended to DOM without div
wrapper.
It sets parsing view starting point. Parsing can be started from root node (when set to true
) or from direct
children of root node (when set to false
).
Injector property sets which dependency injector object is used when fetching controllers, helpers and templates. This property is inherited by controllers and helpers through whole application. You do not need to set it in every controller/helper, only root controller should have this property set. If You initialize application by helpers, You do not have to set it at all directly in controller.
Constructor method reads pushed params
object and use properties: parent
, params
, template
and all that use
Backbone.View
(like el
property).
They are used to set instance properites. Other properties from given object are ignored.
Method fetches template with scope
property, executes template function, parses view for helpers and pushes it to
el
controller. This method usually do not have to be called directly. For example ui-yield call render
after creating new view. However, if you have to refresh view, you can call render
method from controller.
This method provides two public events: render:before
and render:after
. After render event is triggered when view
is placed into DOM. This ensures that calling measurement methods will return proper values.
Triggers callback
action when one or more scope
properties has changed. Expected arguments:
keys
[string]: space separated list of evaluating properties ofcontroller.scope
objectcallback
[function]: called every time when one of keys has changed; called with all keys as arguments in function,this
set tocontroller
instance wherewatch
is set.initCall
[boolean]: Default value:true
. If set tofalse
your callback will not be triggered when watching is initialized.
For Backbone.Model
method listen also to change
event. For Backbone.Collection
its add
,
remove
and change
events.
When watch is set first time, that scope
property is changed to getter/setter property. Then every time
when value of this property change, callback will be invoked:
@scope.one = 'Something'
@watch 'one', (value)-> console.log "One now is: #{value}"
# This will trigger callback with new value
@scope.one = 'New thing'
Deep properties are not supported. Do not use it with keys set to something like 'one.two.three'.
This method do not polyfills Object.observe
method. Changing deep properties directly in controller will not
trigger callback. For more complex structures use Backbone.Model
and 'Backbone.Collection`.
(There is great library Backbone-relational.js for models with relations).
Hovewer, helper accessor triggers proper changes on deep properties (thanks to its mapping proccess). It is recommended to rely more on helpers than using watching method directly in controller.
Usually called by library, accessors or helpers, but can be called manually to trigger watch
callback
for scope properties. Should be called with key as string
containing contorller.scope
property name.
Extends Neck.Controller
. Used to create view logic and controller data manipulation.
View of controller (el
object) is parsed when controller is rendered. Going down in tree, nodes are read
looking for specific attributes - with ui-
prefix. Name after prefix is traslated from dashed to camelCase
and checked, if helper definition exists - firstly in built-in helpers container or when there is no helper -
pushed to Neck.DI
with helper
type.
div(ui-some-helper="someValue", some-helper-property="'test'")
On initializing helper, main attribute body is used. Initializer can take also other attributes. To avoid
duplicates use part after prefix and some name. For example ui-some-helper
should read other attributes
like some-helper-property
or some-helper-other-thing
. This properites are called accessors.
Helper inherits scope
from controller. Controller scope
properties can be accessed from helper scope
.
Accesors are automatically pushed to helper scope
. Main attribute (defining helper) as scope._main
.
Other attributes are translated from dashed to camelCase (from some-helper-property
to scope.someHelperProperty
).
Attributes body is interpreted as JavaScript code in context of parent controller scope
.
This means that someValue
is translating into controller.scope.someValue
. Interpreting method is called every
time accessor value is read or written. Value of accesor is dynamic and depends of actual state of controller scope
.
As body is interpreted as JavaScript, you can write statements as: a + b + 'someText'
or someAction(a + b)
.
In this example a
and b
will be interpreted in controller scope
context. Also method someAction
will be called
as controller.scope.someAction
.
Not initialized controller.scope
properties will be set to undefined
to prevent JavaScript runtime error.
Deep properties will be set as chain of simple objects with last attribute set as undefined
.
For example one.two.three
if not present in controller.scope
will be created as scope.one = { two: { three: undefined }}
.
Creating not initialized properties work onlty for objects. If you use array, it has to be defined in controller.
Complex statements are not supported like: if (a) { someAction(b)}
. You should understand accessor body as write
or read inline statement. However, you can use ;
line separator to create more than one call. It is useful for action triggers.
Reading accessors will use returned value form first statement. Also inline conditions are supported, like a ? b : c
.
Adding special character @
gives possibility to call controller directly (@someValue
will be interpreted
as controller.someValue
). This is useful to call actions from controller inside helper. In below example will be triggered
action written directly in controller :
a(ui-event-click="@controllerAction()")
If you want to call some global value, like window.something
write it with this.
prefix (this.something
).
span(ui-value="this.moment().fromNow()")
Helper accessors are always interpreted as JavaScript. If helper require string you can pass variable or statement.
For example ui-route
uses main accessor as controller ID, but you can put there scope
property that value will be used as controller ID:
@scope.myRoute = 'someController'
div(ui-route="myRoute", ...)
This is general behavior. It will work with any accessor.
Accessor attribute body is scanned for controller's scope
properties and all are pushed
to special scope._resolves
container. When any property changes, refresh event of accessor will be triggered.
class Helper exteds Neck.Helper
constructor:->
super
@watch '_main', (value)->
console.log "new value of helper: #{value}"
div(ui-helper="2 + someProperty + one(thirdProperty) + 'otherValue' + secondProperty")
In this example scope._main
depends on someProperty
, secondProperty
and thirdProperty
of controller.scope
.
Changing deep properties in called function will not trigger accessor change (root properties has own getter/setter and triggers changes).
You have to call function with deep property as parameter, for example: someAction(property.deepProperty)
.
You can also manually invoke `@apply('property.deepProperty') inside controller method.
Mapping works with deep properites:
div(ui-helper="someObject.someProperty.otherProperty")
In this case mapping will add three properties to be watched: someObject
, someObject.someProperty
and
someObject.someProperty.otherProperty
.
Other helper can change this value: someObject.someProperty.otherProperty = "newValue"
and then
it will trigger referesh. When other helper or controller will change someObject
or helper change
someObject.someProperty
, it also will trigger refresh on that accessor.
To avoid problems with changing orderPriority
and template
properties on already initialized helper,
they are taken from helper prototype
. You have to set them before initializing:
class MyHelper extends Neck.Helper
# Good way
template: true
orderPriority: 1
constructor:->
super
# Bad way - parsing will take `true` value of `template`
@template = false
Helpers with templates can have own nested helpers. Using @
in accessor body of all helpers in chain points to root controller.
Array containing list of attributes that will be created as accessors of helper. As written above they should be created with helper name as prefix (but it is not mandatory).
class SuperThing extends Neck.Helper
attributes: [ 'superThingOne', 'superThingTwo']
constructor: ->
super
console.log @scope.superThingOne, @scope.superThingTwo
div(ui-super-thing="'great stuff'", super-thing-one="'yes'", super-thing-two="'no' + ' or ' + 'yes'")
Main attribute (as scope._main
) is always set, even if attributes
property is undefined
.
Helpers are initialized as they are ordered inside node. Chrome and Firefox browsers preserve order of node attributes. Unfortunately Internet Explorer does not. If you plan to write mobile apps for Android and iOS you can skip this property.
When orderProperty
is set to higher value helper will be initialized before other helpers in the same node.
Extends Neck.Controller
, adds url routing for navigation. Should be used only once for application,
usually as root controller. When App
is initialized, it checks routes and starts Backbone.history
.
Routes are connected with yields, created by ui-yield
helper.
List of application routes. Routes can be created in different ways:
class App extends Neck.App
routes:
# Without 'yields' - controller points to 'main' yield
# Shortes way - set as string
'someUrl': 'someController'
# Full way - set as object
# with passed options
'someUrl/otherUrl':
controller: 'someController'
params:
one: 1
two: 2
refresh: false
replace: false
# With multi yields, set `yields` container with yields names
'someUrl/:id':
yields:
main:
controller: 'someController'
modal: 'otherController'
extra: false
This three ways can be mixed. Setting yield to false
will clear it when url will be reached. Params will be passed as controller.params
.
refresh
and replace
properties are described in ui-yield
section.
Routes are passed to Neck.Router
instance module which extends Backbone.Router
. It has all functionality
of its parent as setting url params. Different is with passing params to controller - they are passed as object
with named parameters from url and query parameters (it is prepared to supports
backbone-query-parameters):
# With set url like: '/users/:id' and going to '/users/1?title=asd'
# will initialize controller with object
@params =
id: '1'
title: 'asd' # Only if you add backbone-query-parameters plugin
When route is reached app.scope._state
is set with actual route name. Sometimes it can be useful to check in what state is application.
Options passed to Backbone.history.start
method. Read Backbone.history
docs for more information.
Container for dependency injection modules. Every time when application needs controller
, helper
or template
dependency injection load
method is called:
# Loading controller
@injector.load someControllerID, type: 'controller'
load
method should take first argument as ID of fetching object, second arguments is options object. For now library calls
that method with options set to type: 'controller|helper|template'
.
Neck
supports out of the box two modules: Neck.DI.globals
and Neck.DI.commonjs
. For default Neck
uses globals
.
You can write your own manager (or extends existing) to work with your project setup. When you use ui-neck
helper put your
manager into Neck.DI
container.
ID passed to load
method will be interpreted as global variable path, someController
will be read as window.someController
.
It can be object chain like someObject.someController
. Using globals
is very easy - to define something, put it into
global scope:
class window.MyController extends Neck.Controller
...
div(ui-neck="'MyController'")
When template is fetched and it is not found as global variable, ID is used as template body. This gives possibility to
set controller.template
as string containing template body.
This injector works with CommonJS
module to load dependencies. You have to define your resources in proper way and
have require
global method.
Module has three parameters that are added as prefixes to resources paths:
controllerPrefix
: [string], default:controllers
helperPrefix
: [string], default:helpers
templatePrefix
: [string], default:templates
If template ID is not a proper path it is interpreted as template body and return without change.
Built-in helpers cover basic logic and data management in view. They work with dynamic data-binding. They react automatically
to actual state of controller scope
.
div(ui-attr="{ id: someProperty, disabled: inputRead == 'something' }")
Set collection of attributes. Main accessor should be a object
with key
and value
pairs. Key name is used
as attribute name. If value return true
, key is set with key name (for example: disabled='disabled'
).
When value is false
key is removed from node. In ohter cases key is set with value as string
.
// Binding with input interaction
input(ui-bind="someProperty")
// Backbone.Model property binding with input
input(ui-bind="someModel", bind-property="'name'")
// Binding any node with set 'contenteditable'
div(ui-bind="someProperty", contenteditable="true")
// Binding for only pushig value to node
span(ui-bind="someProperty")
Two way binding scope
property or attribute of Backbone.Model
. Model has be bind
with set bind-property
accessor as name of attribute (second example).
Number is translated to integer
or float
(works with dot and comma delimiter). Other values are returned as string
.
Pushing values into input nodes uses val()
method. Updating other elements (like div
, span
) uses html()
method.
Then writing '<span>something</span>'
will show only 'something'
text wrapped in span
element.
span(ui-value="someProperty")
ui-value
is simpler version of ui-bind
. It only listen to changes of accessor and pushes value into node. Also it uses text()
method, writing '<span>something<span>'
will be displayed as it is. Use it if you only want to display value with no interact with user.
div(ui-class="{ selected: index == 1, moving: someAction(property)}")
Set collection of classes. Main accessor should be a object
with key
and value
pairs. Key name is used as class
name added or removed from node depending on logic value of value
.
div(ui-collection="collection" ... )
Render Backbone.Collection
models. Main accessor should points to Backbone.Collection
. Helper uses accessors:
collection-item
:string
: Name of collection model inscope
property of item. Defaultitem
.collection-sort
:string
orfunction
: Variable pushed as collection.comparator. ReadBackbone.Collection
docs for more info.collection-filter
:string
orfunction
: When this attribute isstring
, it is check ifmodel.toString()
contain thisstring
. When its false,ui-hide
class is added to item. When attribute isfunction
, it should returntrue
orfalse
for given item as argument.collection-view
:string id
: Used with dependeny injection to load item view.collection-empty
:string id
: When collection is empty node can be fill in empty template.collection-controller
:string id
: You can use your own item controller insteadNeck.Helper.collectionItem
. This can broke helper functionality. Use it if you really need it.
Every item has _index
property pushed to its scope
which coresponds to collection list index. This property is set once
at the begining and is not change when collection is sorted or filtered.
When collection-view
is not set, body of node is used as item template:
ul(ui-collection="users")
li(ui-value="item.get('name')")
There is different between using inline template (as node body) and using collection-view
. Inline template can not have
own template engine logic (it is invoke once when parent template is rendered). Also you can not use item scope
properties directly:
ul(ui-collection="users")
// This will be called when hole ul... is rendered, not when using it for item
if item.something
li(ui-value="item.get('name')")
li!= item.get('address') // This will not work also
For that behavior use collection-view
. It is separate view with own logic and access to scope
as normal controller:
ul(ui-collection="users", collection-view="'myUserViewPath'")
if item.something
li!= item.get('name')
Using inline template, everything is refreshed by helpers (data-binding). Using collection-view
gives possibility
to set values directly. Because of that, item view has to be rerender every time model changes. It is better to use with
external template scope
properties directly than by helpers (especially avoid ui-value
and use = property
).
It gives big performance boost. Rendering large collection with nested collections with inline templating
(using lot of helpers) can slow down painting dramatically. Using collection-view
you can set item body with scope
properties written directly or with data-binding (using helepers).
div(ui-element="'myDiv'")
class Controller extends Neck.Controller
...
someCallback: ->
@scope.myDiv.addClass 'super'
Create jQuery
object of node and put it into controller.scope
. Main accessor should be string
name.
// Calling controller callback
div(ui-event-submit="@sendForm(true, false)")
// Simple setting new value of scope.property
div(ui-event-click="property = 'newValue'")
// Statements can use javascript breaking line to create
// more complex actions
div(ui-event-blur="setSomething(inputValue); inputValue = 'something'")
Trigger proper event by calling action written in main accesor. Type of event is set in name of helper, for example
to trigger click
event use ui-event-click
helper. List of implemented events:
- Mouse events:
click
,dblclick
,mouseenter
,mouseleave
,mouseout
,mouseover
,mousedown
,mouseup
,drag
,dragstart
,dragenter
,dragleave
,dragover
,dragend
,drop
. - General events:
load
,focus
,focusin
,focusout
,select
,blur
,submit
,scroll
. - Touch events:
touchstart
,touchend
,touchmove
,touchenter
,touchleave
,touchcancel
. - Keys events:
keyup
,keydown
,keypress
.
Helper invoke preventDefault()
method of DOM event
object. It ensure that for example form will not be submit
or link will not change url.
When event is triggered, apply
method is invoke for releated to main accessor scope
properties. This close the flow
of data-binding proccess. Using event helpers to interact with user actions ensure that your application view will react
to data changes automatically (with deep properties mapping).
You can pass reference to function istead of calling it: ui-event-click="myFunction"
. When helper recognize that main
accessor is a function
, it will invoke that function passing jQuery event as first argument. Unfortunelly You can not then
pass any other arguments defining event.
Also when you pass function normally, but it returns function
, that function
will be treat as explained above (as function
that
will be called with jQuery event object).
Neck does not support using the same halper in one node more than once, as DOM attributes have to be unique. You can use complex actions or wrap node into other node (events are bubbling):
div(ui-event-click="...")
span(ui-event-click="...")
div(ui-show="something == 'great'")
div(ui-hide="something != 'great'")
Hides or shows element depends on main accessor logic value. Both helper works similar (with oposite logic) with one
difference - ui-hide
hides node before it checks logic value for first time. This ensure that element is hidden
before it can be seen.
ui-show
and ui-hide
(also ui-collection
and ui-list
to filter elements) uses global ui-hide
CSS class,
that set display: none !important
. You can overwrite it if you want to this working differently.
a(ui-href="'someUrl'")
Call Neck.Router.navigate
method when node is clicked. This changes url using routes from application and fill in yields.
Helper adds href='#'
attribute to anchor nodes if it is not set (for styling purposes).
div(ui-init="someAction(); someProperty = 123")
Action invoked in parsing proccess. This can be usefull to initially set some values in scope
directly from view.
div(ui-list="someList", ...)
Helper works similar to ui-collection
, but without events which collection has (adding or removing elements, etc..).
Main accessor should points to Array
instance. Helper uses accessors:
list-item
:string
: Name of list element inscope
property of item. Defaultitem
.list-sort
:function
: Function called within_.sortBy
method with element as argument. You can there returned some value that will be used to sort elements.list-filter
:string
orfunction
: When this attribute isstring
, it is check ifitem.toString()
contain thisstring
. When its false,ui-hide
class is added to item. When attribute isfunction
, it should returntrue
orfalse
for given item as argument.list-view
:string id
: Used with dependeny injection to load item view,list-empty
:string id
: When list is empty node can be fill in empty template.list-controller
:string id
: You can use your own item controller insteadNeck.Helper.collectionItem
. This can broke helper functionality. Use it if you really need it.
Every item has _index
property pushed to its scope
which coresponds to list index. This property is set once
at the begining and is not change when list is sorted or filtered.
Helper only rerender list when main accessor changes. If you change array element or remove it from array, helper will not rerender:
@scope.someList = ['something']
# Won't change view
@scope.someList[0] = 'newValue'
# Will change view
@scope.someList = ['one', 'two', 'three']
Use this helper for simple list of data. For better data-binding use ui-collection
.
Read ui-collection
corresponding section.
div(ui-neck="'controllerName'", neck-injector="'commonjs'")
This helper is not real Neck.Helper
instance. It is ordinary jQuery method invoke when document is ready.
For coherence in library, helpers name convention is used here. Main accessor should point to your root controller.
Set neck-injector
as name of one of dependency injector in Neck.DI
container that you want to use. It will be
inherit through all controllers and helpers.
ui-neck
uses RoR convention to use controller name as path to template. It constructs controller passing template
param as controller ID. When controller has not defined template
property, library will look for template using
controller ID (but with template
type). This works well with commonjs
dependency injector.
With globals
it will point to the same object and can throw errors.
To avoid reusing helper when controller is render, ui-neck
attribute is removed from node after initialize.
a(ui-route="'someController'", route-yield="'main'", route-params="{ item: someItem }" ... )
Helper pushes controller to ui-yield
on click. Main accessor should be string
controller ID. Helper uses accessors:
route-yield
:string id
: To yield with that id controller will be pushed. When not set, 'main' id will be used.route-params
:object
: Object passed asparams
property to controller.route-refresh
:boolean
: Refresh option passed to yield. Read more inui-yield
section.route-replace
:boolean
: Replace option passed to yield. Read more inyi-yield
section.
Helper does not work without corresponding ui-yield
(set with route-yield
). Routing does not change url either.
For url routing use ui-href
and Neck.App
with set routes.
div(ui-template="someTemplate")
p Empty list
div(ui-collection="collection", collection-empty="someTemplate")
Set node html body as scope
property. Main accessor will be set.
Helper is useful for example for using as empty template for ui-collection
or ui-list
.
Then you do not have to create separate file with empty message, just use property created with ui-template
.
Node is removed from DOM after helper is initialized.
div(ui-yield="'main'" ... )
Container for nested views (in this context view means controller with connected template). Main accessor
should be string
unique ID of yield. This ID can be used by ui-route
and routes in Neck.App
to determinate where view should be pushed. Helper uses accessors:
yield-view
:string id
: Controller ID of initially pushed view to yieldyield-params
:object
: Params passed to controller set byyield-view
(only for initially view)yield-replace
:boolean
: Determine if view stack is cleared when new view is pushedyield-inherit
:boolean
: Determine scope inheriting
You can use main accessor without setting ID - ui-yield=""
. Then 'main'
ID will be used. Identity
has to be unique through all application views, because yield is searched from view where is trigger (like ui-route
)
up to application root view. This gives possibility to change parts of your application flexible from any point.
ID has to be unique in particular state of application. When view is dismiss you can again use yield with the same ID.
Yield for default do not remove existings views, it appends new one at the end of node. To view only last one (on the top)
you have to write css
like this:
[ui-yield] div
display none
&:last-child
display block
Helper works similar to Android Activity Stack. State of your parent view is holded by browser, and when you close children view, your parent will be in state that was before. But all views work in background. They still has connected data-binding (big stack can slow down application).
If your application is simple, you can use one ui-yield
and push there your views. Create stack like this:
list -> show -> edit of some products. When you need some navigation, it can be other ui-yield
put in root application view
or other place. Also modal with wizard pages can be separate ui-yield
that has own views with other yields.
Similar to ui-neck
, helper uses controller ID as template
parameter in controller constructor. If you use Neck.DI.commonjs
you can leave template property undefined
and Neck will search for template in right place for you.
When yield appends view, it searches if that view is already there. It uses controller ID pushed to ui-route
or set
in Neck.App
routes. If this view is in stack, helper cleares all children of it making this view last one in stack.
Use route-replace
, replace
routes property or yield-replace
to clear stack when new view is appended. Property set in yi-yield
have less importance (other properties overrides it). When you call a view that is in stack as root already, it will not refresh it,
only clear its children if they exists.
Use route-refresh
or refresh
routes property to recreate view even it is in yield. Refresh means that view is removed form yield
and put again (controller is initialized).
In some cases would be better to invoke some callback when view has to be refreshed (for example when user tirgger back action in browser).
If there is set render:refresh
event in controller, it will be used instead rebuilding controller.
Sharing scope
works separate for all views pushed to yield. scope
between views in stack is not shared. Each one inherits
scope
from parent controller, when yield-inherit
is set to true
.
As it is described in scope section inheriting should be used carefully. Use yield inheritance when it is really needed.
Feel free to contribute project. For developing, clone project and run:
npm install && bower install
Use npm start
and go to your browser http://localhost:3333/test/
for checking tests.
Write some changes, update tests and do pull request to this repository. Please provide
proper prefix to your commits: BUG-FIX
, TEST
, DOCS
, REFACTOR
and NEW-FUNC
. It will be easier
to create changelog reading meaningful commits.
Neck is released under the MIT License