ホームページ > 記事 > ウェブフロントエンド > AngularJS モバイル開発の落とし穴
相対的に言えば、Jquery は DOM 操作に重点を置いているのに対し、AngularJS はビュー モデルと双方向バインディングに重点を置いています。
DOM 操作の問題
要素ノードの追加、要素ノードの削除、要素コンテンツの取得、要素の非表示または表示など、DOM の操作に jQuery を使用しないでください。これらのアクションを実装するにはディレクティブを使用し、必要に応じて独自のディレクティブを作成する必要があります。
Web サイトの Web フロントエンド開発において、習慣を変えるのが難しい場合は、Web ページから jQuery を削除することを検討してください。実際、AngularJS の $http サービスは非常に強力で、基本的に jQuery の ajax 関数を置き換えることができます。AngularJS は jQLite に埋め込まれています?? これは、一般的に使用される jQuery DOM 操作メソッド、イベント バインディングなどを含む、内部的に実装された jQuery のサブセットです。 。ただし、これは、AngularJS を使用するときに jQuery を使用できないという意味ではありません。 Web ページに jQuery が読み込まれている場合、AngularJS は最初に jQuery を使用し、それ以外の場合は jQLite にフォールバックします。
モバイルアプリやモバイルWeb開発の場合、どうしてもjqueryの機能が必要な場合は、Zepto.jsを導入することをお勧めします。しかし、信じてください。AngularJS を使用すれば、Jquery は必要ありません。
独自のディレクティブを作成する必要がある状況は、通常、サードパーティの jQuery プラグインを使用する場合です。プラグインは AngularJS の外部でフォームの値を変更するため、すぐにモデルに反映することはできません。たとえば、私たちは jQueryUI datepicker プラグインを頻繁に使用します。日付を選択すると、プラグインは日付文字列を入力ボックスに入力します。 $('.datepicker').datepicker(); このコードは AngularJS の管理スコープに属していないため、ビューは変更されますが、モデルは更新されません。 DOM の変更をモデルに即時に更新するディレクティブを作成する必要があります。
var directives = angular.module('directives', []); directives.directive('datepicker', function() { return function(scope, element, attrs) { element.datepicker({ inline: true, dateFormat: 'dd.mm.yy', onSelect: function(dateText) { var modelPath = $(this).attr('ng-model'); putObject(modelPath, scope, dateText); scope.$apply(); } }); } });
次に、このディレクティブをHTMLに導入します
<input type="text" datepicker ng-model="myObject.myDateValue" />
ディレクティブは、プラグインの機能を実現し、HTMLの機能を効果的に補完するために、HTMLにカスタムタグの属性を記述することです。この宣言構文は HTML を拡張します。使用とコードのメンテナンスを容易にするために、プロジェクト内の共通関数とページ コンポーネントをディレクティブにカプセル化することをお勧めします。
Bootstrap フレームワークのプラグインや jQuery に基づくその他の一般的な UI コンポーネントなど、使用できる多数のディレクティブを提供する AngularUI プロジェクトがあることに注意してください。 AngularJS のコミュニティは現在活発であり、エコシステムは堅牢です。
ngOption
の値は大きな落とし穴です。 ngOption によって生成された 221f08282418e2996498697df914ce4e 内の 5a07473c87748fb1bf73f23d45547ab8 のオプション値 (各 f27989812beae9f6405808e298a46122 の値の部分) を見ると、明らかに無駄です。ここでの値は、指定したフォーム オプションの値ではなく、常に AngularJS の内部要素のインデックスになるためです。
AngularJS ではデータのやり取りにフォームを使用せず、モデルを使用するという概念を変更する必要があります。 $http を使用してモデルを送信し、php で file_get_contents('php://input') を使用してフロントエンドによって送信されたデータを取得します。
Input type='number' の問題
AngularJS の一部のバージョンでは、入力ボックスが Input type='number' に設定されていると、モバイル デバイスで ng-change メソッドが失敗します。
{{ }} の問題
ページが初期化されると、ユーザーには {{ }} が表示され、実際のコンテンツが表示される前にしばらく点滅します。
解決策:
ng-cloak ディレクティブを使用して非表示にします
{{ }} の代わりに ng-bind を使用します
インターフェイスをビジネス ロジックから分離します
コントローラーは DOM を直接参照すべきではありませんが、DOM の動作を制御する必要がありますビュー。たとえば、「ユーザーが X を操作した場合、何が起こるでしょうか?」、「どこで入手できますか? インターフェースはビューのロジックとは何の関係もありません。その役割は単に「Xを行う」ことです。
DOM 操作はディレクティブ内に配置する必要があります。
可能な限り既存の関数を再利用してください
あなたが作成した関数は AngularJS で実装されている可能性が高く、一部のコードはより Angular の方法で抽象化して再利用できます。つまり、jQuery の退屈なコードの多くを置き換えることができます。
1. ng-repeat
ng-repeatはとても便利です。 Ajax がサーバーからデータを取得した後、jQuery (上記の例など) を使用していくつかの HTML コンテナー ノードに要素を追加することがよくありますが、これは AngularJS では悪い習慣です。 ng-repeat を使用すると、すべてが非常に簡単になります。 $scope で配列 (モデル) を定義してサーバーから取得したデータを保存し、ng-repeat を使用してそれを DOM にバインドします。次の例では、friends モデルを初期化して定義します
<div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]"> I have {{friends.length}} friends. They are: <ul> <li ng-repeat="friend in friends"> [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. </li> </ul> </div>
2. ng-show
ng-show 也很有用。使用 jQuery 来根据条件控制界面元素的显示隐藏,这很常见。但是 Angular 有更好的方式来做到这一点。ng-show (以及 ng-hide) 可以根据布尔表达式来决定隐藏和显示。
对于数组或字符串,可以用strXXXX.length控制显示,否则在移动设备上会不正常。
类似的内置 directives 还有 ng-disabled, ng-switch 等等,用于条件控制,语法简洁,都很强大。
3. ng-class
ng-class 用于条件性地给元素添加 class,以前我们也经常用 jQuery 来实现。Angular 中的 ng-class 当然更好用了,例子:
1d7c26ea023fee7ffae44d3aac1b7375...16b28748ea4df4d9c2150843fecfba68
在这里 ng-class 接受一个 object 对象,key 为 CSS class 名,值为 $scope 变量控制的条件表达式,其他类似的内置 directives 还有 ng-class-even 和 ng-class-odd,很实用。
ng-show和ng-if的使用场景问题
使用ng-show和ng-if都实现控制页面元素显示的功能,但2者是不同的,ng-if会动态创建DOM,ng-show只是切换已有DOM的显示,相当于设置style="display:none",如果使用before和after等css伪类控制显示效果,可能会出现问题,需要根据情况合理使用ng-show和ng-if。
$watch 和 $apply
AngularJS 的双向数据绑定是最令人兴奋的特性了,然而它也不是全能的魔法,在某些情况下你需要做一些小小的修正。
当你使用 ng-model, ng-repeat 等等来绑定一个元素的值时, AngularJS 为那个值创建了一个 $watch,只要这个值在 AngularJS 的范围内有任何改变,所有的地方都会同步更新。而你在写自定义的 directive 时,你需要定义你自己的 $watch 来实现这种自动同步。
有时候你在代码中改变了 model 的值,view 却没有更新,这在自定义事件绑定中经常遇到。这时你就需要手动调用 scope.$apply() 来触发界面更新。上面 datepicker 的例子已经说明了这一点。第三方插件可能会有 call back,我们也可以把回调函数写成匿名函数作为参数传入$apply()中。
将 ng-repeat 和其他 directives 结合起来
ng-repeat 很有用,不过它和 DOM 绑定了,很难在同一个元素上使用其他 directives (比如 ng-show, ng-controller 等等)。
如果你想对整个循环使用某个 directive,你可以在 repeat 外再包一层父元素把 directive 写在那儿;如果你想对循环内部的每一个元素使用某个 directive,那么把它放到 ng-repeat 的一个子节点上即可。
Scope的问题
Scope 在 templates 模板中应该是 read-only 的,而在 controller 里应该是 write-only 的。Scope 的目的是引用 model,而不是成为 model。model 就是我们定义的 JavaScript 对象。
$rootScope 是可以用的,不过很可能被滥用
Scopes 在 AngularJS 中形成一定的层级关系,树状结构必然有一个根节点。通常我们用不到它,因为几乎每个 view 都有一个 controller 以及相对应的自己的 scope。
但偶尔有一些数据我们希望全局应用在整个 app 中,这时我们可以将数据注入 $rootScope。因为其他 scope 都会继承 root scope,所以那些注入的数据对于 ng-show 这类 directive 都是可用的,就像是在本地 $scope 中的变量一样。
当然,全局变量是邪恶的,你必须很小心地使用 $rootScope。特别是不要用于代码,而仅仅用于注入数据。如果你非常希望在 $rootScope 写一个函数,那最好把它写到 service 里,这样只有用到的时候它才会被注入,测试起来也方便些。
相反,如果一个函数的功能仅仅是存储和返回一些数据,就不要把它创建成一个 service。
子作用域的原型继承问题
辛酸泪,这个也是个大坑。作用域变量的继承是基于javascript原型继承机制的,在使用涉及到作用域的指令如ng-template,ion-modal等时需要特别注意,相关的查找顺序这里就不细说了。