作为一名初学者来说,一篇篇的按顺序看官网上的文档的确是一件很痛苦的事情,毕竟它的排列也并非是由浅及深的排列,其中的顺序也颇耐人寻味,于是这篇文章我又跳过了部分,进而进入到具体的bindings环节,首先接触到的是Controlling text and appearance。
Visible binding会依据绑定的数据来决定相应的DOM元素是否隐藏。
我们首先在js文件部分定义一个view model,这里我创建的是一个object而不是一个构造函数,个人分析认为,object类型的定义就是一种persistent view model,而构造函数类型的定义就是一种temporary view model,这个也许和pure computed observables中的性能有关,可和knockout学习笔记(四)联系起来。
相应的js部分如下:
1 var myViewModel = {2 shouldShowMessage: ko.observable()3 }4 5 ko.applyBindings(myViewModel);
在html部分的测试元素如下:
1Test for visible binding
在这种情况下,由于我没有给shouldShowMessage赋一个初始值,所以应该是undefined,即对应的false,实际上页面显示出来的是:
可以看出我们添加的p元素并没有显示出来,接下来我在js部分修改了shouldShowMessage的值:
1 myViewModel.shouldShowMessage(true);
页面变成了这样:
此时的页面中显示出了p元素。
有关visible binding的参数:当参数是false或相当于false的值时,binding会将yourElement.style.display设置为none使得订阅的元素隐藏,而这个设定也会覆盖掉所有你自己定义的CSS样式,我猜测这是和CSS样式覆盖规则有关,knockoutjs也许就是将这一个赋值的优先级设置为最高或者是合理利用了相应的顺序,这个可以以后研究knockoutjs的源码的时候查一查。当参数是true或是相当于true的值时,binding会删去相应的yourElement.style.display,从而让用户自定义的CSS样式作用于订阅的元素上。
当参数是一个observable时,每当参数变化,UI部分的元素都会更新自己的visible属性,而当参数不是observable时,UI部分的元素只会对visible属性设定一次。当然,参数也可以是一个函数,binding会根据function返回的值是true还是false来决定是否显示相应的DOW element。
Text binding会使得相应的DOM element的text部分变为参数的值。
js部分的代码如下:
1 var myViewModel = {2 myText: ko.observable()3 }4 5 ko.applyBindings(myViewModel);6 7 myViewModel.myText("Hello world!");
html部分的代码如下:
1This is my text:
页面显示效果如下:
利用text binding的时候,element所订阅的view model的值会覆盖掉html中现有的text部分,如果参数是一个observable,每当参数变化的时候,UI都会更新自己的text部分,而如果参数不是observable,UI只会在最初的时候设定好text,以后不再更新。如果参数不是数字类型也不是字符串类型,则binding会调用yourParameter.toString()来将其转换为text再替换掉text部分的内容。
注意一:如果我们希望通过函数来计算或者说控制输出到text部分的值,我们可以将其绑定至一个computed observable,js部分如下:
1 var myViewModel = { 2 myText: ko.observable(), 3 myComputed: ko.pureComputed(function() { 4 return myViewModel.myText() > 50 ? "Big" : "Small"; 5 }) 6 } 7 8 ko.applyBindings(myViewModel); 9 10 myViewModel.myText(60);
html部分如下:
1This is my text:
页面效果如下:
在上例中的view model是作为object直接声明的,这是为了能够方便地更改其中的属性(myText)。如果将上例中的myViewModel.myText替换为this.myText,必然会报错,因为之前的文章已经提到过,function并不能算作是object的一部分,不过computed提供了一个指定this的值的方式,如果修改成以下模样:
1 var myViewModel = {2 myText: ko.observable(),3 myComputed: ko.pureComputed(function() {4 return this.myText() > 50 ? "Big" : "Small";5 }, this)6 }
结果依然会报错:undefined is not a function。这里也许需要理清的就是函数中的this和object中的this的问题,留作以后研究。之后我换了一种方式,换成了构造函数:
1 function MyViewModel(myText) {2 this.myText = ko.observable(myText);3 this.myComputed = ko.pureComputed(function() {4 return this.myText() > 50 ? "Big" : "Small";5 }, this);6 }
这样是可行的,我就猜想会不会是之前的例子里没有给属性添加this,经测试,object里面完全不支持this,这也是自己对javascript的理解不清晰所致,于是可以认为,在object的定义形式中,computed内所关联的observable需带上这个view model的全名,而在function的定义形式中,则可以使用this来指代当前的实例。这里又涉及到这两种定义方式的区别问题,留作以后研究。
当然,我们也可以直接将computed部分最后返回的结果用data-bind来绑定,如下:
1This is my text:
这样我们就不需要再定义一个computed observable。
注意二:如果我们传入text的参数中含有html的标签,页面中并不会将其以标签的形式展现,而是会以字符串的方式展现出来,例如:
1 myViewModel.myText("Hello world!");
显示出来会是:
这样也可以防止HTML或是script的注入攻击。而有关html标签的绑定则需要参考html binding。
注意三:在某些时候,我们需要在不指定元素的情况下直接插入text,此时我们可以使用无容器的语法来定义一个text binding。例如在option element中是不允许包含其他元素的,如果我们如下定义data-bind
1
我们并不能得到view model中的observable,无容器的语法则是这样写的:
1
<!--ko-->和<!--/ko-->扮演着开始标志和结束标志,它们定义了一个虚拟元素,knockout语法能够理解这种写法并像对待一个最真实元素那样去绑定相应的view model内的值。
Html binding其实与text binding类似,只不过它的参数一般是带有html标签的,这样就可以自定义想要绑定的html元素。
下面是个很简单的例子:
1 var myViewModel = {2 myHtml: ko.observable(),3 }4 5 ko.applyBindings(myViewModel);6 7 myViewModel.myHtml("Google");
html部分如下:
1My html element is:
页面的效果如下:
html binding的其他特性与text binding类似,但是使用html binding的时候要特别注意安全问题,防止注入式攻击等。
CSS binding主要用于根据view model的修改来更改UI中相应元素的class,从而依照CSS文件中已经定义好的样式来体现在页面中。一个简单的static css binding例子如下:
js部分:
1 var myViewModel = {2 myTest : ko.observable(),3 }4 5 ko.applyBindings(myViewModel);6 7 myViewModel.myTest(20);
html部分:
1Test for css binding
css部分:
1 .redText {2 color: red;3 }
页面显示效果如下:
另外,我们也可以通过绑定一个computed observable来动态指定css class的值,这样的绑定称为dynamic css binding,简单的例子如下:
js部分:
1 var myViewModel1 = { 2 myTest: ko.observable(), 3 myComputed: ko.pureComputed(function() { 4 return myViewModel1.myTest() > 0 ? "redText" : "blueText"; 5 }) 6 }; 7 8 var myViewModel2 = { 9 myTest: ko.observable(),10 myComputed: ko.pureComputed(function() {11 return myViewModel2.myTest() > 0 ? "redText" : "blueText";12 })13 };14 15 ko.applyBindings(myViewModel1, document.getElementById("redText"));16 ko.applyBindings(myViewModel2, document.getElementById("blueText"));17 18 myViewModel1.myTest(20);19 myViewModel2.myTest(-20);
html部分:
1This is red text.
2This is blue text.
css部分:
1 .redText {2 color: red;3 }4 5 .blueText {6 color: blue;7 }
页面显示效果如下:
css bidning是十分灵活的,对于static css binding,我们一般是以data-bind="css: {cssClass: viewModelProperty}"的形式来绑定css class,cssClass会根据viewModelProperty返回的值是true还是false来决定这个class现在是否需要使用。另外,我们也可以一次性设置多个css classes,简单的示例如下:
js部分:
1 var myViewModel = {2 redTextDecision: ko.observable(),3 textDecorationDecision: ko.observable()4 };5 6 ko.applyBindings(myViewModel);7 8 myViewModel.redTextDecision(true);9 myViewModel.textDecorationDecision(true);
html部分:
1This is red text, and its text-decoration is underline.
css部分:
1 .redText {2 color: red;3 }4 5 .textDecoration {6 text-decoration: underline;7 }
页面显示效果如下:
在上例中,我们是使用两个view model的property来决定两个不同的css class,KO也允许我们使用一个view model property来决定多个css class,可将上例的html部分改成如下形式:
1This is red text, and its text-decoration is underline.
这里为了简便,并没有更改相应的view model property的名称,可以看到,当对多个css class进行设定的时候,我们需要将它们统一包含在引号中。
如果绑定的view model property属性是一个observable,则UI会根据该observable的变化来动态的增加或删去被绑定的css class,否则,UI只会在开始阶段设定一次css class,之后不再更新。
dynamic css binding的一般形式是这样的:data-bind="css: viewModelProperty",UI会根据viewModelProperty返回的字符串(property本身可以是一个代表css class的字符串)来决定添加的css class的名称。如果property是一个observable,则在该observable改变的时候,UI会删去之前该observable所添加的class并重新设置为当前observable的值。在dynamic css binding中,我们可以让viewModelProperty添加多个css class,只需将这些css class以空格分开并统一以字符串的方式返回即可。一个简单的例子如下:
js部分:
1 var myViewModel = {2 textDecorationDecision: ko.observable("textDecoration redText")3 };4 5 ko.applyBindings(myViewModel);
html部分:
1This is red text, and its text-decoration is underline.
页面显示效果如下:
对于某些带有特殊符号的css class,比如red-text,我们不能直接在data-bind中以data-bind="css: {red-text: redTextDecision}"的方式来设定,而是应该在这类css class外添加一对引号使其成为字符串常量,变成这样:data-bind="css: {'red-text': redTextDecision}"。
style binding是与css binding不同的控制页面样式的方式,css binding需要依赖css文件中对应的样式表,借由增加或删除css class来改变样式,而style binding则是直接在相应的元素上添加或删除style来改变页面的样式。一个简单的例子如下:
js部分:
1 var myViewModel = {2 redTextDecision: ko.observable("red")3 };4 5 ko.applyBindings(myViewModel);
html部分:
1This is red text.
页面显示效果如下:
与css binding类似,我们也可以一次性设定多个style,如下:
1This is red text.
如果绑定的viewModelProperty是一个observable,则每次observable改变的同时,UI会对对应的style进行更新;否则,UI只会设定一次style,之后不再改变。
有些style是带有特殊符号的,比如"text-decoration",比如"font-size"。在这种情况下,直接在data-bind中填写style的原名是不妥的,我们须将其变更为"textDecoration"和"fontSize",其他类似的情形以此类推。
attr binding用于绑定某些元素的attribute,比如说a元素的title、href,img元素的src等。一个简单的例子如下:
js部分:
1 var myViewModel = {2 url: ko.observable("http://www.google.com"),3 description: ko.observable("This is google's website"),4 imgUrl: ko.observable("YukiNagato.JPG")5 };6 7 ko.applyBindings(myViewModel);
html部分:
页面显示效果如下:
如果绑定的viewModelProperty是一个observable,则每次observable改变时,UI均会更新相应的attribute;否则,UI只会设定一次attribute,之后不再更新。
当attribute name中带有连字符号时,我们只需在其外部添加一对引号即可。