十个有用的knockout binding用法
来源:互联网 发布:淘宝话费充值好做吗 编辑:程序博客网 时间:2024/06/01 01:33
十个有用的knockout binding用法
Knockout.js is a very powerful library to build interactive web applications in. The creators of knockout tried really hard to keep it as light-weight as possible (and for good reason). That being said, if you're using knockout to do a lot, it often isn't long before your markup is filled with not-so-pretty-looking data-bind
attribute selectors.
One of the most criminally unused features of knockout tends to be custom binding handlers. I've talked at length about them before, but I thought it would be nice to simply enumerate a couple of really simple binding handlers which I have found invaluable... and if not, at least aesthetically helpful!
href and src attributes
It's pretty common to bind <img />
elements or anchor tags programmatically... but how annoying is it to have to use the attr
binding handler? You can quickly create "shortcut" binding handlers that give you a cleaner syntax.
ko.bindingHandlers.href ={ update:function(element, valueAccessor){ ko.bindingHandlers.attr.update(element,function(){return{ href: valueAccessor()}});}};ko.bindingHandlers.src ={ update:function(element, valueAccessor){ ko.bindingHandlers.attr.update(element,function(){return{ src: valueAccessor()}});}};
Thus we now have:
<imgdata-bind="src: imgSrc"/><adata-bind="href: myUrl">Click Me</a><!-- instead of --><imgdata-bind="attr: { src: imgSrc }"/><adata-bind="attr: { href: myUrl }">Click Me</a>
Hidden
It's easy to quickly get annoyed by the asymmetry of some of the bindings which consume booleans. This is because knockout's binding parser allows you to simply reference an observable by name... ie, text: myTextObservable
which is equivalent totext: myTextObservable()
, however, to do anything more complicated than that, the parser expects a valid expression, in which case you then need to evaluate the observable and do it the second way.
A common example I run into is the visible
binding (one of the top 3 most commonly used bindings in my experience). However, sometimes what you really want is a hidden
binding. No problem!
ko.bindingHandlers.hidden ={ update:function(element, valueAccessor){var value = ko.utils.unwrapObservable(valueAccessor()); ko.bindingHandlers.visible.update(element,function(){return!value;});}};
And thus, we have:
<formdata-bind="hidden: hideForm"> ...</form><!-- instead of --><formdata-bind="visible: !hideForm()"> ...</form>
Instant Value
Of course any interactive web application ought to have user inputs, and thus we find ourselves using the value
binding handler.
Well, part of the fun of using a fancy javascript library such as knockout.js is to give our users immediate feedback. As a result, I found myself using the value
binding with it's binding option valueUpdate: 'afterkeydown'
more often than not! If you're like me, writing that darn option every time is a sight for sore eyes... let's get rid of it:
ko.bindingHandlers.instantValue ={ init:function(element, valueAccessor, allBindings){var newAllBindings =function(){// for backwards compatibility w/ knockout < 3.0return ko.utils.extend(allBindings(),{ valueUpdate:'afterkeydown'});}; newAllBindings.get=function(a){return a ==='valueupdate'?'afterkeydown': allBindings.get(a);}; newAllBindings.has =function(a){return a ==='valueupdate'|| allBindings.has(a);}; ko.bindingHandlers.value.init(element, valueAccessor, newAllBindings);}, update: ko.bindingHandlers.value.update};
And thus, we can now use:
<inputdata-bind="instantValue: val"/><!-- instead of --><inputdata-bind="value: val, valueUpdate: 'afterkeydown'"/>
Note: If you want, you can even replace the original value
binding handler. I don't recommend this, since you are overriding some default behavior of knockout, but if you like to live on the edge, go for it:
(function(original)){var extend = ko.utils.extend, unwrap = ko.utils.unwrapObservable; ko.bindingHandlers.value ={ init:function(element, valueAccessor, allBindingsAccessor){var origBindings = allBindingsAccessor(), origValueUpdate = origBindings.valueUpdate, newAllBindings =function(){return origValueUpdate ===undefined? extend(origBindings,{valueUpdate:'afterkeydown'}: origBindings;};return original.init(element, valueAccessor, newAllBindings);}, update: original.update };}(ko.bindingHandlers.value));
Toggle
This little gem can really help reduce some clutter in your viewmodels. Often times you need to bind a click handler that simply toggles (or negates) a boolean observable. voila:
ko.bindingHandlers.toggle ={ init:function(element, valueAccessor){var value = valueAccessor(); ko.applyBindingsToNode(element,{ click:function(){ value(!value());}});}};
Usage:
<buttondata-bind="toggle: isHidden">Show / Hide</button><!-- instead of --><buttondata-bind="click: function(){isHidden(!isHidden());}">Show / Hide</button>
Time Ago
Not every application needs this, but I've certainly used it plenty. Dates are a tough thing to get right in javascript. Especially when your users could be from anywhere in the world, in any time zone. There are certainly more sophisticated approaches, but one approach is just to ditch the exact time and show a relative time.
While you can certainly replace my hacky relative-time function with something more complex, like moment.js, but this did the trick for me since I just needed "time ago" dates (and only dates in the past).
function toTimeAgo (dt){var secs =(((newDate()).getTime()- dt.getTime())/1000), days =Math.floor(secs /86400);return days ===0&&( secs <60&&"just now"|| secs <120&&"a minute ago"|| secs <3600&&Math.floor(secs /60)+" minutes ago"|| secs <7200&&"an hour ago"|| secs <86400&&Math.floor(secs /3600)+" hours ago")|| days ===1&&"yesterday"|| days <31&& days +" days ago"|| days <60&&"one month ago"|| days <365&&Math.ceil(days /30)+" months ago"|| days <730&&"one year ago"||Math.ceil(days /365)+" years ago";};ko.bindingHandlers.timeAgo ={ update:function(element, valueAccessor){var val = unwrap(valueAccessor()), date =newDate(val),// WARNING: this is not compatibile with IE8 timeAgo = toTimeAgo(date);return ko.bindingHandlers.html.update(element,function(){return'<time datetime="'+ encodeURIComponent(val)+'">'+ timeAgo +'</time>';});}};
With this you can use:
<divdata-bind="timeAgo: dateCreated"></div>
Which will result in the following machine-and-human-friendly HTML being created:
<divdata-bind="timeAgo: dateCreated"><timedatetime="2014-03-04T03:19:06.627">3 hours ago</time></div>
Currency
It seems more often than not, the need to format currency arises. Why fight it? Although it might be overly simplistic for some use cases, the following binding has done the job reasonably well for me.
ko.bindingHandlers.currency ={ symbol: ko.observable('$'), update:function(element, valueAccessor, allBindingsAccessor){return ko.bindingHandlers.text.update(element,function(){var value =+(ko.utils.unwrapObservable(valueAccessor())||0), symbol = ko.utils.unwrapObservable(allBindingsAccessor().symbol ===undefined? allBindingsAccessor().symbol : ko.bindingHandlers.currency.symbol);return symbol + value.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g,"$1,");});}};
This allows you to globally change the "symbol" being used as currency, as well as replace it only locally.
For example:
<spandata-bind="currency: price"></span><!-- Use a different dollar sign --><spandata-bind="currency: price, symbol: '€'"></span>
Which, with price = 1853.251;
will produce standard formatted currency:
<spandata-bind="currency: price">$1,853.25</span><!-- Use a different dollar sign --><spandata-bind="currency: price, symbol: '€'">€1,853.25</span>
Markdown
Although this requires an external dependency, it's worth simply pointing out how simple it is to integrate an external library into knockout. Look how easy it is to pull in a markdown compiler:
var converter =Markdown.getConverter();ko.bindingHandlers.markdown ={ update:function(element, valueAccessor){return ko.bindingHandlers.html.update(element,function(){return converter.makeHtml(ko.utils.unwrapObservable(valueAccessor()));});}};
And then it's as easy as:
<divdata-bind="markdown: markdownSource"></div>
Stop Binding
I can't take credit for this incredibly simple but useful binding. Ryan Neimeyer originally introduced me to this concept.
For large knockout applications, it is inevitable that you will want to bind specific subsections of HTML with a viewmodel that's isolated from everything else. It is important to make sure that that HTML subtree stays completely unbinded from anything else. Well here is a simple way to do it:
ko.bindingHandlers.stopBinding ={ init:function(){return{ controlsDescendantBindings:true};}};ko.virtualElements.allowedBindings.stopBinding =true;
Usage:
<!-- ko stopBinding: true --><divid="widget"> ...</div><!-- /ko -->
toJSON
There may be a use for this in an actual app, but I have mostly found it useful as a debugging tool.
Knockout comes with a useful utility function, ko.toJSON
. Although it has many other uses, you can use it to quickly bind entire viewmodels (which may have a bunch of observable properties) and quickly see what sort of changes are going on in your viewmodel. So simple:
ko.bindingHandlers.toJSON ={ update:function(element, valueAccessor){return ko.bindingHandlers.text.update(element,function(){return ko.toJSON(valueAccessor(),null,2);});}};
Usage:
<h3>Debug Info:</h3><predata-bind="toJSON: $root"></pre>
That's all I got for today. Have any useful binding handlers that you've built for knockout? Share them below!
- 十个有用的knockout binding用法
- 十个有用的网站
- Binding的参数用法
- 十个有用的Sass Mixins
- 十个有用的CSS选择器
- 十个有用的php片段
- Binding,BindingManagerBase, BindingContext的用法
- Binding,BindingManagerBase, BindingContext的用法
- Visual Studio 十个有用的小插件
- Windows XP系统有用的十个命令
- 十个有用的 Google Chrome 扩展
- 十个个非常有用的电脑技术
- 十个超级有用的PHP代码片段
- 十个超级有用的PHP代码片段
- 十个超级有用的PHP代码片段
- 十个超级有用的PHP代码片段
- 使用knockout.js 完成template binding
- flex 十个nb的用法
- linux zip、tar ,linux下打包文件
- 【FAQ】Jenkins如何从slave到slave拷贝文件?
- 我的学习之路(二)
- php7 初体验 - windows下安装和性能测试
- Error code of Media server (MediaPlayer内核异常码说明)
- 十个有用的knockout binding用法
- Unclosed character class near index 2 ],[
- 读书笔记 -- 002_数据预处理_数据集成
- 形式化方法(一) 逻辑部分概念梳理
- Linux Mysql无法启动 /var/run/mysqld/mysqld.sock
- Objective-C获取 iPhone全部字体的Fontfamily和FontName
- 与Servlet相关类的层次结构及HttpServlet的生命周期
- Linux C连接Mysql数据库
- js获取当前页面的url