手机版

AngularJS框架中数据双向绑定的案例分析

时间:2021-09-16 来源:互联网 编辑:宝哥软件园 浏览:

数据绑定

通过将文本输入框绑定到person.name属性,我们可以使我们的应用程序更加有趣。这一步建立了文本输入框和页面之间的双向绑定。

201634144020971.png  (184293)

在这种情况下,“双向”意味着如果视图改变属性值,模型将“看到”改变,如果模型改变属性值,视图也将“看到”改变。Angular.js已经为您自动设置了这个机制。如果您对这是如何实现的感到好奇,请查看我们稍后将推出的一篇文章,其中深入讨论了digest_loop的操作。

为了建立这种绑定,我们在文本输入框中使用ng-model指令属性,如下所示:

Div-controller=' mycontroller '输入类型=' text' ng-model=' person。name' placeholder='输入您的姓名/H5 hello { { person }。name}}/H5/div现在我们已经建立了一个数据绑定(是的,它是如此简单),让我们看看view是如何改变模型的。

试试看:

201634144103466.jpg  (32188)

当您在文本框中键入时,以下名称会自动更改,这显示了我们数据绑定的方向:从视图到模型。

我们还可以在我们的(客户端)后台更改模型,并看到这种更改会自动反映在前端。为了展示这个过程,让我们在MyController的模型中编写一个计时器函数来更新$scope上的数据。在下面的代码中,我们将创建这个计时器函数,它将每秒计时一次(像时钟一样),并更新$scope上的时钟变量数据:

app.controller('MyController ',function($ scope){ $ scope . person={ name : ' Ari Lerner ' };var updateClock=function(){ $ scope . clock=new Date();};var timer=setInterval(function(){ $ scope。$apply(更新时钟);}, 1000);updateClock();});可以看到,当我们改变模型中时钟变量的数据时,视图会自动更新以反映这种变化。使用大括号,我们可以简单地在视图中显示时钟变量的值:

div-controller=' my controller ' H5 { { clock } }/H5/div交互。

我们之前将数据绑定到了文本输入框。请注意,数据绑定不限于数据。我们还可以使用绑定来调用$scope中的函数(如前所述)。

对于按钮、链接或任何其他DOM元素,我们可以使用另一个指令属性来绑定它们:ng-click。ng-click指令将DOM元素的鼠标点击事件(mousedown browser事件)绑定到一个方法,当浏览器触发DOM元素上的点击事件时,调用绑定的方法。与前面的示例类似,此绑定的代码如下:

div ng-controller=' demo controller ' H4有史以来最简单的添加机器/H4 Button ng-click=' add(1)' class='button ' add/Button ng-click='减法(1)' class=' Button '减法/Button H4 currentcount 3360 { { counter } }/H4/div这两个按钮和链接都将绑定到包含其DOM元素的控制器的所有$scope对象。当它们被鼠标点击时,Angular会调用相应的方法。请注意,当我们告诉Angular调用什么方法时,我们会将方法名写入带引号的字符串中。

app.controller('DemoController ',函数($ scope){ $ scope . counter=0;$scope.add=函数(金额){ $scope.counter=金额;};$scope .核减=函数(金额){ $scope.counter -=金额;};});请参见:

201634144205361.jpg  (448176)

$scope。$手表

$scope。$watch(watchExp,listener,objectEquality);要监视变量的变化,可以使用$scope。$手表功能。该函数有三个参数,分别表示“watchExp”、“当它改变时会发生什么”(listener),以及您是想要监视变量还是对象。当我们检查一个参数时,我们可以忽略第三个参数。例如,以下示例:

$ scope.name=' Ryan$scope。$ watch(function(){ return $ scope . name;},函数(newValue,old value){ console . log($ scope . name已更新!');} );AngularJS将在$scope中注册您的监控功能。您可以在控制台中输出$scope来查看$scope中的已注册项目。

您可以在控制台中看到$scope.name已更改这是因为$scope.name之前的值似乎未定义,但现在我们将其分配给Ryan!

对于$wach的第一个参数,也可以使用字符串。这与提供函数完全相同。从AngularJS的源代码中可以看到,如果使用字符串,将运行以下代码:

if(type of watch exp==' string ' get . constant){ var original fn=watcher . fn;watcher.fn=function(newVal,oldVal,scope) { originalFn.call(this,newVal,oldVal,scope);arrayRemove(数组,观察器);};}这将把我们的watchExp设置为一个函数,它将自动返回我们已经命名的作用域中的变量。

$ watchers $作用域中的$$watchers变量保存了我们定义的所有监视器。如果您查看控制台中的$$watchers,您会发现它是一个对象数组。

$$watchers=[{eq: false,//指示我们是否需要在对象级别检查fn:函数(新值、旧值){}的相等性。//这是我们提供的监听器函数last :‘Ryan’。//变量exp:的最新值function(){},//watch exp function get 3360 function(){ }//watch exp function由angular's编译的}];$watch函数将返回一个注销watch函数。这意味着如果我们使用$scope。$watch要监视一个变量,我们可以稍后通过调用一个函数来停止监视。

$scope。$应用当控制器/指令/等。正在AngularJS中运行,这是一个名为$scope的函数。$apply将在AngularJS中运行。$apply函数将接收一个函数作为参数并运行它,之后$digest函数将在rootScope上运行。

AngularJS的$apply函数代码如下:

$apply:函数(expr){ try { Begin phase(' $ apply ');归还这个。$ eval(expr);} catch(e){ $ exception handler(e);}最后{ clearPhase();请尝试{ $rootScope。$ digest();} catch(e){ $ exception handler(e);扔e;}}}上面代码中的expr参数是调用$scope时传递的参数。$apply()但是大多数情况下,您可能不会使用函数$ apply,所以当您想要使用它时,请记住向它传递一个参数。

让我们看看ng-keydown是如何使用$scope的。$apply。要注册该指令,AngularJS将使用以下代码。

var ngeventections={ };forEach('单击dblclick mouse down mouse up mouse over mouse out mouse move mouse enter mouse key down key up键按提交焦点模糊复制剪切粘贴'。split(')、function(name){ var directioname=directiveNormalize(' ng-' name);ngeventections[directiveName]=[' $ parse ',function($ parse){ return { compile : function($ element,attr){ var fn=$ parse(attr[directiveName]);返回函数ngEventHandler(范围,元素){ element.on(小写(名称),函数(事件){ scope。$apply(function() { fn(scope,{ $ event : event });});});};} };}];});上面的代码所做的是循环不同类型的事件,这些事件可能会在以后被触发,并创建一个名为ng-[一个事件]的新指令。在指令的编译函数中,它在元素上注册一个事件处理程序,该事件处理程序与指令的名称一一对应。当事件被设置时,AngularJS将运行作用域。$apply函数并让它运行一个函数。

只是单向数据绑定吗?上面提到的ng-keydown只能更改与元素值相关的$作用域中的值——这只是一个数据绑定。这就是为什么这个指令被称为ng-keydown,只有当keydown事件被触发时,它才能给我们一个新的值。

但是我们想要的是双向数据绑定!现在我们来看看ng-model。当您使用ng-model时,您可以使用双向数据绑定这正是我们想要的。AngularJS使用$scope。$watch(查看模型)和$scope。$apply (model to view)来实现这个功能。

Ng-model将把事件处理指令(比如keydown)绑定到我们使用的输入元素上——这就是$scope的作用域。调用$apply!和$scope。在指令控制器中调用$watch。您可以在下面的代码中看到这一点:

$scope。$watch(函数ngModelWatch() { var值=ngModelGet($ scope);//如果作用域模型值和ngModel值不同步,如果(ctrl。$modelValue!==value) { var formatters=ctrl。$formatters,idx=formatters.lengthctrl。$modelValue=值;while(idx-){ value=formatters[idx](值);} if (ctrl。$viewValue!==值){ ctrl。$ viewValue=valuectrl。$ render();} }返回值;});如果只向$scope传递一个参数。$watch在调用它时,无论作用域发生什么变化,都会调用这个函数。在ng-model中,这个函数用于检查模型和视图是否同步。如果没有,它将使用新值更新模型数据。这个函数将返回一个新的值,当它在$digest函数中运行时,我们将知道这个值是什么!

为什么我们的听众没有被触发?如果我们在$scope的监听器函数中停止监听。$watch,即使我们更新$scope.name,侦听器也不会被触发。

如前所述,AngularJS将运行$scope。$apply在每个指令的控制器函数中。如果我们看看$scope的代码。$apply函数,我们会发现它只会在开始调用controller函数后运行$digest函数——这意味着如果我们立即停止侦听,$scope。$watch函数甚至不会被调用!但是它到底是如何工作的呢?

$digest函数将由$scope调用。$在$rootScope中应用。它将在$rootScope中运行摘要循环,然后遍历每个范围并在每个范围上运行循环。在一个简单的例子中,摘要循环将触发$$watchers变量中的所有watchExp函数,将它们与最新值进行比较,如果值不同,则触发侦听器。

当摘要循环运行时,它将遍历所有侦听器,然后再次循环。只要循环找到一个“脏值”,循环就会继续。如果watchExp的值与最新值不相同,则此循环将被视为查找脏值。理想情况下,它将运行一次。如果它运行超过10次,您将看到一个错误。

因此,当$scope。$apply runs,$digest也将运行,它将循环通过$$watchers。如果发现watchExp不等于最新值,该更改将触发事件侦听器。在AngularJS中,$scope。$apply在模型的值可能改变时运行。这就是为什么当您在AngularJS之外更新$scope时,例如,在setTimeout函数中,您需要手动运行$scope。$apply():这可以让AngularJS意识到它的作用域已经改变了。

就创建我们自己的脏值检查而言,我们已经可以创建一个小的简化版本的脏值检查。当然,相比之下,AngularJS中实现的脏值检查更高级,它提供了疯狂的异步队列和其他高级功能。

设置ScopeScope只是一个函数,它包含了我们想要存储的任何对象。我们可以扩展这个函数的原型对象来复制$digest和$watch。我们不需要$apply方法,因为我们不需要在范围的上下文中执行任何函数——我们只需要简单地使用$digest。我们的范围代码如下:

var Scope=function() { this .$ $ watchers=[];};范围。原型。$ watch=function(){ };范围。原型。$ digest=function(){ };我们的$手表函数需要接受两个参数,watchExp和听众。当$手表被调用时,我们需要将它们推进入到范围的$ $观察者数组中。

var Scope=function() { this .$ $ watchers=[];};范围。原型。$ watch=function(WatchExP,listener ) { this .$ $观察者。push({ WatchExp : WatchExp,listener : listener | | function(){ } });};范围。原型。$ digest=function(){ };你可能已经注意到了,如果没有提供听众,我们会将听众设置为一个空函数 这样一来我们可以$手表所有的变量。

接下来我们将会创建$摘要。我们需要来检查旧值是否等于新的值,如果二者不相等,监听器就会被触发。我们会一直循环这个过程,直到二者相等。这就是"脏值"的来源 脏值意味着新的值和旧的值不相等!

var Scope=function() { this .$ $ watchers=[];};范围。原型。$ watch=function(WatchExP,listener ) { this .$ $观察者。push({ WatchExp : WatchExp,listener : listener | | function(){ } });};范围。原型。$ digest=function(){ var dirty;do { dirty=false for(var I=0;我喜欢这个$ $ watchers . lentigi){ var new value=this .$ $观察者[i].watchExp(),oldValue=this .$ $观察者[i].最后;if(oldValue!==newValue ) { this .$ $观察者[i].侦听器(新值、旧值);脏=真这个。$ $观察者[i].last=NewValue } } } while(dirty);};接下来,我们将创建一个作用域的实例。我们将这个实例赋值给$scope。我们接着会注册一个监听函数,在更新$范围之后运行$digest!

var Scope=function() { this .$ $ watchers=[];};范围。原型。$ watch=function(WatchExP,listener ) { this .$ $观察者。push({ WatchExp : WatchExp,listener : listener | | function(){ } });};范围。原型。$ digest=function(){ var dirty;do { dirty=false for(var I=0;我喜欢这个$ $ watchers . lentigi){ var new value=this .$ $观察者[i].watchExp(),oldValue=this .$ $观察者[i].最后;if(oldValue!==newValue ) { this .$ $观察者[i].侦听器(新值、旧值);脏=真这个。$ $观察者[i].last=NewValue } } } while(dirty);};var $ Scope=new Scope();$ scope.name=' Ryan$scope .$ watch(function(){ return $ scope。姓名;},函数(newValue,oldValue ) { console.log(newValue,old value);} );$scope .$ digest();成功了!我们现在已经实现了脏值检查(虽然这是最简单的形式)!上述代码将会在控制台中输出下面的内容:

赖安(男子名)未定义这正是我们想要的结果$ scope。名字之前的值是未定义,而现在的值是瑞安。

现在我们把$摘要函数绑定到一个投入元素的按键事件上。这就意味着我们不需要自己去调用$摘要。这也意味着我们现在可以实现双向数据绑定!

var Scope=function() { this .$ $ watchers=[];};范围。原型。$ watch=function(WatchExP,listener ) { this .$ $观察者。push({ WatchExp : WatchExp,listener : listener | | function(){ } });};范围。原型。$ digest=function(){ var dirty;do { dirty=false for(var I=0;我喜欢这个$ $ watchers . lentigi){ var new value=this .$ $观察者[i].watchExp(),oldValue=this .$ $观察者[i].最后;if(oldValue!==newValue ) { this .$ $观察者[i].侦听器(新值、旧值);脏=真这个。$ $观察者[i].last=NewValue } } } while(dirty);};var $ Scope=new Scope();$ scope.name=' Ryanvar元素=文档。query selectorall(' input ');元素[0]。onkeyup=function(){ $ scope。name=元素[0]。价值;$scope .$ digest();};$scope .$ watch(function(){ return $ scope。姓名;},函数(新值,旧值){控制台。日志('输入值已更新-现在是新值');元素[0]。value=$ scope . name });var updatescopevalue=function updatescopevalue(){ $ scope。姓名='鲍勃';$scope .$ digest();};使用上面的代码,无论何时我们改变了投入的值,$scope中的名字属性都会相应的发生变化。这就是隐藏在AngularJS神秘外衣之下数据双向绑定的秘密!

版权声明:AngularJS框架中数据双向绑定的案例分析是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。