“MakeNode”部件扩展
7月8日下午02:11,在2011年由萨蒂扬| 开发 | 6评论在我以前的文章中, 一个YUI 3应用配方 ,我表明方式使用Y.substitute一个非常基本的模板处理器。 从那里生活的想法了,从#YUI IRC频道乡亲的建议,我是我的网站上提供一个Widget扩展,称为MakeNode 。 MakeNode是不是一个通用的模板处理器,它不意味着为一体。 另一方面,它紧密集成与YUI 控件的基础类,包括ClassName和事件佣工和国际化。 在这篇文章中,我将采取微调的例子,并修改它遵循从我以前的文章的指引和使用MakeNode。 修改后的微调组件( JS , CSS , 精灵 ),以及一个例子是从我的网站。 在本文末尾可以找到更多的资源链接。
扩展你的组件
你一旦MakeNode加载,需要包括在你的模块YUI().use()语句使用名称'makenode' 然后,延长你的widget,你列出的第三个参数Y.Base.create()像这样:
Y.Spinner = Y.Base.create( “微调”, Y.Widget, [Y.MakeNode] { / /实例成员... }, { / /静态成员 } );
您可以添加任何合适的扩展部件,如WidgetParent,WidgetChild,WidgetStdMode,沿MakeNode等MakeNode增加了两个受保护的方法, _makeNode和_locateNodes,它会读取从几个静态属性,如果找到。
这个扩展的所有成员是protected或private,因为他们是为了组件的开发和使用这些组件的实施者,谁不应该与他们的困扰。
定义模板
你通常会做的第一件事是定义为您的组件的模板。 对于微调,我们的模板将是:
_template: <input type="text" title="{s input}" class="{c input}">“ “<button type="button" title="{s up}" class="{c up}"> </按钮>” “<button type="button" title="{s down}" class="{c down}"> </按钮>” 。加入('\ N'),
通常会被命名为默认模板_TEMPLATE和沿线的其他类的静态属性,如宣布ATTRS 。 如果没有其他明文规定,MakeNode将使用此模板。 模板是纯HTML加上一系列括在大括号中的占位符,每个单个字符(加工代码)和一个或多个参数。 占位符和他们生产什么是:
{@ attributeName}配置属性的值{p propertyName}实例的属性值{m methodName arg1 arg2 ….}给定的方法的返回值。 后跟由空格隔开的参数的方法名称和任何数量的加工代码。 必须括在双引号字符串。 数字,布尔值和null字符串将被转换成适当的数据类型{c classNameKey}CSS类名从产生_CLASS_NAMES静态属性{s key}字符串从strings属性,子属性使用key。{? other placeholder }生成的字符串checked时,其余的占位符处理的结果是真实的。{}任何其他值将处理就像Y.substitute不。
例如, {@ value}将转化到this.get('value') {p value}转换为this['value']
{m}占位符是稍微复杂一些。 m加工代码后的第一个参数是该方法的名称和参数,将通过给定的方法全部由空格隔开的休息。 这些参数可以是数字, null , true , false或字符串括在双引号。 MakeNode将解析和转换他们到正确的类型,从而{m myMethod 123.45 true “this is a string”}将导致在调用this.myMethod(123.45, true, “this is a string”)前两个参数其正确的数据类型转换为字符串可以包含空格。 包括一个双引号,使用\\"正因为JavaScript解释一个单一的一个丢弃它之前,它得到到MakeNode。只允许使用双引号,MakeNode不eval() eval eval()这样的解析器是有限的双反斜线但安全。什么,但号码, null ,布尔和双引号字符串将被忽略。
{?}占位符是很方便的使用复选框和单选按钮。 它会产生字符串“checked”根据真值的处理指令的代码如下。 因此, <input type=”checkbox” {? m getLength}/> <input type=”checkbox” {? m getLength}/>将产生显着的复选框,如果getLength方法返回什么,但零。 {?}将接受任何其他占位符,但它不仅使前三感。
为{c}占位符,我们需要定义有1 _CLASS_NAMES财产。
进一步占位符,可以添加到MakeNode加入到他们_templateHandlers哈希。
_CLASS_NAMES财产
沿着与ATTRS和_TEMPLATE静态属性,我们可以定义一个_CLASS_NAMES属性,它指向一个字符串数组。 这些字符串都将被用来生成一个类的。 因此_CLASS_NAMES: ['input']会产生的className “yui3-spinner-input” 。 那些类名存储实例属性this._classNames 。 {c input} “yui3-spinner-input” {c input}上面的模板中的占位符将被替换。
你可以使用_CLASS_NAMES属性来生成任意数量的类名,你是否在模板或不使用他们。 你仍然可以达到从内部this._classNames这些额外的类名。 使用yui3前缀NAME的静态属性的价值,产生的className转为小写,然后在给定的的字符串_CLASS_NAMES (这一次将不会变成小写),全部由连字符分隔。 _classNames哈希也将包含为boundingBox类名和的contentBox ,下的第一个"."键,并根据第二个“content”键。 部件分配到boundingBox NAME在继承链中的每个类的属性的值派生的类名,开始yui3-widget 。 MakeNode存储到this._classNames只有最顶级的className boundingBox 。
如果一个组件是几个层次,从Widget一样, SuperSpecialSpinner从的继承SuperSpinner从继承从继承Spinner Widget, _CLASS_NAMES this._classNames Spinner Widget,如果其中的任何或全部定义_CLASS_NAMES属性,MakeNode将产生所有的类名,并存储在他们this._classNames 。 你不必包括在每个级别的名字已经宣布,在以前的水平。 事实上,它是更好的,你不会因为在每个级别的类名,使用该级别的属性值的NAME 。 因此, SuperSpecialSpinner , {c input}仍将导致的“yui3-spinner-input”和“yui3-superspecialspinner-input” ,所以它会保持你的CSS文件仍然有效。
{}占位符
构件有strings配置属性定义,虽然它不与任何值初始化。 此属性是为了保存字符串(或通过屏幕阅读器,阅读)的用户是可见的。 重要的是,你永远不包括直接在模板中可见字符串。 这是不是的MakeNode的要求 - 在所有它从来就不是一个好主意。 应始终被视为或读取用户的所有字符串放置在strings属性。 strings属性包含了每一个人的文字是由位于其关键的哈希。 微调器组件有以下字符串,你可以看到在上面的模板使用。
字符串:{ 值:{ 输入:“按箭头/向下键轻微的增量,页/主要增量。” 了起来:“增量” 下来:“减” } },
这样做的最好的部分是组件可以使用您的组件的开发本地化为其他语言很容易。 当创建一个微调的实例,你可以这样做:
mySpinner =新的微调({字符串:Y.Intl.get('微调')}); 以这种方式设置的配置属性strings取代与使用先前定义的语言,从语言资源文件的默认strings值。 {s}占位访问存储在字符串strings属性,无论是默认的或翻译的,如果设置。 {s xxxx}占位符,在事实上,没有什么比一个快捷方式到更多的{@ strings.xxxx}占位符。 然而,第一次只能在顶层访问字符串的同时,例如, {@ strings.xxxx.yyyy.zzzz}将允许您访问字符串更深。
使用在renderUI _makeNode
我们使用模板来创建我们的组件的标记。 这样做,我们可以调用MakeNode的_makeNode方法,是这样的:
renderUI:函数(){ 。this.get(“contentBox)追加(this._makeNode()); },
这将填补我们的部件在contentBox处理模板的标记。 _makeNode方法返回一个实例Y.Node可追加或插入任何地方,或只是为以后使用而举行。 它不返回一个字符串,它产生一个Node实例。
_makeNode方法有两个可选参数:一个参考模板和对象,以填补在占位符,,作为Y.substitute 。 在我们简单的微调例如,有一个单一模板的整体部件,但其他部件可能需要位和几个模板件。 在这种情况下,你通常会致电_makeNode的主要部分没有参数,调用不同的模板,再次填补多余的部分。 例如包含此renderUI方法:
renderUI:函数(){ this._makeNode的fieldset =(); this.each(函数(项目){ fieldset.appendChild(this._makeNode(MultipleTemplates.RADIO_TEMPLATE项)); }); 中this.get(“contentBox)追加(字段集); }
_makeNode第一次调用返回一个Node实例存储在变量中fieldset 。 样品组件也延伸与Y.ArrayList所以RADIO_TEMPLATE将被从存储在数组列表中的项目和所产生的附加 的节点值填充fieldset前最后追加到contentBox 。 如特殊的占位符{@} {p}仍将主要对象的属性或属性。 正如Y.substitute将嵌套的项目将被处理。
_locateNodes方法
MakeNode进一步提供_locateNodes方法,将尽力找到所有的元素在声明的类名_CLASS_NAMES 。 找到特定的元素,您可以通过任意数量的className键,否则, _locateNodes尝试他们所有。 对于每个发现的className的每个元素中, _locateNodes会产生一个私人的实例的属性,使用下划线前缀键的名称和“Node”后缀。 因此,我们飞旋例如, _locateNodes会生成的属性_inputNode , _upNode和_downNode 。 如果有多个元素具有相同的className, _locateNodes将返回一个引用到他们的第一。 如果一个元素没有被发现,没有变量将被创建。
在微调组件,我们使用_locateNodes后创建的标记:
renderUI:函数(){ this.get(CBX公司)追加(this._makeNode()); this._locateNodes(); },
_EVENTS的静态属性
一个进一步的属性可以被定义沿线_TEMPLATE和_CLASS_NAMES ,是_EVENTS 。 _EVENTS将包含类名键,每个包含一个事件的类型和方法来处理它们的哈希散列。 这是更好地用一个例子来解释:
_EVENTS:{ '。':{ 关键:{ FN:“_onDirectionKey” 参数:(Y.UA.opera“下来:”:!“记者:”)+“38,40,33,34” }, 的mousedown:'_onMouseDown' }, “......”:{ 的MouseUp:'_onDocMouseUp' }, 输入:{ 变化:“_onInputChange”, } },
_EVENTS是一个具有任意数量的属性的对象(哈希)。 属性的名称,即哈希键,确定元素的事件,我们会听取。 他们是在使用相同的标识符_CLASS_NAMES 。 有两个额外的特殊功能键"."和".." 虽然内嵌套元素contentBox的className键, "."关键指的boundingBox本身,而".."是指包含此部件的文件。 认为他们做chdir命令时,在位于boundingBox水平。 _EVENTS财产处理后renderUI , bindUI和syncUI方法,被称为widget的预期,因此已经插入文档正文内,否则".."将失败。
条目中的每个_EVENTS是1进一步对象事件的类型作为其关键和1实例的方法来处理它的名称。 _EVENTS ,是一个静态变量,有没有来访问this所以它可以不采取实际功能的引用,只有名称的事件侦听器方法。 一些事件类型,如需要额外的参数, key事件。 在这种情况下,而不是提供的事件处理程序的名称,你可以提供与物业fn args args对象举行的函数的名称和额外的参数,需要时。
MakeNode将使用Node.delegate听嵌套元素的事件,而它会使用Y.on听从事件boundingBox和笔体。 (注:听任何嵌套元素的key事件仅适用于版本3.4.0pr1及以上,由于该代表团key 。事件是不是之前的所有其他功能的工作以及与以前的版本)
_EVENTS声明是累积性的,当组件从一个继承。 继承链中的每个类都会有其自己的_EVENTS声明分开处理。
_ATTRS_2_UI的静态属性
活动是双向的,从UI组件从组件到UI。 首先是处理由_EVENTS属性。 然后有由属性值的变化,必须体现在用户界面中引发的事件。 正如我在前面的文章,有任何改变配置属性的辅助疗效时提到,他们应改变事件监听器处理,而不是可选的setter方法的属性,它应该只处理正常化设置的值。 UI应该反映配置属性的状态,首先在syncUI时被初始化,然后对每一个属性更改事件。 对于后者,我们需要附加一个事件监听器,我们在做bindUI 。 部件已经提供了一种机制,使这个简单,我在上一篇文章的评论。
部件使用,它包含一个与另外两个性质,对象_UI_ATTRS SYNC和BIND实例属性_UI_ATTRS 。 这些是一个数组,列出最初同步时的配置属性的名称,然后听取以反映当前值保持UI。 部件预计每个条目有一个与之关联的方法,由前缀属性名后的第一个字符转换为大写有骆驼的情况下适当的方法名称的属性名称的命名_uiSet 。 因此,如果"value"在任何上市_UI_ATTRS阵列( SYNC或BIND )的Widget希望找到一个_uiSetValue方法。 这种方法将收到两个参数的value设置和src的变化。 这是为我们的微调代码_uiSetValue方法:
_uiSetValue:功能(价值,SRC){ 如果(SRC ===界面){ 返回; } this._inputNode.set(值,this.get(格式化)(值)); },
你在这段代码中看到对应的字符串常量的所有大写字母标识符宣布在其他地方,让YUI压缩到更好地完成其工作。 ,基本上,该方法设置的value在HTML属性<input>中的新值集,被格式化后。 被由_locateNodes提供参考文本框。 src参数的初步检查,看看是否设置为字符串值'ui' 如果是这样的,将采取任何行动。 这是为了避免无休止的循环。 如果用户在输入框中输入的东西,其价值将进入value的配置属性,然后将火的事件valueChange ,会得到_uiSetValue呼吁,如果不加以控制,然后去改变输入框的值,将再次引发整个过程。 因此, _uiSetValue ,如果我们知道的变化,从UI,我们什么也不做,所以打破循环。 然而,这需要另一块代码的其他地方。 在DOM事件监听器,当我们设置的配置属性,我们使用的第三个可选参数设置,像这样:
_afterValueChange:功能(EV){ this.set(值,ev.newVal {SRC:用户界面}); }
这是我们确保,因此,从用户界面的变化标记,然后检查相同的标志,以避免环路。
这一切说,我还没有提到_ATTRS_2_UI在本节的标题提到的静态属性。 正如我在以前的文章显示评论(通过我在他们的失误),确保正确列出的所有属性影响的UI是有些凌乱。 你永远不应该初始化_UI_ATTRS以来从头部件已经列出了一大堆的属性和那些会丢失。 你有新的属性名称串连在现有的,这有点很难记得如何做是正确的。 为了简单,MakeNode将读取静态属性_ATTRS_2_UI并为你做什么,串联。 它将连接所有这些清单,以便从继承链中的每一类,每一级,每一类可以处理自己的属性。 在微调,我们有:
_ATTRS_2_UI:{ BIND的:价值, 同步:VALUE },
MakeNode将接受的名称数组或一个单一的属性名称,在这种情况下。
自然产生的问题,为什么两份名单,结合其他同步? SYNC阵列往往比少BIND列表项和,这是因为组件的模板可能已经有很相同的配置属性的默认值,有没有必要做一个初步的同步。 因此,如果的value配置属性的默认值是一个空字符串的<input>模板中的元素没有value属性,那么有没有必要在初始化同步。
MakeNode将检查这些阵列中的任何重复的条目。 如果任何出现的,它意味着已经处理此属性和任何新的声明将最有可能超越我们的组件1类继承_uiSetXxxx它的处理程序。 顺便说一下,MakeNode重复的条目,在_CLASS_NAMES ,这也可能导致在一些冲突,但并非所有的情况下,还检查。 MakeNode会写消息到任何这样的错误日志。
结论
MakeNode提供了一个非常简单的模板处理器,简单的功能,高度集成与Widget的基础类。 它还提供了创建在模板中使用的类名,并使用这些名称来定位节点的辅助方法。 它还提供了挂接到由UI组件本身所产生的事件和与之相关的每一个方法的手段。 它所有这些东西,同时注意尊重直线上升Widget和任何级别的类,你可以定义继承链。
它不提供绝对的所有可能性,但涵盖了他们良好的范围。 然而,它并不妨碍你增加额外的功能。 你可能很少有写bindUI或syncUI方法,如果你使用由MakeNode提供胶水,但你可以这样做,因为MakeNode不使用它们。
作为奖励那些有耐心去阅读,到目前为止,我还修改安东尼Pipkin的图库组件的按钮集:
API文档可以在这里找到。
共享和扩展: 书签del.icio.us Digg它! | reddit!


这是Ryan的新的MVC的View组件在3.4.x兼容吗? 它可以被用在这一框架兼容的方式呈现的标记?
安德鲁·伍尔德里奇 - 7月9日,2011年#
安德鲁 ,
这个扩展是建立一个辅助组件,按钮和微调的例子表明,没有建立整个应用,MVC框架。 这些组件可以在任何地方使用从Widget派生的任何其他组件。 在MVC框架,它会自然地使用这些组件一起普通的HTML或任何其他组件从Widget派生类继承Y.View构建用户界面,它是否使用MakeNode与否。
萨蒂扬 - 7月10日,2011年#
萨蒂扬,
这是非常伟大的! 我经历了所有的痛点,你这个widget扩展解决。 看来,使用这个扩展可以消除大量的重复锅炉板代码结束了书面创建自定义部件时,同时对DOM和渲染,这是令人兴奋地看到了如何连接的代码和逻辑规范!
你将被添加到了YUI 3画廊,它更多的人访问。使用()?
像安德鲁指出,有是Y.View事件和渲染一些概念上的重叠,虽然这两个API是不同的。 这可能是值得搞清楚,如果是为这两个API的共同地面有更多类似的(特别是与DOM事件的东西)。
从整体API的角度来看,您所做的一切保护/私营部门通过“_”(下划线)前缀,我好奇地听到您的想法。 我觉得,静态属性,如:_CLASS_NAMES`和`_EVENTS`等,可能也仅仅是:`CLASS_NAMES`,`活动`无下划线前缀。 这可能只是我的偏好,但感觉过于保护:)
评论由埃里克Ferraiuolo - 7月12日,2011年#
埃里克 ,
感谢您的评论。 事实上,这个诞生于无聊的重复。 我也很喜欢它声明的方式处理的程序性的东西,减少和规范,特别所有_uiSetXxxx方法所产生的组件整洁。
我不想处理与GitHub上和YUI的画廊,所以我不会张贴有。 我不介意如果有人这么做,但我不会做或维护。
DOM事件的事情来,右出Y.View除了我使用类名识别的元素,使整个扩展以来,以及广泛使用,其中的键。 它还涉及挂钩,所以你不需要重复那些从其他类继承时,所有的类层次结构中的事件。
关于保护/私有成员,我与珍妮问团队和保护的根据他的意见,我改变了以前所有市民提出的。
基本上有两个开发者的角色,组件的创建者和组件的用户或“实施者”珍妮提到他们。 这是更好,如果组件开发意味着类成员不弄乱API文档的实现。 在这个意义上说,应该有许多成员,如CONTENT_TEMPLATE,renderUI,HTML_PARSER或Base.ATTRS的Widget从未公开的实现应该甚至不知道他们。
另一方面,如_uiSetTabIndex或_uiSetDisabled成员是非常正确的声明为protected。 因此,在组件的开发模式,你应该总是有显示保护,而作为实现者,你不应该。 这将防止重新实现的功能已经存在像重做这两种方法已经做了代码库中的原始的按钮组件,组件开发人员。
我想,自从珍妮不得不把它最多的球队,在这方面有没有指引,因此,我们将不得不忍受一些不一致的,在现有的组件。
萨蒂扬 - 7月12日,2011年#
更新:
我添加了一个更强大的处理代码:“1”。 单/复数,例如文字处理,是有帮助的:{数量} {1 {数量}“单位”,“单位”}。 该字符串会产生无论是“1单位”或“123部队”根据财产数量价值。
在上面的例子所示,占位符现在可以互相嵌套。 因此,一个占位符的参数可以是另一个占位符的返回值。
我也改变了行事更像占位符{?}:操作员。 ,而不是一个固定的文本,它可以返回任何东西,它的论点说,例如:{? {数量} {数量}“无”}。
作为一个极端的例子,这个模板:
{? {数量}“{数量} {1 {数量}”单位“,”单位“}”,“无”}
会产生文本“无”,“1单位”,“2个单位”,“3个单位”等物业数量连续值。
现在是作为方法_substitute提供的模板处理的方法。
萨蒂扬- 2011年8月13日, #
进一步的变化:
现在的_EVENTS静态属性,每个事件定义哈希听众,需要一个额外的虚拟选择情侣。 的结构_EVENTS:
_EVENTS: {
selector: {
eventType: listener,
....
}
}
选择是使用在_CLASS_NAMES属性创建用于在模板中的HTML元素,这有助于找到他们的类名的键。
有两个特殊的选择:“。” 表示boundingBox的'......“ 部件所在的文件
我现在又增加了其他两个虚拟的选择,这一点,全部大写,是指本身的部件和Y到Y的实例,例如:
_EVENTS: {
THIS: {
visibleChange: '_afterVisibleChange'
},
Y: {
'broadcastingWidget:somethingChange':'_afterSomethingChange'
}
}
Y键,虽然它是指为代表的Y实例,将采取由JavaScript字符串“Y”型。 始终使用虚拟键Ÿ即使你叫YUI的实例别的东西,记得,这是“Y”,只是字符串,而不是实际y实例。
MakeNode只会设置事件侦听器后,从未有过(上)的听众,这是最常见的情况。 如果你要听“前的事件,像往常一样。
萨蒂扬- 2011年8月19日, #