ionic-myAppDemo项目分析

来源:互联网 发布:淘宝拖鞋底 编辑:程序博客网 时间:2024/05/16 14:14


项目效果图:


项目代码目录图:


项目代码目录图

ionic中是使用angularJs和ionic的页面组件来进行开发的,其代码的核心目录为上图中的www目录,代码一般都写在该目录下,主要是html,js和css文件。(上图中的hello.html文件不属于该项目,可忽略)

整个项目运作的流程如下:

1.用户请求应用起始页。

2.用户的浏览器向服务器发起一次HTTP 连接,然后加载index.html 页面,这个页面里面包含了模板。

3.Angular 被加载到页面中,等待页面加载完成,然后查找ng-app 指令,用来定义模板边界。

4.Angular 遍历模板,查找指令和绑定关系,这将触发一系列动作:注册监听器、执行一些DOM 操作、从服务器获取初始化数据。这项工作的最后结果是,应用将会启动起来,并且模板被转换成了DOM 视图。

5.连接到服务器去加载需要展示给用户的其他数据。

下面具体分析一下代码:

index.html

<!DOCTYPE html><html>  <head>    <meta charset="utf-8">    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">    <title></title>    <link href="lib/ionic/css/ionic.css" rel="stylesheet">    <link href="css/style.css" rel="stylesheet">    <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above    <link href="css/ionic.app.css" rel="stylesheet">    -->    <!-- ionic/angularjs js -->    <script src="lib/ionic/js/ionic.bundle.js"></script>    <!-- cordova script (this will be a 404 during development) -->    <script src="cordova.js"></script>    <!-- your app's js -->    <script src="js/app.js"></script>    <script src="js/controllers.js"></script>    <script src="js/services.js"></script>  </head>  <body ng-app="starter">    <!--      The nav bar that will be updated as we navigate between views.    -->    <ion-nav-bar class="bar-stable">      <ion-nav-back-button>      </ion-nav-back-button>    </ion-nav-bar>    <!--      The views will be rendered in the <ion-nav-view> directive below      Templates are in the /templates folder (but you could also      have templates inline in this html file if you'd like).    -->    <ion-nav-view></ion-nav-view>  </body></html>

index.html中 head中就是配置一些适配参数和引用一些js,包括app.js,controllers.js,services.js 自己定义的3个js文件。

body中是绘制整个页面的,在body这个标签上加上了ng-app 标记,表示body这个标签之内的东西由angularJs来解析,angularJs承包了body这个鱼塘,并且起了一个名字:starter,这样就会告诉Angular 去管理页面上的所有DOM 元素。

body中就是一些ionic的界面组件了,每个组件的解释和用法可参考该网站:组件api

在最后一行,有个ion-nav-view组件,该组件的作用为:在app启动时,$stateProvider就会检查url,检查它的索引匹配状态,然后尝试将对应的html加载到<ion-nav-view>内。

tabs.html

<!--Create tabs with an icon and label, using the tabs-positive style.Each tab's child <ion-nav-view> directive will have its ownnavigation history that also transitions its views in and out.--><ion-tabs class="tabs-icon-top tabs-color-active-positive">  <!-- Dashboard Tab -->  <ion-tab title="Status" icon-off="ion-ios-pulse" icon-on="ion-ios-pulse-strong" href="#/tab/dash">    <ion-nav-view name="tab-dash"></ion-nav-view>  </ion-tab>  <!-- Chats Tab -->  <ion-tab title="Chats" icon-off="ion-ios-chatboxes-outline" icon-on="ion-ios-chatboxes" href="#/tab/chats">    <ion-nav-view name="tab-chats"></ion-nav-view>  </ion-tab>  <!-- Account Tab -->  <ion-tab title="Account" icon-off="ion-ios-gear-outline" icon-on="ion-ios-gear" href="#/tab/account">    <ion-nav-view name="tab-account"></ion-nav-view>  </ion-tab></ion-tabs>

该html即为整个布局页面,3个tab页:Status,Chats,Account

定义了3个tab,每个tab中有一个ion-nav-view ,还定义了tab 页选中和没选中时的图标,以及指定超链接目标的 URL

tab-dash.html

<ion-view view-title="Dashboard">  <ion-content class="padding">    <div class="list card">      <div class="item item-divider">Recent Updates</div>      <div class="item item-body">        <div>          There is a fire in <b>sector 3</b>        </div>      </div>    </div>    <div class="list card">      <div class="item item-divider">Health</div>      <div class="item item-body">        <div>          You ate an apple today!        </div>      </div>    </div>    <div class="list card">      <div class="item item-divider">Upcoming</div>      <div class="item item-body">        <div>          You have <b>29</b> meetings on your calendar tomorrow.        </div>      </div>    </div>  </ion-content></ion-view>
此html中是纯的静态代码,用到了较多的ionic组件,可参考上面的api了解每个组件的用法。

-----重点来了-----

tab-chats.html

<ion-view view-title="Chats">  <ion-content>    <ion-list>      <ion-item class="item-remove-animate item-avatar item-icon-right" ng-repeat="chat in chats" type="item-text-wrap" href="#/tab/chats/{{chat.id}}">        <img ng-src="{{chat.face}}">        <h2>{{chat.name}}</h2>        <p>{{chat.lastText}}</p>        <i class="icon ion-chevron-right icon-accessory"></i>        <ion-option-button class="button-assertive" ng-click="remove(chat)">          Delete        </ion-option-button>      </ion-item>    </ion-list>  </ion-content></ion-view>


这是第2个tab页,是一个list列表。首先定义了页面的标题:Chats ,然后定义了一个列表,接着定义列表的item,其中有这么一句:ng-repeat="chat in chats" , ng-repeat是angular的标签,表示遍历,后面的写法应该就是遍历chats了,那么问题来了:chats从哪来的呢?

带着疑问继续看代码

先看一下angularJs中的一个重要概念

$scope概念

$scope 的使用贯穿整个 Angular App 应用,它与数据模型相关联,同时也是表达式执行的上下文.有了 $scope 就在视图和控制器之间建立了一个通道,基于作用域视图在修改数据时会立刻更新 $scope,同样的 $scope 发生改变时也会立刻重新渲染视图。其实就是双向绑定。

有了 $scope 这样一个桥梁,应用的业务代码可以都在 controller 中,而数据都存放在controller 的 $scope 中。


$scope概念图

$scope 的作用

$scope 对象在 Angular 中充当数据模型的作用,也就是一般 MVC 框架中 Model 得角色.但又不完全与通常意义上的数据模型一样,因为 $scope 并不处理和操作数据,它只是建立了视图和 HTML 之间的桥梁,让视图和 Controller 之间可以友好的通讯。

再进一步系统的划分它的作用和功能:

  1. 提供了观察者可以监听数据模型的变化
  2. 可以将数据模型的变化通知给整个 App
  3. 可以进行嵌套,隔离业务功能和数据
  4. 给表达式提供上下文执行环境

在 Javascript 中创建一个新的执行上下文,实际就是用函数创建了一个新的本地上下文,在 Angular 中当为子 DOM 元素创建新的作用域时,其实就是为子 DOM 元素创建了一个新的执行上下文。

$scope 生命周期

Angular 中也有一个'事件'的概念,比如当一个绑定了 ng-model的 input 值发生变化时,或者一个 ng-click 的 button 被点击时,Angular 的事件循环就会启动.事件循环是 Angular 中非常非常核心的一个概念,因为不是本文主旨所以不多说,感兴趣的可以自己看看资料.这里事件就在 Angular 执行上下文中处理,$scope 就会对定义的表达式求值.此时事件循环被启动, Angular 会监控应用程序内所有对象,脏值检查循环也会启动。

$scope 的生命周期有4个阶段:

  1. 创建

    控制器或者指令(ng-controller)创建时, Angular 会使用 $injector 创建一个新的作用域,然后在控制器或指令运行时,将作用域传递进去。

  2. 链接

    Angular 启动后会将所有 $scope 对象附加或者说链接到视图上,所有创建 $scope 对象的函数也会被附加到视图上.这些作用域将会注册当 Angular 上下文发生变化时需要运行的函数.也就是 $watch 函数, Angular 通过这些函数或者何时开始事件循环。

  3. 更新

    一旦事件循环开始运行,就会开始执行自己的脏值检测.一旦检测到变化,就会触发 $scope 上指定的回调函数。

  4. 销毁

    通常来讲如果一个 $scope 在视图中不再需要, Angular 会自己清理它.当然也可以通过 $destroy() 函数手动清理。

可以参考以下网站来进一步了解$scope :

http://www.tuicool.com/articles/nUzMz2J 或者

http://blog.csdn.net/aitangyong/article/details/40267583?utm_source=tuicool&utm_medium=referral

笔者总结一下:

一个controller对应了一个$scope对应了一个DOM,强调一下是DOM,不一定是body,也有可能是div,总之就是一个标签都可以配置一个ng-controller。而这个DOM范围内,都可以引用$scope中存储的数据或者函数

接着上面的问题:chats从哪来的呢?

从来的解释中可以看出,这个chats定是存储在tab-chats.html对应的某个$scope中,由于tab-chats.html中没有直接的DOM指出一个具体的controller,否者我们就直接去controller中找$scope就行了。

但是我们在app.js文件中发现一些端倪:

app.js源代码

// Ionic Starter App// angular.module is a global place for creating, registering and retrieving Angular modules// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)// the 2nd parameter is an array of 'requires'// 'starter.services' is found in services.js// 'starter.controllers' is found in controllers.jsangular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])    .run(function ($ionicPlatform) {        $ionicPlatform.ready(function () {            // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard            // for form inputs)            if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {                cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);                cordova.plugins.Keyboard.disableScroll(true);            }            if (window.StatusBar) {                // org.apache.cordova.statusbar required                StatusBar.styleLightContent();            }        });    })    .config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider) {        $ionicConfigProvider.platform.ios.tabs.style('standard');        $ionicConfigProvider.platform.ios.tabs.position('bottom');        $ionicConfigProvider.platform.android.tabs.style('standard');        $ionicConfigProvider.platform.android.tabs.position('bottom');        $ionicConfigProvider.platform.ios.navBar.alignTitle('center');        $ionicConfigProvider.platform.android.navBar.alignTitle('left');        $ionicConfigProvider.platform.ios.backButton.previousTitleText('').icon('ion-ios-arrow-thin-left');        $ionicConfigProvider.platform.android.backButton.previousTitleText('').icon('ion-android-arrow-back');        $ionicConfigProvider.platform.ios.views.transition('ios');        $ionicConfigProvider.platform.android.views.transition('android');    })    .config(function ($stateProvider, $urlRouterProvider) {        // Ionic uses AngularUI Router which uses the concept of states        // Learn more here: https://github.com/angular-ui/ui-router        // Set up the various states which the app can be in.        // Each state's controller can be found in controllers.js        $stateProvider            // setup an abstract state for the tabs directive            .state('tab', {                url: '/tab',                // abstract: true 表明此状态不能被显性激活,只能被子状态隐性激活                abstract: true,                templateUrl: 'templates/tabs.html'            })            // Each tab has its own nav history stack:            .state('tab.dash', {                url: '/dash',                views: {                    'tab-dash': {                        templateUrl: 'templates/tab-dash.html',                        controller: 'DashCtrl'                    }                }            })            .state('tab.chats', {                url: '/chats',                views: {                    'tab-chats': {                        templateUrl: 'templates/tab-chats.html',                        controller: 'ChatsCtrl'                    }                }            })            .state('tab.chat-detail', {                url: '/chats/:chatId',                views: {                    'tab-chats': {                        templateUrl: 'templates/chat-detail.html',                        controller: 'ChatDetailCtrl'                    }                }            })            .state('tab.account', {                url: '/account',                views: {                    'tab-account': {                        templateUrl: 'templates/tab-account.html',                        controller: 'AccountCtrl'                    }                }            });        // if none of the above states are matched, use this as the fallback        $urlRouterProvider.otherwise('/tab/dash');    });直接看到以下代码:.state('tab.chats', {    url: '/chats',    views: {        'tab-chats': {            templateUrl: 'templates/tab-chats.html',            controller: 'ChatsCtrl'        }    }})

tab-chats.html页面对应的controller为ChatsCtrl,这时候我们就可以去controllers.js中找到名字为ChatsCtrl的controller了。

controllers.js

angular.module('starter.controllers', []).controller('DashCtrl', function($scope) {}).controller('ChatsCtrl', function($scope, Chats) {  $scope.chats = Chats.all();  $scope.remove = function(chat) {    Chats.remove(chat);  };}).controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {  $scope.chat = Chats.get($stateParams.chatId);}).controller('AccountCtrl', function($scope) {  $scope.settings = {    enableFriends: true  };});

看到 $scope.chats = Chats.all(); 给chats赋值了,此处的chats即为前面提到问题的答案,tab-chats.html页面的chats的来源即为此处,并且已经赋值了。

那么问题又来了,它(chats )的值是什么呢?

从上面的代码可以看出它的值为:Chats.all() 但是Chats是什么呢?

其实Chats和$scope一样都是服务,只不过$scope是系统服务(就是有系统来定义的),而Chats是自定义服务,需要自己写代码定义。

我们在services.js中可以找到Chats服务。

services.js代码

angular.module('starter.services', []).factory('Chats', function() {  var chats = [{    id: 0,    name: 'Ben Sparrow',    lastText: 'You on your way?',    face: 'https://pbs.twimg.com/profile_images/514549811765211136/9SgAuHeY.png'  }, {    id: 1,    name: 'Max Lynx',    lastText: 'Hey, it\'s me',    face: 'https://avatars3.githubusercontent.com/u/11214?v=3&s=460'  }, {    id: 2,    name: 'Adam Bradleyson',    lastText: 'I should buy a boat',    face: 'https://pbs.twimg.com/profile_images/479090794058379264/84TKj_qa.jpeg'  }, {    id: 3,    name: 'Perry Governor',    lastText: 'Look at my mukluks!',    face: 'https://pbs.twimg.com/profile_images/598205061232103424/3j5HUXMY.png'  }, {    id: 4,    name: 'Mike Harrington',    lastText: 'This is wicked good ice cream.',    face: 'https://pbs.twimg.com/profile_images/578237281384841216/R3ae1n61.png'  }];  return {    all: function() {      return chats;    },    remove: function(chat) {      chats.splice(chats.indexOf(chat), 1);    },    get: function(chatId) {      for (var i = 0; i < chats.length; i++) {        if (chats[i].id === parseInt(chatId)) {          return chats[i];        }      }      return null;    }  };});

上面的factory('Chats', function() 中的 Chats 自定义的服务名称 ,该服务定义了返回值,返回了3个函数:all() ,

remove() ,get() 。

所以上面的Chats.all()的值是什么也就很清楚了,即是var chats 这个对象数组。

关于如何自定义服务,可参考这个网站 http://my.oschina.net/tanweijie/blog/295067

从上面的网站中,了解到这一点:

在Angular里面,services作为单例对象在需要到的时候被创建,只有在应用生命周期结束的时候(关闭浏览器)才会被清除。而controllers在不需要的时候就会被销毁了。

说的是服务的生命周期,很重要的一点。


0 0
原创粉丝点击