smalluban/neck

语言: JavaScript

git: https://github.com/smalluban/neck

None
README.md (中文)

颈部

Build Status

Neck是一个为Backbone.js添加许多功能的库:

  • 控制器和视图之间的事件驱动数据绑定(无脏检查)
  • 灵活的依赖注入模块与CommonJS一起开箱即用
  • 支持任何JavaScript模板引擎
  • 十几个用于视图逻辑的助手: 馆藏/列表管理 显示/隐藏元素 触发事件 路由(通过refrencing和url)到嵌套视图(像Android Activity Stack一样工作) 和更多...

概观

图书馆受到Angular和。框架的启发(在常规和代码中) 蝙蝠侠。颈部不是separete框架。它扩展了Backbone.js的功能。 您可以将它与为Backbone.js创建的许多其他插件和库一起使用。

简单的待办事项应用程序可能如下所示:

控制器:

class MyController extends Neck.Controller
  constructor:->
    super
    @scope.models = new Backbone.Collection()

视图:

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

库依赖于Backbone.js和Underscore.js。 它们必须包括在颈部之前。

<script src="/path/to/underscore.js"></script>
<script src="/path/to/backbone.js"></script>
<script src="/path/to/neck.js"></script>

浏览器支持

  • 所有现代浏览器的当前版本,IE9 +

项目骨架

要充分发挥Neck的潜力,您应该将它与CoffeeScript和JavaScript模板引擎之一一起使用。

我鼓励在Brunch骨架上使用Neck,这可能很棒 与颈部一起工作的起点。它包括Brunch构建工具,CommonJS,CoffeeScript等工具, 手写笔和玉。这是简单的颈部应用的工作示例。

API文档

  • Neck.Controller 初始化 实例属性 范围 亲 PARAMS 模板 divWrapper parseSelf 注射器 实例方法 构造函数 给予 看 应用
  • Neck.Helper 初始化 了解访问者 普遍解释的力量 映射访问器 帮助与模板 实例属性 属性 orderPriority
  • Neck.App 实例属性 路线 历史
  • Neck.DI Neck.DI.globals Neck.DI.commonjs
  • 内置助手 UI-ATTR ui-bind&ui-value UI级 UI收集 UI元素 UI事件-... ui-hide&ui-show UI的HREF UI-INIT UI列表 UI领 UI路线 UI模板 UI收益

Neck.Controller

扩展Backbone.View,控制传递给模板的数据。它也可以是回调操作的容器 从视图(带控制器的初始化模板称为视图)。

初始化

Neck.Controller可以直接初始化或通过ui-neck helper初始化(阅读ui-neck部分):

class MyController extends Neck.Controller
  template: 'myControllerTemplate'
  constructor:->
    super
    # Your setup actions
    @$el.addClass 'myController'

myController = (new MyController el: $('#myElement')).render()

直接初始化控制器时,必须调用render方法来开始解析过程并连接 模板到控制器el对象。

实例属性

范围controller.scope

您在scope属性中设置的所有内容都将在视图中可用。

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")

您可以直接使用范围属性并将它们放在模板中(它们将在每次渲染调用时刷新), 或使用访问器将它们放入帮助器中。

scope作为模板函数的第一个参数传递。许多模板引擎使用传递的对象作为上下文(如Jade)。然后可以直接通过其名称访问范围属性。

可以预定义属性(可以省略构造函数方法):

class MyController extends Neck.Controller
  scope:
    property: 'first property text'

  someAction: ->
    console.log @scope.property # Will write 'first property text'

scope具有特殊的_context和_resolves属性。不应直接触摸或使用它们。

parent controller.parent(默认值:undefined)

Controller可以用parent参数初始化,该参数应指向其他Neck.Controller实例:

myController = new Neck.Controller parent: ohterController

子控制器从父级继承范围属性(作为其原型)。然后孩子控制器会 可以通过原型链访问父作用域属性(这是从主控制器继承作用域的助手使用的)。

params controller.params(默认值:undefined)

使用它可以安全地将参数从一个控制器传递到另一个控

例如:

myController =  new Neck.Controller(params: myParam: 'some text')

考虑使用优先级高于继承范围的参数。将params传递给 控制器更安全,更有用。例如,您可以创建通用控制器 对给定的参数作出反应。

参数不会被推到范围财产。控制器中没有scope.params。 如果你想要一些param在视图中,你必须在初始化控制器时将其设置在范围内:

class myController extends Neck.Controller
  constructor:->
    super
    @scope.myProperty = @params.passedObject

template controller.template(默认值:undefined)

用于渲染视图主体的属性。预期价值:

  • string:依赖注入器的id或模板体(读取Neck.DI部分)。注射器应该返回 function(如下面的函数值)或包含模板的字符串。
  • boolean:设置为false时,控制器el body将用作模板,但不会从DOM中删除。设置为true时   渲染前DOM树为空。这很重要,例如当helper将重用自己的时候   body来创建元素列表(读取ui-collection或ui-list部分)。
  • function:使用controller.scope属性作为第一个参数进行设置。应该返回字符串值   包含模板体

未设置时(默认未定义值)模板未使用,但可以通过推送到构造函数方法的选项覆盖:

# 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'

一些帮助程序使用此功能自动将控制器与相应的视图(RoR方式)连接。 使用set template param初始化控制器将指向与控制器相同的视图路径 (但使用Neck.DI的模板类型)。

divWrapper controller.divWrapper(默认值:true)

默认情况下,控制器el对象(读取Backbone.View文档)创建为空div。模板将是 放在这个div元素中。 divWrapper为您提供了更改此行为的选项。如果设置为false, el对象将被视图替换并附加到DOM而不使用div包装器。

parseSelf controller.parseSelf(默认值:true)

它设置解析视图的起始点。解析可以从根节点(设置为true)或直接启动 根节点的子节点(设置为false时)。

注入器controller.injector(默认值:Neck.DI.globals)

Injector属性设置在获取控制器,帮助器和模板时使用哪个依赖注入器对象。 控制器和帮助程序通过整个应用程序继承此属性。您不需要在每个中设置它 controller / helper,只有root控制器才能设置此属性。如果您通过帮助程序初始化应用程序, 您无需直接在控制器中进行设置。

实例方法

构造函数新的Neck.Controller(params)

构造方法读取推送的params对象并使用属性:parent,params,template和所有使用的属性 Backbone.View(像el属性)。

它们用于设置实例属性。来自给定对象的其他属性将被忽略。

render controller.render()

方法使用scope属性获取模板,执行模板函数,解析帮助程序的视图并将其推送到 el控制器。通常不必直接调用此方法。例如ui-yield调用render 创建新视图后。但是,如果必须刷新视图,则可以从控制器调用render方法。

此方法提供两个公共事件:render:before和render:after。在查看后触发渲染事件 放入DOM。这可确保调用测量方法将返回正确的值。

watch control.watch(keys,callback,initCall)

当一个或多个范围属性发生更改时,触发回调操作。预期论点:

  • keys [string]:空间分隔的controller.scope对象的计算属性列表
  • callback [function]:每当其中一个键发生变化时调用;使用所有键作为参数调用   在函数中,这设置为设置监视的控制器实例。
  • initCall [boolean]:默认值:true。如果设置为false,则不会触发回调   观看已初始化。

对于Backbone.Model方法,也请监听更改事件。对于Backbone.Collection它的添加, 删除和更改事件。

当第一次设置watch时,该范围属性将更改为getter / setter属性。然后每一次 当此属性的值更改时,将调用回调:

@scope.one = 'Something'
@watch 'one', (value)-> console.log "One now is: #{value}"

# This will trigger callback with new value
@scope.one = 'New thing'

不支持深层属性。不要将它设置为'one.two.three'之类的键。 此方法不会填充Object.observe方法。直接在控制器中更改深度属性不会 触发回调。对于更复杂的结构,请使用Backbone.Model和'Backbone.Collection`。 (对于具有关系的模型,有很棒的库Backbone-relational.js)。

但是,帮助评估程序会触发对深层属性的适当更改(由于其映射过程)。 建议更多地依赖辅助工具,而不是直接在控制器中使用监视方法。

apply controller.apply(key)

通常由库,访问器或帮助程序调用,但可以手动调用以触发监视回调 对于范围属性。应该使用key作为包含contorller.scope属性名称的字符串进行调用。

Neck.Helper

扩展Neck.Controller。用于创建视图逻辑和控制器数据操作。

初始化

在呈现控制器时解析控制器(el对象)的视图。在树中下来,读取节点 寻找特定属性 - 使用ui-前缀。前缀后的名称从dashed转换为camelCase 并检查,如果存在帮助程序定义 - 首先在内置帮助程序容器中或当没有帮助程序时 - 用辅助类型推送到Neck.DI。

div(ui-some-helper="someValue", some-helper-property="'test'")

在初始化帮助器时,使用主属性体。初始化程序还可以采用其他属性。避免 重复使用前缀后的部分和一些名称。例如,ui-some-helper应该读取其他属性 喜欢some-helper-property或some-helper-other-thing。这个专业被称为访问者。

Helper从控制器继承范围。可以从辅助作用域访问控制器作用域属性。

了解访问者

Accesors会自动推送到辅助范围。主要属性(定义帮助程序)为scope._main。 其他属性从dashed转换为camelCase(从some-helper-property转换为scope.someHelperProperty)。

属性主体在父控制器范围的上下文中被解释为JavaScript代码。 这意味着someValue正在转换为controller.scope.someValue。每个人都会调用解释方法 读取或写入时间访问器值。访问者的值是动态的,取决于控制器范围的实际状态。

由于body被解释为JavaScript,您可以将语句编写为:a + b +'someText'或someAction(a + b)。 在此示例中,将在控制器范围上下文中解释a和b。也会调用someAction方法 作为controller.scope.someAction。

未初始化的controller.scope属性将设置为undefined以防止JavaScript运行时错误。 深度属性将设置为简单对象链,其中last属性设置为undefined。 例如,如果在controller.scope中不存在,则one.two.three将创建为scope.one = {two:{three:undefined}}。 创建未初始化的属性对于对象起作用。如果使用数组,则必须在控制器中定义。

不支持复杂语句,如:if(a){someAction(b)}。您应该将访问者身体理解为写入 或阅读内联声明。但是,你可以使用;行分隔符以创建多个调用。它对动作触发很有用。 读取访问器将使用返回值形式的第一个语句。还支持内联条件,如? b:c。

添加特殊字符@可以直接调用控制器(@someValue将被解释 as controller.someValue)。这对于从助手内部的控制器调用操作很有用。在下面的例子将被触发 直接写在控制器中的动作:

a(ui-event-click="@controllerAction()")

如果你想调用一些全局值,比如window.something用它来写。前缀(this.something)。

span(ui-value="this.moment().fromNow()")

普遍解释的力量

Helper访问器始终被解释为JavaScript。如果helper需要字符串,则可以传递变量或语句。 例如,ui-route使用主访问器作为控制器ID,但您可以放置​​范围 将该值用作控制器ID的属性:

@scope.myRoute = 'someController'
div(ui-route="myRoute", ...)

这是一般行为。它适用于任何访问者。

映射访问器

扫描访问者属性主体以获取控制器的范围属性,并推送所有属性主体 到特殊范围._resolves容器。当任何属性更改时,将触发访问者的刷新事件。

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")

在此示例中,scope._main依赖于controller.scope的someProperty,secondProperty和thirdProperty。 更改被调用函数中的深层属性不会触发访问者更改(root属性具有自己的getter / setter并触发更改)。 您必须使用deep属性作为参数调用函数,例如:someAction(property.deepProperty)。 您也可以在控制器方法中手动调用`@apply('property.deepProperty')。

映射适用于深层属性:

div(ui-helper="someObject.someProperty.otherProperty")

在这种情况下,映射将添加三个要监视的属性:someObject,someObject.someProperty和 someObject.someProperty.otherProperty。

其他帮助器可以更改此值:someObject.someProperty.otherProperty =“newValue”然后 它会触发引用。当其他帮助器或控制器将更改someObject或帮助器更改时 someObject.someProperty,它还会触发该访问器的刷新。

帮助与模板

为了避免在已初始化的帮助程序上更改orderPriority和模板属性的问题, 它们来自帮助原型。您必须在初始化之前设置它们:

class MyHelper extends Neck.Helper
  # Good way
  template: true
  orderPriority: 1

  constructor:->
    super

    # Bad way - parsing will take `true` value of `template`
    @template = false

具有模板的助手可以拥有自己的嵌套助手。在链接点中的所有帮助程序的访问者主体中使用@到根控制器。

实例属性

attributes helper.attributes(默认值:undefined)

包含将作为帮助程序的访问者创建的属性列表的数组。如上所述,应该创建它们 辅助名称作为前缀(但不是必需的)。

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'")

即使未定义attributes属性,也始终设置主要属性(作为scope._main)。

orderPriority helper.orderPriority(默认值:0)

辅助程序在节点内部进行了初始化。 Chrome和Firefox浏览器保留了节点属性的顺序。 不幸的是,Internet Explorer没有。如果您打算为Android和iOS编写移动应用程序,则可以跳过此属性。

当orderProperty设置为更高值时,将在同一节点中的其他帮助程序之前初始化helper。

neck.app

扩展Neck.Controller,添加url路由以进行导航。应该只使用一次, 通常作为根控制器。当App初始化时,它会检查路由并启动Backbone.history。

路由与产量相关联,由ui-yield帮助程序创建。

实例属性

路由app.routes(默认值:false)

申请路线清单。路径可以以不同的方式创建:

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

这三种方式可以混合使用。将yield设置为false将在达到url时清除它。参数将作为controller.params传递。 刷新和替换属性在ui-yield部分中描述。

路由传递给Neck.Router实例模块,该模块扩展了Backbone.Router。它具有所有功能 其父级设置为url params。不同的是将params传递给控制器​​ - 它们作为对象传递 使用来自url和查询参数的命名参数(它准备支持 骨干查询参数):

# 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

到达路径时,app.scope._state设置为实际路径名称。有时检查应用程序的状态是有用的。

历史app.history(默认:pushestate:true)

传递给Backbone.history.start方法的选项。阅读Backbone.history文档以获取更多信息。

Neck.DI

依赖注入模块的容器。每次应用程序需要控制器,帮助器或模板时 依赖注入加载方法被调用:

# Loading controller
@injector.load someControllerID, type: 'controller'

load方法应该将第一个参数作为获取对象的ID,第二个参数是options对象。现在库调用 选项设置为type:'controller | helper | template'的方法。

Neck支持开箱即用的两个模块:Neck.DI.globals和Neck.DI.commonjs。默认情况下,Neck使用全局变量。 您可以编写自己的经理(或扩展现有经理)来处理项目设置。当你使用ui-neck帮手放你的 经理进入Neck.DI容器。

Neck.DI.globals

传递给load方法的ID将被解释为全局变量path,someController将被读作window.someController。 它可以像someObject.someController一样是对象链。使用全局变量非常容易 - 定义一些东西,把它放进去 全球范围:

class window.MyController extends Neck.Controller
  ...
div(ui-neck="'MyController'")

当获取模板并且找不到全局变量时,ID用作模板体。这给了可能性 将controller.template设置为包含模板主体的字符串。

Neck.DI.commonjs

此注入器与CommonJS模块一起使用以加载依赖项。您必须以适当的方式定义资源 需要全球方法。

模块有三个参数作为资源路径的前缀添加:

  • controllerPrefix:[string],默​​认:controllers
  • helperPrefix:[string],默​​认:帮助器
  • templatePrefix:[string],默​​认:templates

如果模板ID不是正确的路径,则将其解释为模板主体并返回而不进行更改。

内置助手

内置帮助程序涵盖了视图中的基本逻辑和数据管理。它们使用动态数据绑定。他们会自动反应 到控制器范围的实际状态。

那里有tr

div(ui-attr="{ id: someProperty, disabled: inputRead == 'something' }")

设置属性集合。主访问器应该是具有键和值对的对象。使用密钥名称 作为属性名称。如果value返回true,则使用键名设置键(例如:disabled ='disabled')。 当value为false时,将从节点中删除密钥。在较坏的情况下,键的值设置为字符串。

ui-bind&ui-value

// 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")

Backbone.Model的双向绑定范围属性或属性。模型已经绑定 使用set bind-property访问器作为属性名称(第二个示例)。

Number转换为整数或浮点数(与点和逗号分隔符一起使用)。其他值以字符串形式返回。

将值推送到输入节点使用val()方法。更新其他元素(如div,span)使用html()方法。 然后写'<span> something </ span>'将只显示包含在span元素中的'something'文本。

span(ui-value="someProperty")

ui-value是ui-bind的简单版本。它只监听访问者的变化并将值推入节点。它也使用text() 方法,写'<span>某事<span>'将按原样显示。如果您只想显示没有与用户交互的值,请使用它。

UI级

div(ui-class="{ selected: index == 1, moving: someAction(property)}")

设置类的集合。主访问器应该是具有键和值对的对象。键名用作类 根据值的逻辑值在节点中添加或删除名称。

UI收集

div(ui-collection="collection" ... )

渲染Backbone.Collection模型。主访问器应指向Backbone.Collection。助手使用评估员:

  • collection-item:string:item的scope属性中的集合模型的名称。默认项目。
  • collection-sort:string或function:作为collection.comparator推送的变量。阅读Backbone.Collection   docs获取更多信息。
  • collection-filter:string或function:当此属性为string时,检查model.toString()   包含此字符串。当它的false,ui-hide类被添加到item时。当属性是功能时,   对于给定的项目,它应该返回true或false作为参数。
  • collection-view:string is:与依赖注入一起使用以加载项目视图。
  • collection-empty:string id:当集合为空时,节点可以填入空模板。
  • collection-controller:string id:您可以使用自己的项目控制器而不是Neck.Helper.collectionItem。   这可能会打破辅助功能。如果你真的需要它,请使用它。

每个项目都将_index属性推送到其范围,该范围对应于集合列表索引。此属性设置一次 在开始时并且在收集被分类或过滤时不会改变。

项目模板

未设置集合视图时,节点主体用作项目模板:

ul(ui-collection="users")
  li(ui-value="item.get('name')")

使用内联模板(作为节点主体)和使用集合视图有所不同。内联模板不能有 自己的模板引擎逻辑(在呈现父模板时调用一次)。您也不能使用项目范围 属性直接:

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

对于该行为,请使用collection-view。它是具有自己逻辑的单独视图,并且作为普通控制器访问范围:

ul(ui-collection="users", collection-view="'myUserViewPath'")
if item.something
  li!= item.get('name')

使用内联模板,所有内容都由帮助程序刷新(数据绑定)。使用集合视图提供了可能性 直接设置值。因此,每次模型更改时都必须重新呈现项目视图。最好与之配合使用 外部模板范围属性直接比帮助程序(特别是避免使用ui-value和use = property)。

它提供了很大的性能提升。使用带有内联模板的嵌套集合渲染大型集合 (使用大量助手)可以大大减缓绘画速度。使用集合视图,您可以使用范围设置项目正文 直接写入或使用数据绑定(使用helepers)编写的属性。

UI元素

div(ui-element="'myDiv'")
class Controller extends Neck.Controller
  ...
  someCallback: ->
    @scope.myDiv.addClass 'super'

创建节点的jQuery对象并将其放入controller.scope。主访问者应该是字符串名称。

UI事件-...

// 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'")

通过调用主访问器中写入的操作来触发正确的事件。例如,事件的类型在帮助程序的名称中设置 触发click事件使用ui-event-click helper。已实施事件清单:

  • 鼠标事件:单击,dblclick,mouseenter,mouseleave,mouseout,mouseover,mousedown,mouseup, 拖动,拖拽,dragenter,dragleave,dragover,dragend,drop。
  • 一般事件:加载,聚焦,聚焦,聚焦,选择,模糊,提交,滚动。
  • 触摸事件:touchstart,touchend,touchmove,touchenter,touchleave,touchcancel。
  • 键事件:keyup,keydown,keypress。

Helper调用DOM事件对象的preventDefault()方法。它确保例如不提交表格 或链接不会更改网址。

了解数据绑定

触发事件时,将调用apply方法以关联到主访问器范围属性。这关闭了流程 数据绑定过程使用事件帮助程序与用户操作进行交互可确保应用程序视图作出反应 自动更改数据(使用深层属性映射)。

使用jQuery事件对象

您可以将参考传递给函数而不是调用它:ui-event-click =“myFunction”。当助手认出那个主要时 accessor是一个函数,它将调用该函数作为第一个参数传递jQuery事件。不幸的是,你不能 传递定义事件的任何其他参数。

当你正常传递函数但它返回函数时,该函数将按照上面的解释处理(作为函数 将使用jQuery事件对象调用。

乘以事件

Neck不支持在一个节点中多次使用相同的halper,因为DOM属性必须是唯一的。您可以使用复杂的操作 或者将节点包装到其他节点(事件冒泡):

div(ui-event-click="...")
  span(ui-event-click="...")

ui-hide&ui-show

div(ui-show="something == 'great'")
div(ui-hide="something != 'great'")

隐藏或显示元素取决于主访问者逻辑值。两个帮助程序的工作方式类似(使用oposite逻辑) 差异 - ui-hide在第一次检查逻辑值之前隐藏节点。这可以确保隐藏元素 之前可以看出来。

ui-show和ui-hide(也是用于过滤元素的ui-collection和ui-list)使用全局ui-hide CSS类, 设置显示:无!重要。如果您希望以不同的方式工作,则可以覆盖它。

UI的HREF

a(ui-href="'someUrl'")

单击节点时调用Neck.Router.navigate方法。这会使用应用程序中的路径更改URL并填写产量。

如果未设置锚点节点,Helper会将href ='#'属性添加到锚点节点(用于样式目的)。

UI-INIT

div(ui-init="someAction(); someProperty = 123")

在解析过程中调用的操作。这可以直接从视图中初始设置范围内的某些值。

UI列表

div(ui-list="someList", ...)

Helper的工作方式类似于ui-collection,但没有集合所具有的事件(添加或删除元素等)。 主访问器应指向Array实例。助手使用访问者:

  • list-item:string:item的scope属性中列表元素的名称。默认项目。
  • list-sort:function:在_.sortBy方法中调用的函数,以element作为参数。你可以在那里归还一些   将用于对元素进行排序的值。
  • list-filter:string或function:当该属性为string时,检查item.toString()   包含此字符串。当它的false,ui-hide类被添加到item时。当属性是功能时,   对于给定的项目,它应该返回true或false作为参数。
  • list-view:string is:与依赖注入一起使用以加载项目视图,
  • list-empty:string id:当list为空时,可以填充空模板。
  • list-controller:string id:您可以使用自己的项目控制器而不是Neck.Helper.collectionItem。   这可能会打破辅助功能。如果你真的需要它,请使用它。

每个项目都将_index属性推送到其范围,该范围对应于列表索引。此属性设置一次 在开始时并且在列表被排序或过滤时不会更改。

主要访问者更改时,助手仅重新呈现列表。如果更改数组元素 或者从数组中删除它,帮助程序不会重新呈现:

@scope.someList = ['something']

# Won't change view
@scope.someList[0] = 'newValue'

# Will change view 
@scope.someList = ['one', 'two', 'three']

使用此帮助程序可以获得简单的数据列表。为了更好的数据绑定,请使用ui-collection。

项目模板

阅读ui-collection对应部分。

UI领

div(ui-neck="'controllerName'", neck-injector="'commonjs'")

这个助手不是真正的Neck.Helper实例。当文档准备好时,它是普通的jQuery方法调用。 对于库中的一致性,此处使用帮助程序名称约定。主访问器应指向您的根控制器。

将neck-injector设置为您要使用的Neck.DI容器中依赖项注入器之一的名称。这将是 通过所有控制器和助手继承。

ui-neck使用RoR约定将控制器名称用作模板的路径。它构造控制器传递模板 param作为控制器ID。当控制器没有定义模板属性时,库将使用查找模板 控制器ID(但具有模板类型)。这适用于commonjs依赖注入器。 对于全局变量,它将指向同一个对象并可能抛出错误。

为避免在渲染控制器时重用辅助函数,初始化后会从节点中删除ui-neck属性。

洋葱路线

a(ui-route="'someController'", route-yield="'main'", route-params="{ item: someItem }" ... )

Helper在点击时将控制器推向ui-yield。主访问者应该是字符串控制器ID。助手使用访问者:

  • route-yield:string id:使用该id控制器将被推送。未设置时,将使用'main'id。
  • route-params:object:作为params属性传递给控制器​​的对象。
  • route-refresh:boolean:刷新选项传递给yield。在ui-yield部分阅读更多内容。
  • route-replace:boolean:替换传递给yield的选项。在yi-yield部分阅读更多内容。

没有相应的ui-yield(使用route-yield设置),Helper不起作用。路由也不会更改URL。 对于url路由,请使用带设置路由的ui-href和Neck.App。

UI模板

div(ui-template="someTemplate")
  p Empty list

div(ui-collection="collection", collection-empty="someTemplate")

将节点html体设置为范围属性。将设置主访问者。

例如,Helper可用作ui-collection或ui-list的空模板。 然后,您不必使用空消息创建单独的文件,只需使用使用ui-template创建的属性。

初始化帮助程序后,节点将从DOM中删除。

UI收益

div(ui-yield="'main'" ... )

嵌套视图的容器(在此上下文视图中表示具有连接模板的控制器)。主要访问者 应该是yield唯一的字符串ID。此ID可由ui-route和Neck.App中的路由使用 确定应该推送视图的位置。助手使用访问者:

  • yield-view:string id:要生成的最初推送视图的控制器ID
  • yield-params:object:由yield-view传递给控制器​​的参数(仅适用于初始视图)
  • yield-replace:boolean:确定在推送新视图时是否清除视图堆栈
  • yield-inherit:boolean:确定范围继承

收益率ID

您可以在不设置ID的情况下使用主访问器 - ui-yield =“”。然后将使用'main'ID。身分 必须在所有应用程序视图中都是唯一的,因为从视图中搜索yield是触发器(如ui-route) 应用程序根视图。这样就可以从任何角度灵活地更改应用程序的各个部分。

ID必须在特定的应用状态中是唯一的。当视图被忽略时,您可以再次使用具有相同ID的yield。

产量视图堆栈

默认情况下,Yield不会删除现有视图,而是在节点末尾添加新视图。仅查看最后一个(在顶部) 你必须写这样的css:

[ui-yield] div 
  display none
  &:last-child
    display block

Helper的工作方式类似于Android Activity Stack。您的父视图的状态由浏览器控制,当您关闭子视图时, 你的父母将处于以前的状态。但所有观点都在后台运作。他们仍然连接了数据绑定 (大堆栈可以减慢应用程序)。

如果您的应用程序很简单,您可以使用一个ui-yield并推送您的视图。像这样创建堆栈: list - > show - >编辑某些产品。当你需要一些导航时,可以将其他ui-yield放在root应用程序视图中 或其他地方。使用向导页面的模态也可以是单独的ui-yield,它具有自己的视图和其他产量。

以RoR方式模板化

与ui-neck类似,helper使用控制器ID作为控制器构造函数中的模板参数。如果你使用Neck.DI.commonjs 您可以保留模板属性undefined,并且Neck会在适当的位置为您搜索模板。

替换和刷新视图

当yield附加视图时,它会搜索该视图是否已存在。它使用推送到ui-route或set的控制器ID 在Neck.App路线。如果此视图在堆栈中,则帮助程序会清除它的所有子项,使此视图在堆栈中最后一个。

添加新视图时,使用route-replace,replace routes属性或yield-replace清除堆栈。属性设置为yi-yield 重要性较低(其他属性覆盖它)。当您以root身份调用堆栈中的视图时,它将不会刷新它, 只有存在才会清除其子女。

使用route-refresh或refresh routes属性重新创建视图,即使它处于yield状态。刷新意味着视图从yield中删除 然后再放(控制器初始化)。

在某些情况下,在必须刷新视图时调用某些回调会更好(例如,当用户tirgger在浏览器中执行后退操作时)。 如果在控制器中设置了render:refresh事件,则将使用它来代替重建控制器。

范围继承

共享范围对于推动产生的所有视图都是分开的。不共享堆栈中的视图之间的范围。每一个都继承 当yield-inherit设置为true时,父控制器的范围。

正如在范围部分中所描述的那样,应谨慎使用继承。在真正需要时使用yield继承。

贡献

随意贡献项目。对于开发,克隆项目和运行:

npm install && bower install

使用npm start并转到浏览器http:// localhost:3333 / test /进行检查测试。

拉请求

编写一些更改,更新测试并将请求拉到此存储库。请提供 适当的前缀:BUG-FIX,TEST,DOCS,REFACTOR和NEW-FUNC。这会更容易 创建更改日志阅读有意义的提交。

执照

Neck根据MIT许可证发布

本文使用googletrans自动翻译,仅供参考, 原文来自github.com

en_README.md

Neck

Build Status

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...

Overview

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

Setup

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>

Browser support

  • Current version of all modern browsers, IE9+

Project skeleton

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.

API Documentation

Neck.Controller

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).

Initializing

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.

Instance properties

scope controller.scope

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.

parent controller.parent (default: undefined)

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).

params controller.params (default: undefined)

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

template controller.template (default: undefined)

Property used for rendering body of view. Expected values:

  • string: id or template body for dependency injector (read Neck.DI section). Injector should return
    function (as function value below) or string containing template.
  • boolean: When set to false controller el body will be used as template but not removed from DOM. When set to true
    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 with controller.scope property as first argument. Should return string 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).

divWrapper controller.divWrapper (default: true)

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.

parseSelf controller.parseSelf (default: true)

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 controller.injector (default: Neck.DI.globals)

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.

Instance methods

constructor new Neck.Controller(params)

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.

render controller.render()

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.

watch controller.watch(keys, callback, initCall)

Triggers callback action when one or more scope properties has changed. Expected arguments:

  • keys [string]: space separated list of evaluating properties of controller.scope object
  • callback [function]: called every time when one of keys has changed; called with all keys as arguments
    in function, this set to controller instance where watch is set.
  • initCall [boolean]: Default value: true. If set to false 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.

apply controller.apply(key)

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.

Neck.Helper

Extends Neck.Controller. Used to create view logic and controller data manipulation.

Initializing

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.

Understanding accessors

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()")

Power of universal interpreting

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.

Mapping accessors

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.

Helper with template

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.

Instance properties

attributes helper.attributes (default: undefined)

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.

orderPriority helper.orderPriority (default: 0)

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.

Neck.App

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.

Instance properties

routes app.routes (default: false)

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.

history app.history (default: pushestate: true)

Options passed to Backbone.history.start method. Read Backbone.history docs for more information.

Neck.DI

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.

Neck.DI.globals

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.

Neck.DI.commonjs

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

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.

ui-attr

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.

ui-bind & ui-value

// 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.

ui-class

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.

ui-collection

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 in scope property of item. Default item.
  • collection-sort: string or function: Variable pushed as collection.comparator. Read Backbone.Collection
    docs for more info.
  • collection-filter: string or function: When this attribute is string, it is check if model.toString()
    contain this string. When its false, ui-hide class is added to item. When attribute is function,
    it should return true or false 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 instead Neck.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.

Item templating

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).

ui-element

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.

ui-event-...

// 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.

Understading data-binding

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).

Using jQuery event object

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).

Multiply events

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="...")

ui-hide & ui-show

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.

ui-href

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).

ui-init

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.

ui-list

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 in scope property of item. Default item.
  • 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 or function: When this attribute is string, it is check if item.toString()
    contain this string. When its false, ui-hide class is added to item. When attribute is function,
    it should return true or false 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 instead Neck.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.

Item templating

Read ui-collection corresponding section.

ui-neck

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.

ui-route

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 as params property to controller.
  • route-refresh: boolean: Refresh option passed to yield. Read more in ui-yield section.
  • route-replace: boolean: Replace option passed to yield. Read more in yi-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.

ui-template

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.

ui-yield

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 yield
  • yield-params: object: Params passed to controller set by yield-view (only for initially view)
  • yield-replace: boolean: Determine if view stack is cleared when new view is pushed
  • yield-inherit: boolean: Determine scope inheriting

Yield ID

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 view stack

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.

Templating in RoR way

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.

Replace and refresh views

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.

Scope inheriting

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.

Contribution

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.

Pull requests

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.

License

Neck is released under the MIT License