ionic开发Android App学习笔记(二)

第一个ionic简例分析

上文中已经将ionic的开发环境搭建完毕,并且创建了一个带有tab页的项目。

运行效果图:

运行效果

项目代码目录图:

项目代码目录图

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 own
navigation 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 创建一个新的作用域,然后在控制器或指令运行时,将作用域传递进去。
  1. 链接
Angular 启动后会将所有 $scope 对象附加或者说链接到视图上,所有创建 $scope 对象的函数也会被附加到视图上.这些作用域将会注册当 Angular 上下文发生变化时需要运行的函数.也就是 $watch 函数, Angular 通过这些函数或者何时开始事件循环。
  1. 更新
一旦事件循环开始运行,就会开始执行自己的脏值检测.一旦检测到变化,就会触发 $scope 上指定的回调函数。
  1. 销毁
通常来讲如果一个 $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.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在不需要的时候就会被销毁了。

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

基本上分析了demo中的大部分,不想写了,下一次再写,分析一下app.js中的一些东西。

文章中用到项目地址 :https://github.com/viphsx123/myApp

完。

参考链接:

http://my.oschina.net/blogshi/blog/316745

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

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

http://my.oschina.net/tanweijie/blog/295067

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容