Knockout. js study Note 3

Source: Internet
Author: User

The previous section mainly refers to mutual notifications between viewmodel domains. This section describes mutual notifications between viewmodel and nodes.

Add the following HTML snippet to the body:

 
The name is <span data-bind = "text: fullname" id = "Node"> </span>

Then, cut the $. applybindings mentioned in section 1 to the following:

 
$. Applybindings = function (model, node) {var STR = node. getattribute ("data-bind"); STR = "{" + STR + "}" Var bindings = eval ("0," + Str); For (var key in bindings) {// If Eval is used directly, an error is returned because it finds the fullname console. log (key)} window. onload = function () {VAR model = new myviewmodel (); var node = document. getelementbyid ("Node"); $. applybindings (model, node )}

Unexpected failure, because fullname cannot be found in the window. In knockoutjs, there is a function called buildevalwithinscopefunction to handle this problem:

$. Buildevalwithinscopefunction = function (expression, scopelevels) {var functionbody = "Return (" + expression + ")"; for (VAR I = 0; I

Then change applybindings to the following:

 
$. Applybindings = function (model, node) {var STR = node. getattribute ("data-bind"); STR = "{" + STR + "}" Var fn = $. buildevalwithinscopefunction (STR, 2); var bindings = FN ([node, model]) console. log (bindings. TEXT = model. fullname) // here we associate the viewmodel with the node}

Data-bind defines two things: one is the field in viewmodel and the other is the corresponding operation. Here is text! There is an object named KO. bindinghandlers in knockout, which stores various operations in the format:

Ko. bindinghandlers ['event'] = {'init ': function (element, valueaccessor, allbindingsaccessor, viewmodel) {}}; Ko. bindinghandlers ['submit '] = {'init': function (element, valueaccessor, allbindingsaccessor, viewmodel) {}}; Ko. bindinghandlers ['visable'] = {'update': function (element, valueaccessor) {}} Ko. bindinghandlers ['enable'] = {'update': function (element, valueaccessor) {}}; Ko. bindinghandlers ['disable'] = {'update': function (element, valueaccessor) {}}; Ko. bindinghandlers ['value'] = {'init': function (element, valueaccessor, allbindingsaccessor) {}, 'update': function (element, valueaccessor) {}}; Ko. bindinghandlers ['options'] = {'update': function (element, valueaccessor, allbindingsaccessor) {}}; Ko. bindinghandlers ['selectedomainexception'] = {'init': function (element, valueaccessor, allbindingsaccessor) {}, 'update': function (element, valueaccessor) {}}; Ko. bindinghandlers ['text'] = {'update': function (element, valueaccessor) {Ko. utils. settextcontent (element, valueaccessor () ;}}; Ko. bindinghandlers ['html'] = {'init ': function () {return {'controlsdescendantbindings': true} ;}, 'update': function (element, valueaccessor) {VaR value = Ko. utils. unwrapobservable (valueaccessor (); Ko. utils. sethtml (element, value );}};

Init can be used for calling when an element is bound for the first time. Update is called every time the viewmodel is called.

Now we are playing games.

$. Applybindings = function (model, node) {var STR = node. getattribute ("data-bind"); STR = "{" + STR + "}" Var fn = $. buildevalwithinscopefunction (STR, 2); var bindings = FN ([node, model]); For (var key in bindings) {If (bindings. hasownproperty (key) {var fn = $. bindinghandlers ["text"] ["Update"]; FN (node, bindings [Key]) }}$. bindinghandlers ={}$. bindinghandlers ["text"] = {'update': function (node, obser Vable) {var val = observable () val = NULL? "": Val + ""; if ("textcontent" in node) {// give priority to the standard attribute textcontent node. textcontent = val;} else {node. innertext = val;} // handle ie9 rendering bug if (document.doc umentmode = 9) {node. style. display = node. style. display ;}} window. onload = function () {VAR model = new myviewmodel (); var node = document. getelementbyid ("Node"); $. applybindings (model, node );}

here, we can correctly display planet Earth in span, but when the fullname in viewmodel changes, the span does not change, the reason is that we didn't tie them together. Simply put, we can combine the logic in $. applybindings into a $. computed.

VaR validvaluetype =$. oneobject ("null, Nan, undefined, Boolean, number, string") $. dependencydetection = (function () {VaR _ frames = []; return {begin: function (RET) {_ frames. push (RET) ;}, end: function () {_ frames. pop () ;}, collect: function (Self) {If (_ frames. length> 0) {self. list = self. list | []; var fn = _ frames [_ frames. length-1]; If (self. list. indexof (FN)> = 0) return; self. list. pu Sh (FN) ;}};}) (); $. valuewillmutate = function (observable) {var list = observable. list if ($. type (list, "array") {for (VAR I = 0, El; El = list [I ++];) {El () ;}}$. observable = function (value) {var v = value; // save the last pass to the value of V. RET and it form the closure function RET (NEO) {If (arguments. length) {// setter if (! Validvaluetype [$. Type (NEO)]) {$. Error ("arguments must be primitive type! ") Return ret} If (V! = Neo) {v = NEO; $. valuewillmutate (RET); // send a notification to the dependent party} return ret;} else {// getter $. dependencydetection. collect (RET); // collect return V;} value = validvaluetype [$. type (value)]? Value: void 0; RET (arguments [0]); // you must first run return ret} $. computed = function (OBJ, scope) {// is an inert function and will overwrite itself. // computed is composed of multiple $. the observable component is Var getter, and setter if (typeof OBJ = "function") {getter = OBJ} else if (OBJ & typeof OBJ = "object") {getter = obj. getter; setter = obj. setter; Scope = obj. scope;} var v var ret = function (NEO) {If (arguments. length) {If (typeof setter = "function") {// The setter is not necessarily stored. If (! Validvaluetype [$. Type (NEO)]) {$. Error ("arguments must be primitive type! ") Return ret} If (V! = Neo) {setter. call (scope, Neo); V = NEO; $. valuewillmutate (RET); // send a notification to the dependent party} return ret;} else {$. dependencydetection. begin (RET); // Let the dependency know its existence v = getter. call (scope); $. dependencydetection. end (); Return v ;}} RET (); // you must first execute return ret;} function myviewmodel () {This. firstname = $. observable ('Planet '); this. lastname = $. observable ('global'); this. fullname = $. computed ({getter: function () {return this. firstname () + "" + this. lastname () ;}, setter: function (value) {var lastspacepos = value. lastindexof (""); If (lastspacepos> 0) {// ignore values with no space character this. firstname (value. substring (0, lastspacepos); // Update "firstname" this. lastname (value. substring (lastspacepos + 1); // Update "lastname" }}, scope: This}) ;}$. buildevalwithinscopefunction = function (expression, scopelevels) {var functionbody = "Return (" + expression + ")"; for (VAR I = 0; I

you can download it and check the effect: Click me

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.