6 使用Ionic開發天氣應用


  簡介:本節課我們會制作一款天氣應用,這款應用允許用戶查看當前的天氣情況、天氣預報以及地點收藏,在模態框內顯示日出和日落的數據,使用分頁滾動面板顯示天氣信息,使用側滑菜單實現導航。

  6.1 項目配置

  環境配置,我們在第二節課的時候講過了呦~下面我們直接創建項目啦~

  打開終端,在我們選擇的目錄下執行命令:

    $ ionic start weatherApp blank

  進入文件夾:

    $ cd weatherApp

  啟動服務:

    $ ionic serve

  應用初始頁面如下:

  6.2 設置側滑菜單和視圖

  本應用我們將會采用側滑菜單作為主要的導航組件。側滑菜單既可以在左邊也可以在右邊滑入展開,本應用將從左邊滑入。使用ionSideMenus組件可以很容易實現這個功能,它允許右划顯示側滑菜單,或者使用在左上角已配置好的切換按鈕。

  現在我們打開index.html文件,配置側滑菜單組件:

<body ng-app="starter">
    <!-- 聲明ionSideMenus容器以包裹側邊欄菜單和內容區域 -->
    <ion-side-menus>
        <!-- 使用ionSideMenuContent組件包裹主體內容 -->
        <ion-side-menu-content>
            <!-- 在側滑菜單內容區域中使用帶切換圖標的導航組件切換側滑菜單的狀態 -->
            <ion-nav-bar class="bar-positive">
                <ion-nav-buttons side="left">
                    <button class="button button-clear" menu-toggle="left">
                        <span class="icon ion-navicon"></span>
                    </button>
                </ion-nav-buttons>
            </ion-nav-bar>
            <ion-nav-view></ion-nav-view>
        </ion-side-menu-content>
        <!-- 聲明一個側滑菜單並設置其位置為左側邊緣 -->
        <ion-side-menu side="left">
            <!-- 為側滑菜單設置一個頭部 -->
            <ion-header-bar class="bar-dark">
                <h1 class="title">天氣</h1>
            </ion-header-bar>
            <!-- 使用ionContent組件包裹鏈接列表為側滑導航菜單設置內容 -->
            <ion-content>
                <ion-list>
                    <ion-item class="item-icon-left" ui-sref="search" menu-close>
                        <span class="icon ion-search"></span> 查詢城市
                    </ion-item>
                    <ion-item class="item-icon-left" ui-sref="settings" menu-close>
                        <span class="icon ion-ios-cog"></span> 設置
                    </ion-item>
                </ion-list>
            </ion-content>
        </ion-side-menu>
    </ion-side-menus>
</body>

  側滑菜單的定義十分簡單,只要在代碼中包含並使用ionSideMenus、ionSideMenuContent和ionSideMenu指令即可。首先我們需要用ionSideMenus包裹其他功能指令,否則菜單無法生效。在ionSideMenus內部需要增加ionSideMenuContent和ionSideMenu這兩個元素,並使用side屬性設置菜單位置,注意每個側滑菜單組件中只能定義一個ionSideMenuContent元素,可以定義最多兩個ionSideMenu元素。

  ionNavButtons元素,設置menu-toggle屬性來控制單擊菜單時菜單的開啟和顯示,而menuclose屬性,則控制菜單的關閉與隱藏。

  下面我們來預覽一下我們設置的側滑菜單的樣子~

  6.3 制作搜索視圖-->地理位置搜索

  當用戶第一次啟動時,用戶需要設置想要查看天氣的地點。我們將創建一個新的視圖用來允許用戶搜索並查看結果列表。

  要實現這個功能,需要使用state provider創建一個新的路由並定義模板和控制器,現在我們先來添加一個路由狀態,打開app.js文件:

angular.module('starter', ['ionic'])
    //添加config()方法定義路由狀態
    .config(function($stateProvider, $urlRouterProvider) {
        $stateProvider
        //定義搜索路由的狀態
        .state('search', {
          url:'/search',
          controller:'SearchController',
          templateUrl:'views/search/search.html'
        });
        //使用搜索頁面作為默認視圖
        $urlRouterProvider.otherwise('/search');
    })

  搜索視圖的路由狀態我們已經設置好了,現在來建搜索視圖模板,新建文件www/views/search/search.html:

<ion-view view-title="查詢城市">
    <ion-content>
        <div class="list">
            <!-- 帶有ngModel指令的搜索框和一個可單擊按鈕制作的搜索列表 -->
            <div class="item item-input-inset">
                <label class="item-input-wrapper">
                    <input type="search" ng-model="model.term" placeholder="搜索城市">
                </label>
                <button class="button button-small button-positive" ng-click="search()">查詢</button>
            </div>
            <!-- 當搜索結果存在時遍歷搜索結果顯示地址和天氣視圖連接 -->
            <div class="item" ng-repeat="result in results" ui-sref="weather({city:result.formatted_address, lat:result.geometry.location.lat,lng:result.geometry.location.lng})">
                {{result.formatted_address}}
            </div>
        </div>
    </ion-content>
</ion-view>

  然后我們來設置搜索視圖的控制器,新建文件www/views/search/search.js文件:

angular.module('starter')
    //新建控制器SearchController並注入服務
    .controller('SearchController', function($scope, $http) {
        //定義一個搜索數據模板
        $scope.model = { term: '' };
        $scope.search = function() {
            //從googleapis中搜索數據並存儲結果到scope中
            $http.get('https://maps.googleapis.com/maps/api/geocode/json', { params: { address: $scope.model.term } })
                .success(function(response) {
                    $scope.results = response.results;
                });
        };
    });

  現在將搜索視圖的控制器引入到index.html文件中:

    <script src="views/search/search.js"></script>

  預覽一下應用~

  6.4 完成設置視圖的制作

  現如今,我們用過的應用,基本都會有設置的功能,提供給用戶一些應用配置的選項。我們這款天氣應用也添加了設置的功能,現在我們就來逐一的完成它。

  我們需要為視圖界面增加一個新的模板和控制器,然后為了管理應用,需要使用另外兩個服務來在視圖之間共享數據和方法,最后當編輯成功時同時更新側滑菜單中的內容,它包含一個快速進入收藏地點的入口。

  第一步我們需要先創建兩個服務,一個用來追蹤收藏地點操作,另一個用來操作設置視圖。使用Angular的工廠函數創建服務可以方便的注入任意的控制器,settings服務是一個帶有屬性的簡單對象,locations服務包含一些方法幫助管理收藏的地點。

  在主應用的JS文件中,同時增加這兩個服務可以保證應用更流暢,但也可以使用兩個獨立的模塊。打開app.js文件,增加以下內容:

//使用工廠函數定義Settings服務
    .factory('Settings', function() {
        var Settings = {
            units: 'si',
            days: 8
        };
        //配置默認設置后返回一個JS對象
        return Settings;
    })
    //使用工廠函數定義Locations服務
    .factory('Locations', function() {
        var Locations = {
            //創建地點對象並存儲在數組中,默認數據為芝加哥
            data: [{
                city: '北京,中國',
                lat: 39.904211,
                lng: 116.407395
            }],
            //確定一個地點在搜索結果列表中的索引
            getIndex: function(item) {
                var index = -1;
                angular.forEach(Locations.data, function(location, i) {
                    if (item.lat == location.lat && item.lng == location.lng) {
                        index = i;
                    }
                });
                return index;
            },
            //從收藏地點中增加或者刪除元素
            toggle: function(item) {
                var index = Locations.getIndex(item);
                if (index >= 0) {
                    Locations.data.splice(index, 1);
                } else {
                    Locations.data.push(item);
                }
            },
            //如果新增數據,將其移到頂層或者將它增加到頂層
            primary: function(item) {
                var index = Locations.getIndex(item);
                if (index >= 0) {
                    Locations.data.splice(index, 1);
                    Locations.data.splice(0, 0, item);
                } else {
                    Locations.data.unshift(item);
                }
            }
        };
        //返回帶有數據和方法的Locations對象
        return Locations;
    })

  上面個我們使用Angular服務工廠函數定義了一個可以在不同控制器中共享使用的服務,將這個服務添加到不同的視圖中后,隨便某個視圖中的該服務有變化,其他視圖也會顯示出相應的變化。我們用Locations.data這個數組來存儲地點列表,並為Locations服務建立了三個方法,如果收藏的地點存在,getIndex()方法會返回收藏地點在Locations.data數組中的索引,toggle()方法會檢查Locations.data數組中是否存在收藏地點,如果存在就刪除如果不存在就添加,primary()方法則用來將收藏地點置頂,或者移除一個已經置頂的地點。

  現在,我們配置好了Locations服務后,可以實現在側滑菜單中顯示收藏城市的列表了。首先,我們需要為側滑菜單添加一個控制器來向應用作用域中注入Locations服務,因為這個控制器屬於側滑菜單視圖,不是一個獨立的視圖頁面,所以我們可以把控制器代碼直接放在app.js文件中,打開app.js文件:

    //創建一個控制器並注入服務
    .controller('LeftMenuController', function($scope, Locations) {
        //將地點列表數據映射到作用域中
        $scope.locations = Locations.data;
    })

  然后我們把控制器添加到側滑菜單模板中去,打開index.html文件,做如下修改:

        <!-- 聲明一個側滑菜單並設置其位置為左側邊緣 添加控制器-->
        <ion-side-menu side="left" ng-controller="LeftMenuController">

  下面我們來更新一下側滑菜單,給它添加一個收藏列表用以顯示已經收藏的地點,還是打開index.html文件:

            <ion-content>
                <ion-list>
                    <ion-item class="item-icon-left" ui-sref="search" menu-close>
                        <span class="icon ion-search"></span> 查詢城市
                    </ion-item>
                    <ion-item class="item-icon-left" ui-sref="settings" menu-close>
                        <span class="icon ion-ios-cog"></span> 設置
                    </ion-item>
                    <!-- .item-divider樣式用來給文字添加背景以做區別顯示 -->
                    <ion-item class="item-divider">收藏城市</ion-item>
                    <!-- 循環顯示地點列表,顯示城市的名稱,增加到其具體天氣視圖的鏈接,使用menu-close指令來確保單擊后隱藏側滑菜單 -->
                    <ion-item class="item-icon-left" ui-sref="weather({city: location.city,lat:location.lat,lng:location.lng})" menu-close ng-repeat="location in locations">
                        <span class="icon ion-ios-location"></span>{{location.city}}
                    </ion-item>
                </ion-list>
            </ion-content>

  側滑菜單的收藏列表現在我們預覽,就能夠看到啦~

 

 

  現在,我們來創建設置視圖的模板,新建文件www/views/settings/settings.html:

<ion-view view-title="設置">
    <ion-content>
        <ion-list>
            <ion-item class="item-divider">單位</ion-item>
            <!-- 使用ionRadio組件來切換溫度顯示的單位 -->
            <ion-radio ng-model="settings.units" ng-value="'si'">攝氏度</ion-radio>
            <ion-radio ng-model="settings.units" ng-value="'us'">華氏度</ion-radio>
            <div class="item item-divider">
                預報天數<span class="badge badge-dark">{{settings.days-1}}</span>
            </div>
            <!-- 使用范圍選擇器來控制顯示天數 -->
            <div class="item range range-positive">
                1
                <input type="range" name="days" ng-model="settings.days" min="2" max="8" value="8"> 7
            </div>
            <!-- 創建一個帶有可切換canDelete變量狀態的分割線 -->
            <div class="item item-button-right">
                收藏城市
                <button class="button button-small" ng-click="canDelete = !canDelete">{{canDelete ? '完成' : '修改'}}</button>
            </div>
        </ion-list>
        <!-- 創建一個地點列表並根據canDelete值顯示刪除按鈕 -->
        <ion-list show-delete="canDelete">
            <!-- 循環遍歷顯示所有的地點 -->
            <ion-item ng-repeat="location in locations">
                <!-- 當列表的刪除狀態為真時顯示刪除按鈕 -->
                <ion-delete-button class="ion-minus-circled" ng-click="remove($index)">
                </ion-delete-button>
                {{location.city}}
            </ion-item>
        </ion-list>
        <p class="padding">天氣數據由<a href="">Forecast.io</a>提供,地理位置信息由<a href="">谷歌提供。</a></p>
    </ion-content>
</ion-view>

  設置視圖模板已經完成了,現在我們來給他添加一個控制器,可以快速訪問之前寫好的服務,同時增加刪除按鈕的邏輯,新建文件www/views/settings/settings.js:

angular.module('starter')
    //添加控制器並注入服務
    .controller('SettingsController', function($scope, Settings, Locations) {
        //在scope中添加設置的收藏地點數據
        $scope.settings = Settings;
        $scope.locations = Locations.data;
        //為刪除操作設置默認狀態
        $scope.canDelete = false;
        //定義從收藏地點中刪除元素的方法
        $scope.remove = function(index){
            Locations.toggle(Locations.data[index]);
        };
    });

  把設置視圖的控制器文件引入index.html文件中:

    <script src="views/settings/settings.js"></script>

  然后我們為設置頁面添加一個路由,打開app.js文件:

            .state('settings', {
                url: '/settings',
                controller: 'SettingsController',
                templateUrl: 'views/settings/settings.html'
            });

  現在,我們來看一下設置視圖的預覽效果吧~

  6.5 設置天氣視圖

  現在我們來制作天氣視圖頁面。天氣視圖頁面用來顯示某地點的當前天氣和天氣預報。

  我們會使用Forecast.io服務發送請求來獲取天氣數據。所以要先創建一個賬號,登錄https://darksky.net/dev/地址,創建賬號,然后使用申請的免費賬號登錄獲取的token。

  Forecast.io服務是不提供跨域資源共享功能的,也就是說,默認情況下,我們不能在瀏覽器中載入它們的API數據。Ionic命令行組件提供了一些特性,包括允許通過使用代理來解除瀏覽器的這個限制。

  在應用中,需要使用ionic.config.json文件,這個文件包含一個json對象用來配置Ionic項目,在配置中可以定義一個需要被代理的地址列表,在項目根目錄下打開ionic.config.json文件:

{
    "name":"weatherApp",
    "app_id":"",
    "proxies":[
        {
        "path":"/api/forecast",
        "proxyUrl":"https://api.darksky.net/forecast/YOUR_KEY/"
        }
    ]
}

  上面的這段代碼中,我們增加了一個proxies的代理屬性,它的值為一個數組或者對象,在這個對象里,我們增加了一個path屬性,用來定義最終會被應用訪問的代理地址,還增加了一個proxyUrl屬性,用來定義初始會被訪問的地址,your_key即為之前注冊時給你的key值,lat和lng為你希望應用初始訪問的地點的地理編碼。

  當ionic serve命令運行成功時,我們的代理也就成功運行了。不過這種方法只能用於使用Forecast.io服務進行本地開發,因為當應用在移動設備中運行時,它是沒有Ionic命令行組件來代理API請求的。此時,我們只能通過升級API接口讓其支持跨域資源共享(cors),或者增加cors代理服務來解決跨域的問題。

  現在,我們來增加天氣視圖模板,基本需求是在頭部標題欄顯示地點的名稱並在視圖中展示當前溫度,創建文件www/views/weather/weather.html:

<ion-view view-titile="{{params.city}}">
    <ion-content>
        <h3>實時天氣</h3>
        <p>{{forecast.currently.temperature | number:0}}&deg;</p>
    </ion-content>
</ion-view>

  然后我們來創建天氣視圖的控制器,新建文件www/views/weather/weather.js:

angular.module('starter')
    //創建WeatherController控制器,並注入服務
    .controller('WeatherController', function($scope, $http, $stateParams, Settings) {
        //在scope中添加服務數據
        $scope.params = $stateParams;
        $scope.settings = Settings;
        //添加HTTP請求載入forecast的天氣數據
        $http.get('/api/forecast/' + $stateParams.lat + ',' + $stateParams.lng, { params: { units: Settings.units } }).success(function(forecast) {
            $scope.forecast = forecast;
        });
    });

  然后,我們將天氣視圖的控制器引入到index.html文件中:

    <script src="views/weather/weather.js"></script>

  最后我們給天氣視圖增加新路由,打開app.js文件:

            //定義天氣視圖路由狀態
            .state('weather', {
                url: '/weather/:city/:lat/:lng',
                controller: 'WeatherController',
                templateUrl: 'views/weather/weather.html' 
            });

  現在,我們來看一下天氣視圖的預覽效果:

  如果能夠看到上面的預覽效果,就證明,我們跨域獲取數據沒有問題,現在我們需要美化天氣視圖的樣式,再給天氣視圖增加一個滾動的功能,以方便展示更多天氣信息。

  我們先來給天氣視圖模板添加一個ionScroll滾動組件,來創建一個垂直滾動分頁的效果。對於垂直滾動,通常我們會使用ionContent組件,這個組件默認就是垂直滾動的並且內容是自帶填充的,但是ionScroll組件更加可配置化,可以設置滾動內容區域函數,以達到我們想要的滾動效果。ionScroll指令需要指定寬度和高度,ionContent則是自適應的。因為應用可能在不同的設備和分辨率上使用,所以,我們不得不基於屏幕大小來計算ionScroll的大小。

  下面,我們先來給天氣視圖模板添加滾動組件,打開weather.html文件:

<ion-view view-title="{{params.city}}">
    <!-- 使用ionContent指令包裹ionScroll組件 -->
    <ion-content>
        <!-- 使用ionScroll指令,鎖定垂直方向滾動,並設置容器的寬高 -->
        <ion-scroll direction="y" paging="true" ng-style="{width:getWidth(), height:getHeight()}">
            <!-- ionScroll內部創建div標簽,並設置所有的div元素大小等於所有的頁面高度之和 -->
            <div ng-style="{height:getTotalHeight()}">
                <!-- 定義具體的每一頁,並設置美一頁的寬高等於ionScroll組件區域大小 -->
                <div class="scroll-page page1" ng-style="{width:getWidth(), height:getHeight()}">
                    page1
                </div>
                <div class="scroll-page page2" ng-style="{width:getWidth(), height:getHeight()}">
                    page2
                </div>
                <div class="scroll-page page3" ng-style="{width:getWidth(), height:getHeight()}">
                    page3
                </div>    
            </div>
        </ion-scroll>
    </ion-content>
</ion-view>

  滾動組件已經按需求布局好了,里面使用的方法,我們在天氣視圖的控制器里進行定義,打開weather.js文件:

        //獲得標題欄的高度
        var barHeight = document.getElementsByTagName('ion-header-bar')[0].clientHeight;
        //返回應用的寬度
        $scope.getWidth = function() {
            return window.innerWidth + 'px';
        };
        //返回去除標題欄后的應用的高度
        $scope.getHeight = function() {
            return parseInt(window.innerHeight - barHeight) + 'px';
        };
        //返回滾動頁面的高度總和
        $scope.getTotalHeight = function() {
            return parseInt(parseInt($scope.getHeight()) * 3) + 'px';
        };

  現在我們已經完成了滾動的部署,下面我們來給每一個滾動頁面添加內容,打開weather.html文件:

<ion-view view-title="{{params.city}}">
    <!-- 使用ionContent指令包裹ionScroll組件 -->
    <ion-content>
        <!-- 使用ionScroll指令,鎖定垂直方向滾動,並設置容器的寬高 -->
        <ion-scroll direction="y" paging="true" ng-style="{width:getWidth(), height:getHeight()}">
            <!-- ionScroll內部創建div標簽,並設置所有的div元素大小等於所有的頁面高度之和 -->
            <div ng-style="{height:getTotalHeight()}">
                <!-- 定義具體的每一頁,並設置美一頁的寬高等於ionScroll組件區域大小 -->
                <div class="scroll-page center" ng-style="{width:getWidth(), height:getHeight()}">
                    <!-- 使用一個標題欄樣式作為二級標題 -->
                    <div class="bar bar-dark">
                        <h1 class="title">實時天氣狀態</h1>
                    </div>
                    <!-- 使用has-header來定位頁面內容 -->
                    <div class="has-header">
                        <h2 class="primary">{{forecast.currently.temperature | number:0}}&deg;</h2>
                        <!-- 基於天氣情況使用icons過濾器獲取當前天氣的圖標 -->
                        <h2 class="secondary icon" ng-class="forecast.currently.icon | icons"></h2>
                        <p>{{forecast.currently.summary}}</p>
                        <p>
                            最高:{{forecast.daily.data[0].temperatureMax | number:0}}&deg; 最低:{{forecast.daily.data[0].temperatureMin | number:0}}&deg; 體感:{{forecast.currently.apparentTemperature | number:0}}&deg;
                        </p>
                        <p>
                            風速:{{forecast.currently.windSpeed | number:0}}
                            <!-- 根據溫度中的風向旋轉屏幕上的箭頭 -->
                            <span class="icon wind-icon ion-ios7-arrow-thin-up" ng-style="{transform: 'rotate(' + forecast.currently.windBearing + 'deg)'}"></span>
                        </p>
                    </div>
                </div>
                <div class="scroll-page" ng-style="{width:getWidth(), height:getHeight()}">
                    <div class="bar bar-dark">
                        <h1 class="title">周天氣預報</h1>
                    </div>
                    <div class="has-header">
                        <p class="padding">{{forecast.daily.summary}}</p>
                        <!-- 使用limitiTo過濾器來設置值,顯示頁面中設置的天數 -->
                        <div class="row" ng-repeat="day in forecast.daily.data | limitTo:settings.days">
                            <!-- 使用date過濾器轉換時間戳為星期 -->
                            <div class="col col-50">{{day.time + '000' | date:'EEEE'}}</div>
                            <div class="col">
                                <span class="icon" ng-class="day.icon | icons"></span>
                                <!-- 使用chance過濾器對百分數取整 -->
                                <sup>{{day.precipProbability | chance}}</sup>
                            </div>
                            <div class="col">{{day.temperatureMax | number:0}}&deg;</div>
                            <div class="col">{{day.temperatureMin | number:0}}&deg;</div>
                        </div>
                    </div>
                </div>
                <div class="scroll-page" ng-style="{width:getWidth(), height:getHeight()}">
                    <div class="bar bar-dark">
                        <h1 class="title">天氣概況</h1>
                    </div>
                    <div class="list has-header">
                        <!-- 獲取已經轉換成當地時區的日出日落時間 -->
                        <div class="item">
                            日出時間:{{forecast.daily.data[0].sunriseTime | timezone:forecast.timezone}}
                        </div>
                        <div class="item">
                            日落時間:{{forecast.daily.data[0].sunsetTime | timezone:forecast.timezone}}
                        </div>
                        <div class="item">
                            能見度:{{forecast.currently.visibility}}
                        </div>
                        <div class="item">
                            空氣濕度:{{forecast.currently.humidity*100}}%
                        </div>
                    </div>
                </div>
            </div>
        </ion-scroll>
    </ion-content>
</ion-view>
View Code

  上面這段代碼中,載入數據的時候使用了很多過濾器,而這些過濾器我們還沒有定義,致使頁面數據顯示會有些問題,所以我們先來定義一下過濾器。因為我們從服務獲取的天氣數據大都不是我們先想要的格式,所以,我們建立一個過濾器來轉換服務器給我的數據格式。首先,我們需要安裝moment.js類庫,執行命令:ionic add moment-timezone(我當時執行沒有成功,按照錯誤提示,又執行了這條命令:bower install --save-dev moment-timezone),安裝完成后,將類庫文件引入到index.html文件中:

    <script src="lib/moment/moment.js"></script>
    <script src="lib/moment-timezone/builds/moment-timezone-with-data.js"></script>

  現在我們已經配置好了moment.js類庫,可以開始創建過濾器了,打開文件app.js,添加如下內容:

//創建時區過濾器,用來根據天氣地點時區轉換時間
    .filter('timezone', function(){
        return function(input,timezone){
            //過濾器只有在時間戳和時區都指定時才開始工作
            if(input && timezone){
                var time = moment.tz(input*1000, timezone);
                return time.format('LT');
            }
            return '';
        };
    })
    //創建chance過濾器,用於將小數轉化為百分比形式數據
    .filter('chance', function(){
        return function(chance){
            //如果給定某值,將其轉化為百分比后取整輸出
            if(chance){
                var value = Math.round(chance / 10);
                return value*10;
            }
            return 0;
        };
    })
    //創建icons過濾器,根據具體的天氣條件返回相應的圖標
    .filter('icons',function(){
        //設置天氣圖標對象,返回在這個對象中找到的對應的值
        var map = {
            'clear-day':'ion-ios-sunny',
            'clear-night':'ion-ios-moon',
            rain:'ion-ios-rainy',
            snow:'ion-ios-snowy',
            sleet:'ion-ios-rainy',
            wind:'ion-ios-flag',
            fog:'ion-ios-cloud',
            cloudy:'ion-ios-cloudy',
            'partly-cloudy-day':'ion-ios-partlysunny',
            'partly-cloudy-night':'ion-ios-cloudy-night'
        };
        return function(icon){
            return map[icon] || '';
        }
    })

  此時,我們查看應用時,數據都按需求顯示了,但是樣式不美觀,打開style.css文件,我們來給天氣視圖增加一些樣式:

.scroll-page .icon:before { padding-right: 5px; }
.scroll-page .row + .row { margin-top: 0; padding-top: 5px; }
.scroll-page .row:nth-of-type(odd) { background: #fafafa; }
.scroll-page .row:nth-of-type(even) { background: #f3f3f3; }
.scroll-page .wind-icon { display: inline-block; }
.scroll-page.center { text-align: center; }
.scroll-page .primary { margin: 0; font-size: 100px; font-weight: lighter; padding-left: 30px; }
.scroll-page .secondary { margin: 0; font-size: 150px; font-weight: lighter; }
.scroll-page .has-header { position: relative; }

  現在我們來預覽一下應用~

 

  下面,我們來個天氣視圖導航欄右側添加一個菜單列表的按鈕,以便向用戶顯示一些可操作選項。這個按鈕是可以通過下方上滑喚醒的組件。里面會有一個"取消"選型,單擊組件外部可以關閉組件列表。

  這個組件的所有內容都寫在$ionicActionSheet服務中,它沒有模板內容,我們需要定義一系列按鈕,告訴每個按鈕的作用。首先,我們先在天氣視圖導航欄添加一個"更多"的圖標按鈕來喚醒活動菜單列表組件,打開weather.html文件:

<ion-view view-title="{{params.city}}">
    <!-- 定義導航欄左側的切換主導航按鈕 -->
    <ion-nav-buttons side="left">
        <button class="button button-clear" menu-toggle="left"><span class="icon ion-navicon"></span></button>
    </ion-nav-buttons>
    <!-- 定義導航欄右側的更多圖標按鈕,並調用showOptions()方法 -->
    <ion-nav-buttons side="right">
        <button class="button button-icon" ng-click="showOptions()"><span class="icon ion-more"></span></button>
    </ion-nav-buttons>

  預覽一下頁面效果:

  現在,打開weather.js文件,向控制器注入$ionicActionSheet服務和Locations服務:

.controller('WeatherController', function($scope, $http, $stateParams, $ionicActionSheet, Settings, Locations) {

  然后還是在weather.js文件中,定義showOptions()方法:

//定義showOptions()方法
        $scope.showOptions = function(){
            //使用show方法設置並顯示活動菜單列表組件,前提是必須注入$ionicActionSheet服務
            var sheet = $ionicActionSheet.show({
                //按鈕對象數組
                buttons:[
                    {text:'收藏城市'},
                    {text:'收藏置頂'},
                    {text:'日出日落'}
                ],
                //顯示取消按鈕並設置顯示文字
                cancelText:'取消',
                //處理按鈕點擊事件方法,參數為點擊按鈕的順序
                buttonClicked:function(index){
                    //Locations服務的toggle方法切換收藏狀態
                    if(index === 0){
                        Locations.toggle($stateParams);
                    }
                    //使用Locations服務的primary方法指定收藏
                    if(index === 1){
                        Locations.primary($stateParams);
                    }
                    //日出日落彈窗功能區域
                    if(index === 2){
                        $scope.showModal();
                    }
                    //返回TRUE會關閉活動菜單列表組件,否則它會保存打開狀態
                    return true;
                }
            });
        };

  現在我們已經實現了收藏切換和置頂的功能,預覽下應用~

  上面那段代碼中,活動菜單列表第三個按鈕日出日落的彈窗方法,我們還沒有定義,現在來完成彈窗功能,繼續在weather.js文件中編輯:

 

        //定義開啟彈窗的方法
        $scope.showModal = function(){
            //如果彈窗已經存在,直接顯示彈窗
            if($scope.modal){
                $scope.modal.show();
            }
            //如果彈窗不存在,則導入彈窗模板,並注入作用域以備使用
            else{
                $ionicModal.fromTemplateUrl('views/weather/modal-chart.html', {
                    scope:$scope
                })
                //當模板加載完畢后,將彈窗實例存儲到作用域中
                .then(function(modal){
                    $scope.modal = modal;
                    //顯示彈窗
                    $scope.modal.show();
                });
            }
        };

  彈窗配置中已經指向了彈窗模板頁面,新建www/views/weather/modal-chart.html文件:

<ion-modal-view>
    <!-- 增加一個帶有關閉按鈕的標題欄 -->
    <ion-header-bar class="bar-dark">
        <h1 class="title">日出日落時間</h1>
        <button class="button button-clear" ng-click="hideModal()">關閉</button>
    </ion-header-bar>
    <ion-content>
    </ion-content>
</ion-modal-view>

  預覽下頁面效果:

  現在彈窗加載正常,但是沒有數據,我們使用SunCalc庫來幫助我們計算全年的日出日落時間。執行命令$ ionic add suncalc,安裝完成后,將SunCalc引入到index.html文件中:

    <script src="lib/suncalc/suncalc.js"></script>

  然后我們來更新一下showModal()方法,打開weather.js文件:

        //定義開啟彈窗的方法
        $scope.showModal = function(){
            //如果彈窗已經存在,直接顯示彈窗
            if($scope.modal){
                $scope.modal.show();
            }
            //如果彈窗不存在,則導入彈窗模板,並注入作用域以備使用
            else{
                $ionicModal.fromTemplateUrl('views/weather/modal-chart.html', {
                    scope:$scope
                })
                //當模板加載完畢后,將彈窗實例存儲到作用域中
                .then(function(modal){
                    $scope.modal = modal;
                    //創建變量
                    var days = [];
                    var day = Date.now();
                    //為每一天增加對應數據
                    for(var i=0;i<365;i++){
                        day += 1000*60*60*24;
                        //使用SunCalc基於經緯度和時間獲取日出和日落的時間
                        days.push(SunCalc.getTimes(day, $scope.params.lat, $scope.params.lng));
                    };
                    //將最后生成的列表數據注入到作用域中
                    $scope.chart = days;
                    //顯示彈窗
                    $scope.modal.show();
                });
            }
        };

  之后,我們來更新一下彈窗模板,使用collection repeat來渲染列表(對於collection-repeat和ng-repeat的區別,大家可以自己查一下喲),打開modal-chart.html文件:

    <ion-content>
        <!-- 使用.list類的div嵌套元素 -->
        <div class="list">
            <!-- 使用collectionRepeat指令 -->
            <div class="item" collection-repeat="day in chart">
                <!-- 綁定日期,日出時間,日落時間到列表元素中 -->
                {{day.sunrise | date:'MMM d'}}:{{day.sunrise | date:'shortTime'}},{{day.sunset | date:'shortTime'}}
            </div>
        </div>
    </ion-content>

  現在我們再預覽一下彈窗,看看效果~

  天氣視圖最后一個功能-->彈框提示並確認收藏地點修改功能。現在,我們點擊活動菜單列表里的收藏按鈕時,應用只會默默的執行,不會給我們提示信息,這樣不友好,用戶在操作后沒有得到反饋。

  我們需要在Locations服務的切換收藏方法中增加彈框方法。首先需要確認用戶是否想要移除/添加收藏地點,並且當收藏地點移除/添加成功后提示用戶,打開app.js文件:

            //從收藏地點中增加或者刪除元素
            toggle: function(item) {
                var index = Locations.getIndex(item);
                if (index >= 0) {
                    //創建確認彈框,參數為預先定義好的對象;默認確認彈框帶有確認和取消按鈕
                    $ionicPopup.confirm({
                        title:'確定嗎?',
                        template:'不再收藏' + Locations.data[index].city
                    })
                    //當單擊彈框中的某個按鈕時,觸發該函數,當用戶點擊確認的時候執行刪除收藏地點功能
                    .then(function(res){
                        if(res){
                            Locations.data.splice(index, 1);
                        }
                    });
                } else {
                    Locations.data.push(item);
                    //創建一個帶有標題的提示彈窗,告知用戶收藏成功,默認只有確認按鈕
                    $ionicPopup.alert({
                        title:'收藏成功'
                    });
                }
            },

  現在我們來預覽一下應用:

  到現在,我們這款天氣應用已經開發完了哦~

 

 

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM