ionic-myAppDemo项目分析


项目效果图:ionic-myAppDemo项目分析_第1张图片


项目代码目录图:


项目代码目录图

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



  
    
    
    

    
    

    

    
    

    
    

    
    
    
    
  
  
    
    
      
      
    
    
    

  

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加载到内。

tabs.html




  
  
    
  

  
  
    
  

  
  
    
  


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

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

tab-dash.html


  
    
Recent Updates
There is a fire in sector 3
Health
You ate an apple today!
Upcoming
You have 29 meetings on your calendar tomorrow.
此html中是纯的静态代码,用到了较多的ionic组件,可参考上面的api了解每个组件的用法。

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

tab-chats.html


  
    
      
        
        

{{chat.name}}

{{chat.lastText}}

Delete


这是第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  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.js
angular.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在不需要的时候就会被销毁了。

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


你可能感兴趣的:(ionic开发)