Angular データの双方向バインディングを理解する_AngularJS
AngularJS は、多くの Google 製品で使用されている優れたフロントエンド JS フレームワークです。 AngularJS には多くの機能があり、その最も核となる機能は、MVVM、モジュール化、自動双方向データ バインディング、セマンティック タグ、依存関係注入などです。
1. 双方向データバインディングとは
Angular は双方向のバインド メカニズムを実装しています。いわゆる双方向バインディングとは、インターフェース上の操作をリアルタイムにデータに反映でき、データの変化をリアルタイムにインターフェースに表示できるということに他なりません。
最も単純な例は次のとおりです:
<div ng-controller="CounterCtrl"> <span ng-bind="counter"></span> <button ng-click="counter++">increase</button> </div>function CounterCtrl($scope) { $scope.counter = 1; }
この例は非常に単純で、ボタンをクリックするたびにインターフェイス上の数字が 1 つずつ増加します。
2. 双方向データバインディングの原則
1. 深い理解
ユーザーが携帯電話リストの表示順序を制御できるようにします。動的並べ替えは、新しいモデル プロパティを追加し、それをイテレータと統合し、残りの作業をデータ バインディングに実行させることで実装できます。
テンプレート (app/index.html)
Search: <input ng-model="query"> Sort by: <select ng-model="orderProp"> <option value="name">Alphabetical</option> <option value="age">Newest</option> </select> <ul class="phones"> <li ng-repeat="phone in phones | filter:query | orderBy:orderProp"> {{phone.name}} <p>{{phone.snippet}}</p> </li> </ul>
index.html に次の変更が加えられました:
まず、ユーザーが提供された 2 つの並べ替え方法から選択できるように、orderProp という
次に、フィルター filter の後に orderBy フィルターを追加して、イテレーターに入るデータを処理します。 orderBy フィルターは配列を入力として受け取り、コピーを作成し、そのコピーを反復子に出力する前に並べ替えます。
AngularJS は、select 要素と orderProp モデルの間に双方向のバインディングを作成します。次に、orderProp が orderBy フィルターへの入力として使用されます。
データ モデルが変更されると (たとえば、ユーザーがドロップダウン メニューで別の順序を選択するなど)、AngularJS のデータ バインディングによってビューが自動的に更新されます。不器用な DOM 操作は必要ありません。
コントローラー (app/js/controllers.js)
function PhoneListCtrl($scope) { $scope.phones = [ {"name": "Nexus S", "snippet": "Fast just got faster with Nexus S.", "age": 0}, {"name": "Motorola XOOM™ with Wi-Fi", "snippet": "The Next, Next Generation tablet.", "age": 1}, {"name": "MOTOROLA XOOM™", "snippet": "The Next, Next Generation tablet.", "age": 2} ]; $scope.orderProp = 'age'; }
修改了phones模型—— 手机的数组 ——为每一个手机记录其增加了一个age属性。根据age属性来对手机进行排序。
在控制器代码里加了一行让orderProp的默认值为age。如果我们不设置默认值,这个模型会在用户在下拉菜单选择一个顺序之前一直处于未初始化状态。
现在我们该好好谈谈双向数据绑定了。注意到当应用在浏览器中加载时,“Newest”在下拉菜单中被选中。这是因为我们在控制器中把orderProp设置成了‘age'。所以绑定在从我们模型到用户界面的方向上起作用——即数据从模型到视图的绑定。现在当你在下拉菜单中选择“Alphabetically”,数据模型会被同时更新,并且手机列表数组会被重新排序。这个时候数据绑定从另一个方向产生了作用——即数据从视图到模型的绑定。
2.原理分析
下面的原理想法实际上很基础,可以被认为是3步走计划:
- 我们需要一个UI元素和属性相互绑定的方法
- 我们需要监视属性和UI元素的变化
- 我们需要让所有绑定的对象和元素都能感知到变化
还是有很多方法能够实现上面的想法,有一个简单有效的方法就是使用PubSub模式。 这个思路很简单:我们使用数据特性来为HTML代码进行绑定,所有被绑定在一起的JavaScript对象和DOM元素都会订阅一个PubSub对象。只要JavaScript对象或者一个HTML输入元素监听到数据的变化时,就会触发绑定到PubSub对象上的事件,从而其他绑定的对象和元素都会做出相应的变化。
3.发布者-订阅者模式(PubSub模式)
设计该模式背后的主要动力是促进形成松散耦合。在这种模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象的特定活动并在状态改变后获得通知。订阅者也称为观察者,而补观察的对象称为发布者或主题。当发生了一个重要的事件时,发布者将会通知(调用)所有订阅者并且可能经常以事件对象的形式传递消息。
假设有一个发布者paper,它每天出版报纸及月刊杂志。订阅者joe将被通知任何时候所发生的新闻。
该paper对象需要一个subscribers属性,该属性是一个存储所有订阅者的数组。订阅行为只是将其加入到这个数组中。当一个事件发生时,paper将会循环遍历订阅者列表并通知它们。通知意味着调用订阅者对象的某个方法。故当用户订阅信息时,该订阅者需要向paper的subscribe()提供它的其中一个方法。
paper也提供了unsubscribe()方法,该方法表示从订阅者数组(即subscribers属性)中删除订阅者。paper最后一个重要的方法是publish(),它会调用这些订阅者的方法,总而言之,发布者对象paper需要具有以下这些成员:
- ①subscribers 一个数组
- ②subscribe() 将订阅者添加到subscribers数组中
- ③unsubscribe() 从subscribers数组中删除订阅者
- ④publish() 循环遍历subscribers数组中的每一个元素,并且调用他们注册时所提供的方法
所有这三种方法都需要一个type参数,因为发布者可能触发多个事件(比如同时发布一本杂志和一份报纸)而用户可能仅选择订阅其中一种,而不是另外一种。
由于这些成员对于任何发布者对象都是通用的,故将它们作为独立对象的一个部分来实现是很有意义的。那样我们可将其复制到任何对象中,并将任意给定对象变成一个发布者。
三.用jQuery做一个简单的实现
对于DOM事件的订阅和发布,用jQuery实现起来是非常简单的,接下来我们就是用Jquery比如下面:
function DataBinder( object_id ) { // Use a jQuery object as simple PubSub var pubSub = jQuery({}); // We expect a `data` element specifying the binding // in the form: data-bind-<object_id>="<property_name>" var data_attr = "bind-" + object_id, message = object_id + ":change"; // Listen to change events on elements with the data-binding attribute and proxy // them to the PubSub, so that the change is "broadcasted" to all connected objects jQuery( document ).on( "change", "[data-" + data_attr + "]", function( evt ) { var $input = jQuery( this ); pubSub.trigger( message, [ $input.data( data_attr ), $input.val() ] ); }); // PubSub propagates changes to all bound elements, setting value of // input tags or HTML content of other tags pubSub.on( message, function( evt, prop_name, new_val ) { jQuery( "[data-" + data_attr + "=" + prop_name + "]" ).each( function() { var $bound = jQuery( this ); if ( $bound.is("input, textarea, select") ) { $bound.val( new_val ); } else { $bound.html( new_val ); } }); }); return pubSub; }
对于上面这个实现来说,下面是一个User模型的最简单的实现方法:
function User( uid ) { var binder = new DataBinder( uid ), user = { attributes: {}, // The attribute setter publish changes using the DataBinder PubSub set: function( attr_name, val ) { this.attributes[ attr_name ] = val; binder.trigger( uid + ":change", [ attr_name, val, this ] ); }, get: function( attr_name ) { return this.attributes[ attr_name ]; }, _binder: binder }; // Subscribe to the PubSub binder.on( uid + ":change", function( evt, attr_name, new_val, initiator ) { if ( initiator !== user ) { user.set( attr_name, new_val ); } }); return user; }
现在我们如果想要将User模型属性绑定到UI上,我们只需要将适合的数据特性绑定到对应的HTML元素上。
// javascript var user = new User( 123 ); user.set( "name", "Wolfgang" ); // html <input type="number" data-bind-123="name" />
这样输入值会自动映射到user对象的name属性,反之亦然。到此这个简单实现就完成了。
四.Angular实现数据双向绑定
Angular主要通过scopes实现数据双向绑定。AngularJS的scopes包括以下四个主要部分:
digest循环以及dirty-checking,包括watch,digest,和$apply。
Scope继承 - 这项机制使得我们可以创建scope继承来分享数据和事件。
对集合 – 数组和对象 – 的有效dirty-checking。
事件系统 - on,emit,以及$broadcast。
我们主要讲解第一条Angular数据绑定是怎么实现的。
1.digest循环以及dirty-checking,包括watch,digest,和$apply
①浏览器事件循环和Angular.js扩展
我们的浏览器一直在等待事件,比如用户交互。假如你点击一个按钮或者在输入框里输入东西,事件的回调函数就会在javascript解释器里执行,然后你就可以做任何DOM操作,等回调函数执行完毕时,浏览器就会相应地对DOM做出变化。 Angular拓展了这个事件循环,生成一个有时成为angular context的执行环境(这是个重要的概念)。
②watch队列(watch list)
每次你绑定一些东西到你的UI上时你就会往$watch队列里插入一条$watch。想象一下$watch就是那个可以检测它监视的model里时候有变化的东西。
当我们的模版加载完毕时,也就是在linking阶段(Angular分为compile阶段和linking阶段---译者注),Angular解释器会寻找每个directive,然后生成每个需要的$watch。
③$digest循环
还记得我前面提到的扩展的事件循环吗?当浏览器接收到可以被angular context处理的事件时,digest循环就会触发。这个循环是由两个更小的循环组合起来的。一个处理evalAsync队列,另一个处理watch队列。 这个是处理什么的呢?digest将会遍历我们的watch,然后询问它是否有属性和值的变化,直$watch队列都检查过。
这就是所谓的dirty-checking。既然所有的$watch都检查完了,那就要问了:有没有$watch更新过?如果有至少一个更新过,这个循环就会再次触发,直到所有的$watch都没有变化。这样就能够保证每个model都已经不会再变化。记住如果循环超过10次的话,它将会抛出一个异常,防止无限循环。 当$digest循环结束时,DOM相应地变化。
例如:controllers.js
app.controller('MainCtrl', function() { $scope.name = "Foo"; $scope.changeFoo = function() { $scope.name = "Bar"; } });
{{ name }} <button ng-click="changeFoo()">Change the name</button>
这里我们有一个$watch因为ng-click不生成$watch(函数是不会变的)。
- 我们按下按钮
- 浏览器接收到一个事件,进入angular context(后面会解释为什么)。
- $digest循环开始执行,查询每个$watch是否变化。
- 由于监视$scope.name的$watch报告了变化,它会强制再执行一次$digest循环。
- 新的$digest循环没有检测到变化。
- 浏览器拿回控制权,更新与$scope.name新值相应部分的DOM。
这里很重要的(也是许多人的很蛋疼的地方)是每一个进入angular context的事件都会执行一个$digest循环,也就是说每次我们输入一个字母循环都会检查整个页面的所有$watch。
④通过$apply来进入angular context
谁决定什么事件进入angular context,而哪些又不进入呢?$apply!
如果当事件触发时,你调用apply,它会进入angularcontext,如果没有调用就不会进入。现在你可能会问:刚才的例子里我也没有调用apply,为什么?Angular为你做了!因此你点击带有ng-click的元素时,时间就会被封装到一个apply调用。如果你有一个ng−model="foo"的输入框,然后你敲一个f,事件就会这样调用apply("foo = 'f';")。
Angular什么时候不会自动为我们apply呢?这是Angular新手共同的痛处。为什么我的jQuery不会更新我绑定的东西呢?因为jQuery没有调用apply,事件没有进入angular context,$digest循环永远没有执行。
2.具体实现
AngularJS的scopes就是一般的JavaScript对象,在它上面你可以绑定你喜欢的属性和其他对象,然而,它们同时也被添加了一些功能用于观察数据结构上的变化。这些观察的功能都由dirty-checking来实现并且都在一个digest循环中被执行。
①Scope 对象
创建一个test/scope_spec.js文件,并将下面的测试代码添加到其中:
test/scope_spec.js ------- /* jshint globalstrict: true */ /* global Scope: false */ 'use strict'; describe("Scope", function() { it("can be constructed and used as an object", function() { var scope = new Scope(); scope.aProperty = 1; expect(scope.aProperty).toBe(1); }); });
这个测试用来创建一个Scope,并在它上面赋一个任意值。我们可以轻松的让这个测试通过:创建src/scope.js文件然后在其中添加以下内容:
src/scope.js
------ /* jshint globalstrict: true */ 'use strict'; function Scope() { }
在这个测试中,我们将一个属性(aProperty)赋值给了这个scope。这正是Scope上的属性运行的方式。它们就是正常的JavaScript属性,并没有什么特别之处。这里你完全不需要去调用一个特别的setter,也不需要对你赋值的类型进行什么限制。真正的魔法在于两个特别的函数:watch和digest。我们现在就来看看这两个函数。
②监视对象属性:watch和digest
watch和digest是同一个硬币的两面。它们二者同时形成了$digest循环的核心:对数据的变化做出反应。
为了实现这一块功能,我们首先来定义一个测试文件并断言你可以使用watch来注册一个监视器,并且当有人调用了digest的时候监视器的监听函数会被调用。
在scope_spec.js文件中添加一个嵌套的describe块。并创建一个beforeEach函数来初始化这个scope,以便我们可以在进行每个测试时重复它:
test/scope_spec.js
------ describe("Scope", function() { it("can be constructed and used as an object", function() { var scope = new Scope(); scope.aProperty = 1; expect(scope.aProperty).toBe(1); }); describe("digest", function() { var scope; beforeEach(function() { scope = new Scope(); }); it("calls the listener function of a watch on first $digest", function() { var watchFn = function() { return 'wat'; }; var listenerFn = jasmine.createSpy(); scope.$watch(watchFn, listenerFn); scope.$digest(); expect(listenerFn).toHaveBeenCalled(); }); }); });
在上面的这个测试中我们调用了watch来在这个scope上注册一个监视器。我们现在对于监视函数本身并没有什么兴趣,因此我们随便提供了一个函数来返回一个常数值。作为监听函数,我们提供了一个JasmineSpy。接着我们调用了digest并检查这个监听器是否真正被调用。
首先,这个Scope需要有一些地方去存储所有被注册的监视器。我们现在就在Scope构造函数中添加一个数组存储它们:
src/scope.js
----- function Scope(){ this.$$watchers = []; }
上面代码中的$$前缀在AngularJS框架中被认为是私有变量,它们不应该在应用的外部被调用。
现在我们可以来定义watch函数了。它接收两个函数作为参数,并且将它们储存在$watchers数组中。我们想要每一个Scope对象都拥有这个函数,因此我们将它添加到Scope的原型中:
src/scope.js ----- Scope.prototype.$watch = function(watchFn, listenerFn) { var watcher = { watchFn: watchFn, listenerFn: listenerFn }; this.$$watchers.unshift(watcher); };
最后我们应该有一个digest函数。现在,我们来定义一个digest函数的简化版本,它仅仅只是会迭代所有的注册监视器并调用它们的监听函数:
digest能够持续的迭代所有监视函数,直到被监视的值停止变化。多做几次digest是我们能够获得运用于监视器并依赖于其他监视器的变化。
首先,我们新建名为$$digestOnce,并且调整它以便它能够在所有监视器上运行一遍,然后返回一个布尔值来说明有没有任何变化:
src/scope.js
---- Scope.prototype.$$digestOnce = function(){ var length = this.$$watchers.length; var watcher, newValue, oldValue, dirty; while(length--){ watcher = this.$$watchers[length]; newValue = watcher.watchFn(this); oldValue= watcher.last; if(newValue !== oldValue){ watcher.last == newValue; watcher.listenerFn(newValue, oldValue, this); dirty = true; } } return dirty; };
接着,我们重定义digest以便它能够运行“外循环”,在变化发生时调用$digestOnce:
src/scope.js
----- Scope.prototype.$digest = function(){ var dirty; do { dirty = this.$$digestOnce(); } while (dirty); };
以上就是Angular数据双向绑定的相关介绍,希望对大家的学习有所帮助。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











0.この記事は何をするのですか?私たちは、多用途かつ高速な最先端の生成単眼深度推定モデルである DepthFM を提案します。従来の深度推定タスクに加えて、DepthFM は深度修復などの下流タスクでも最先端の機能を実証します。 DepthFM は効率的で、いくつかの推論ステップ内で深度マップを合成できます。この作品について一緒に読みましょう〜 1. 論文情報タイトル: DepthFM: FastMonocularDepthEstimationwithFlowMatching 著者: MingGui、JohannesS.Fischer、UlrichPrestel、PingchuanMa、Dmytr

DDREASE は、ハード ドライブ、SSD、RAM ディスク、CD、DVD、USB ストレージ デバイスなどのファイル デバイスまたはブロック デバイスからデータを回復するためのツールです。あるブロック デバイスから別のブロック デバイスにデータをコピーし、破損したデータ ブロックを残して正常なデータ ブロックのみを移動します。 ddreasue は、回復操作中に干渉を必要としないため、完全に自動化された強力な回復ツールです。さらに、ddasue マップ ファイルのおかげでいつでも停止および再開できます。 DDREASE のその他の主要な機能は次のとおりです。 リカバリされたデータは上書きされませんが、反復リカバリの場合にギャップが埋められます。ただし、ツールに明示的に指示されている場合は切り詰めることができます。複数のファイルまたはブロックから単一のファイルにデータを復元します

Excel で複数の条件によるフィルタリングを使用する方法を知る必要がある場合は、次のチュートリアルで、データを効果的にフィルタリングおよび並べ替えできるようにするための手順を説明します。 Excel のフィルタリング機能は非常に強力で、大量のデータから必要な情報を抽出するのに役立ちます。設定した条件でデータを絞り込み、条件に合致した部分のみを表示することができ、データ管理を効率化できます。フィルター機能を利用すると、目的のデータを素早く見つけることができ、データの検索や整理の時間を節約できます。この機能は、単純なデータ リストに適用できるだけでなく、複数の条件に基づいてフィルタリングすることもできるため、必要な情報をより正確に見つけることができます。全体として、Excel のフィルタリング機能は非常に実用的です。

Google が推進する JAX のパフォーマンスは、最近のベンチマーク テストで Pytorch や TensorFlow のパフォーマンスを上回り、7 つの指標で 1 位にランクされました。また、テストは最高の JAX パフォーマンスを備えた TPU では行われませんでした。ただし、開発者の間では、依然として Tensorflow よりも Pytorch の方が人気があります。しかし、将来的には、おそらくより大規模なモデルが JAX プラットフォームに基づいてトレーニングされ、実行されるようになるでしょう。モデル 最近、Keras チームは、ネイティブ PyTorch 実装を使用して 3 つのバックエンド (TensorFlow、JAX、PyTorch) をベンチマークし、TensorFlow を使用して Keras2 をベンチマークしました。まず、主流のセットを選択します

iPhone のモバイル データ接続に遅延や遅い問題が発生していませんか?通常、携帯電話の携帯インターネットの強度は、地域、携帯ネットワークの種類、ローミングの種類などのいくつかの要因によって異なります。より高速で信頼性の高いセルラー インターネット接続を実現するためにできることがいくつかあります。解決策 1 – iPhone を強制的に再起動する 場合によっては、デバイスを強制的に再起動すると、携帯電話接続を含む多くの機能がリセットされるだけです。ステップ 1 – 音量を上げるキーを 1 回押して放します。次に、音量小キーを押して、もう一度放します。ステップ 2 – プロセスの次の部分は、右側のボタンを押し続けることです。 iPhone の再起動が完了するまで待ちます。セルラーデータを有効にし、ネットワーク速度を確認します。もう一度確認してください 修正 2 – データ モードを変更する 5G はより優れたネットワーク速度を提供しますが、信号が弱い場合はより適切に機能します

Angular.js は、動的アプリケーションを作成するための無料でアクセスできる JavaScript プラットフォームです。 HTML の構文をテンプレート言語として拡張することで、アプリケーションのさまざまな側面を迅速かつ明確に表現できます。 Angular.js は、コードの作成、更新、テストに役立つさまざまなツールを提供します。さらに、ルーティングやフォーム管理などの多くの機能も提供します。このガイドでは、Ubuntu24 に Angular をインストールする方法について説明します。まず、Node.js をインストールする必要があります。 Node.js は、ChromeV8 エンジンに基づく JavaScript 実行環境で、サーバー側で JavaScript コードを実行できます。ウブにいるために

テスラのロボット「オプティマス」の最新映像が公開され、すでに工場内で稼働可能となっている。通常の速度では、バッテリー(テスラの4680バッテリー)を次のように分類します:公式は、20倍の速度でどのように見えるかも公開しました - 小さな「ワークステーション」上で、ピッキング、ピッキング、ピッキング:今回は、それがリリースされたハイライトの1つビデオの内容は、オプティマスが工場内でこの作業を完全に自律的に行い、プロセス全体を通じて人間の介入なしに完了するというものです。そして、オプティマスの観点から見ると、自動エラー修正に重点を置いて、曲がったバッテリーを拾い上げたり配置したりすることもできます。オプティマスのハンドについては、NVIDIA の科学者ジム ファン氏が高く評価しました。オプティマスのハンドは、世界の 5 本指ロボットの 1 つです。最も器用。その手は触覚だけではありません

マルチモーダル文書理解機能のための新しい SOTA!アリババの mPLUG チームは、最新のオープンソース作品 mPLUG-DocOwl1.5 をリリースしました。これは、高解像度の画像テキスト認識、一般的な文書構造の理解、指示の遵守、外部知識の導入という 4 つの主要な課題に対処するための一連のソリューションを提案しています。さっそく、その効果を見てみましょう。複雑な構造のグラフをワンクリックで認識しMarkdown形式に変換:さまざまなスタイルのグラフが利用可能:より詳細な文字認識や位置決めも簡単に対応:文書理解の詳しい説明も可能:ご存知「文書理解」 「」は現在、大規模な言語モデルの実装にとって重要なシナリオです。市場には文書の読み取りを支援する多くの製品が存在します。その中には、主にテキスト認識に OCR システムを使用し、テキスト処理に LLM と連携する製品もあります。
