ionic開發篇之踩坑集

一、API篇

1.接口無法訪問

百度地圖、自定義API無法訪問,遠程調試結果顯示404
原因是cordova 5.x的版本增加了“Content-Security-Policy”用於解決安全訪問的問題。默認情況下,只能訪問本機資源。

解決方法:
1.添加白名單插件,在項目目錄下執行

ionic plugin add cordova-plugin-whitelist

2.在index.html頭部增加

<meta http-equiv="Content-Security-Policy"
      content="script-src * 'unsafe-eval'; connect-src * 'unsafe-eval'; 
object-src 'self'; style-src * 'unsafe-inline'; img-src *" >

2.無法跨域訪問

服務端設置(PHP)

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Credentials:true');

3.POST請求提交自動變成Options請求

當我們使用瀏覽器調試的時候,發現POST請求會自動變成Options請求,然後調用任意接口都提示不能跨域訪問,即使服務端已經設置允許跨域訪問。

解決方法:

.config(function ( $stateProvider, $urlRouterProvider, $ionicConfigProvider, $httpProvider) {
    // Use x-www-form-urlencoded Content-Type
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
    }

這個問題只出現在瀏覽器調試中,實際在手機裏運行不需要這個配置。

4. ionic 裏使用 iframe 可能遇到的問題

無法訪問外部url的問題–兩個步驟解決:
iframe的src屬性用ng-src屬性替代,並指明綁定對象: ng-src=”{{targetUrl}}”
在controller裏,調用sce: scope.targetUrl = $sce.trustAsResourceUrl(url)

高度無法最大化的問題–兩個步驟解決:
ion-content 屬性裏添加 scroll=”true” overflow-scroll=”true”,參考
iframe content scroll issue #1151

iframe的style裏添加 min-height: 100%,參考
Fill content container

Ionic與第三方的Wap網頁交互

採用Windows.Open的方法,打開一個瀏覽器,不帶地址欄,全屏的窗口,顯示第三方的WebApp頁面。
代碼如下:

// event類型有以下幾種類型
// loadstart - 開始加載
// loadstop - 完成加載
// loaderror - 加載出錯
// exit - 窗口退出
var ref = window.open(encodeURI(url), '_blank','location=no');
 ref.addEventListener('loadstart', function(event) {
     if (event.url.match("mobile/close")) {
         ref.close();
     }
 }); 

當瀏覽器內的url地址包含 mobile/close頁面的時候,就會關閉當前的瀏覽器。
具體可以參考
http://cordova.apache.org/docs/en/3.0.0/cordova/inappbrowser/inappbrowser.html

二、調試篇

1.本地調試

電腦上本地調試,用ionic serve即可在瀏覽器中調試

2.遠程調試

1.在手機上運行debug版軟件,在電腦上調試程序
2.在啓動手機上的APP後,在谷歌瀏覽器(其實360也行)上輸入chrome://inspect/#devices,可以進入調試界面(如果出不來,請翻牆)
這裏寫圖片描述
3.單擊inspect,可以進入當前顯示的頁面調試,調試方法和和在瀏覽器上一致。

遠程調試可以快速定位,在瀏覽器上沒有發現的問題,方便調試手機API接口

三、應用篇

1.導航置底設置

好不容易按教程一步步建立了tabs樣例工程,卻發現安卓機上這個tab導航在頂部,瀏覽器和ios這個導航在頂部。
解決方案:
在app.js裏添加以下代碼

.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('center');
    $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');

2.百度地圖開發

坑1:使用了插件angular-BMap

使用了插件angular-BMap

這個插件功能並不完善,好多功能都沒有,如果要使用需要繼續開發(如果你有時間,有興趣,有能力可以fork後繼續開發)

坑2:使用了百度地圖實例代碼,地圖不顯示

var map = new BMap.Map("container");          // 創建地圖實例  
var point = new BMap.Point(116.404, 39.915);  // 創建點座標  
map.centerAndZoom(point, 15);                 // 初始化地圖,設置中心點座標和地圖級別  

其實只是在模擬手機的瀏覽器下不顯示,在瀏覽器上不要選擇任何手機型號,就能顯示,而實際我在自己的手機上build後,也是能正常顯示的。具體原因沒有深究,如果有知道的朋友歡迎來信。

坑3:GPS位置偏移

使用$cordovaGeolocation.getCurrentPosition()獲得的座標,在百度地圖上位置偏移。原因是GPS座標和百度地圖座標並不是完全對應的,需要使用百度地圖提供的GPS座標轉換接口進行轉換

 function posToAddrByBaidu(lat, long){

        var Ak = 'yourAK'; //你應用的AK

        var getUrl = 'http://api.map.baidu.com/geocoder/v2/?'+ 'ak=' + Ak +'&location='
            +lat+ ',' + long + '&output=json&pois=0';

        $http.get(getUrl)
            .success(function (data) {
                if (data['status'] == '0') {
                    $scope.appeal.location = data.result.formatted_address;

                } else {
                    return '定位失敗';

                }

            }).error(function () {
                alert("網絡問題");
            });

    }

坑4 infowindow裏面的a鏈接只能跳轉一次

BMap中創建的infowindow,如果裏面帶有a鏈接,第一次跳轉後,第二次進去就無法跳轉了。這個問題目前不知道怎麼解決,如果有人知道請告訴我,謝謝。

四、常見錯誤

1. 編譯錯誤

a. Execution failed for task ‘:app:mergeDebugResources’ OR Execution failed for task ‘:Application:processDebugResources’

> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\Users\admin\AppData\Local\Android\sdk\build-tools\23.0.3\aapt.exe'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

Error: Error code 1 for command: cmd with args: /s,/c,"D:\myApp\platforms\android\gradlew cdvBuildDebug -b D:\myApp\platforms\android\build.gradle -Dorg.gradle.daemon=true -Pandroid.useDeprecatedNdk=true"

檢查一下,看看是不是哪裏的資源文件命名不規範,文件名稱不規範就可能導致該問題.
或者執行以下命令:

C:\>D:\myApp\platforms\android\gradlew cdvBuildDebug -b D:\myApp\platforms\android\build.gradle -D org.gradle.daemon=true -Pandroid.useDeprecatedNdk=true" --info --debug > d:\debug.info

然後從d:\debug.info文件中查找詳細的錯誤信息。

2.ionic build android 沒有反應

檢首次Build的時候 android平臺,會安裝gradle工具,但是因爲網絡的問題,很可能安裝的非常的慢到無法忍受。那麼很有可能是因爲你的Gradle的工具會去下載platforms/android/cordova和CordovaLib的maven的庫,這個時候需要修改build.gradle文件的repositories庫中,mavenCentral()修改成爲阿里雲的庫

    repositories {
        flatDir {
            dirs 'libs'
        }
        //mavenCentral()
        maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
    }

常識與技巧篇

  1. list 有延遲,可以在ion-content處使用 overflow-scroll=”true”嘗試
  2. 上用ng-click上是沒效果的
  3. 快捷修改背景色style=”background-color: #212326;”
  4. 能用ng-if就用ng-if,ng-if的效率比ng-show和ng-hide高
  5. 直接在ion-list中的ion-item中並不能觸發ng-click事件,可以在item中的元素上再套一層div
  6. 可以用ng-class=”{‘important’: post.important}”配合css 根據列表元素顯示不同的效果
  7. 獲取日期用filtervarpostdate= filter(‘date’)(date, ‘yyyy-MM-dd HH:mm:ss’);
  8. 列表中的元素不能寫成 id : 4,應寫成 id : “4”,注意在創建id變量的時候也需要轉成string,如:

    var id = InfoListService.getListLength()+1+"";

    1. 使用loglog, log而不是console.log呢?可以看看這個
    2. 在安卓上的體驗比較差,動畫有延遲?可以試試ionic集成的crosswalk
    3. controllers和services 的文件名可能會重合,但是他們意義差不多,可以將controllers中的文件名小寫,對應的services中的文件名大寫進行區分,或者加後綴xxxControler,xxxService
    4. 安裝cordova插件的時候用ionic plugin add …的方式添加,這樣會在package.json中添加這個插件的條目,如果有人clone了你的項目想在本地運行,可以用ionic state restore它會根據cordovaPlugins條目安裝對應的插件。如果直接用cordova plugin add 安裝則不會更新package.json。
    5. 上傳base64編碼的時候如果提示413錯誤,是因爲文件過大導致的,可以在nodejs中設置bodyparser的文件限制:
var bodyParser = require('body-parser');
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
  1. img 中 base64編碼的圖片無法顯示?在源碼中發現angular添加了unsafe標籤?需要在白名單中添加data:image
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|content|file|assets-library):|data:image\//);
  1. 有時候pm2運行有問題,重啓一下即可

Q&A篇

Q: 在 iOS 下使用 cordova-plugin-file-transfer 下載中文名文件失敗,提示 Could not create target file
A: encodeURI(“包含霸氣的中文文件名的 URI”)

Q: 應用需要存儲較大量數據,原始格式是 json ,存 sqlite 數據庫嫌麻煩。
A: lokiJS ,類 mongodb 的 js 內存數據庫,配合爲 ionic 打造的插件做持久化存儲。

Q: 不同 Android 手機上出現字體錯位之類的奇怪問題。
A: 使用 Crosswalk 消除不同安卓機上 WebView 的差別,順便還能提升應用性能。

Q: 在實機上使用 livereload 功能時出現空白、連接失敗等情況。
A: 實機上的 livereload 本質是用手機訪問電腦上的網站,檢查手機和電腦之間的網絡連接是否通暢。

Q: gitignore 默認排除了 plugins 文件夾,團隊其他人 clone 了項目後缺少插件,一個一個裝太麻煩。
A: ionic platform add/remove xxx 以及 ionic plugin add/remove xxx 的時候,Ionic CLI 都在 package.json 中保存了項目的狀態。clone 完後可以使用 ionic state restore 命令快速恢復

Q: 在哪裏查看 Ionic 帶的所有圖標?
A: http://ionicons.com

Q: 在 ionic platform add xxx 時卡住
A: 掛 VPN ,或者丟着睡一覺(不確定是不是網絡原因,就遇過兩次沒深究)

Q: tel:xxxxx sms:xxxxxx mailto:xxxxxx geo:xxxxxx 一類的鏈接不能喚起其他應用。
A: 在 config.xml 中加入:

<access origin="*"/>
<access origin="tel:*" launch-external="yes"/>
<access origin="sms:*" launch-external="yes"/>
<access origin="mailto:*" launch-external="yes"/>
<access origin="geo:*" launch-external="yes"/>

Q: 跟上 Q 一樣,加了還 TM 不行!
A: 再在 config.xml 中加入:

<allow-intent href="tel:*"/>
<allow-intent href="sms:*"/>
<allow-intent href="mailto:*"/>
<allow-intent href="geo:*"/>

Q: Android 中調用其他應用打開 applicationDirectory 下的文件時提示路徑不存在, iOS 可以。
A: 兩個系統策略不一樣, Android 中有這個需求簡單的辦法是參考該頁中的 Android 文件系統佈局,把文件從 Private 目錄複製到 Public 目錄下再做操作。

Q: 對 Android 進行遠程調試。
A: 打開 Chrome ,地址欄輸入 chrome://inspect

Q: 對 iOS 進行遠程調試
A: 打開 Safari -> 開發 -> 手機名 -> 應用名

Q: Ionic 的 Modal 是什麼鬼?不能給它設定狀態麼?!
A: 超級弱逼的模態框,因爲 uirouter 的限制,給它轉狀態非常不方便。確定只需要一個頁面就能完成的操作才用他。下一 Q 提供個解決辦法。

Q: 替代 Modal 的方案
A: 在 $state.go 前記錄下當前的 view ,然後禁止下一個 view 記錄 backView ,就不會顯示後退按鈕( Android 硬件後退也不行 )。在需要關閉時,後來加入導航棧的任意 view 中設置 backView 爲記錄下來的 view ,然後 back 。

// go 的時候
var backHistoryId = $ionicHistory.currentHistoryId();
var backViewId = $ionicHistory.currentView().viewId;
$ionicHistory.nextViewOptions({
  disableBack: true,
  disableAnimate: true
});
$state.go('my-awesome-modal', {backViewId: backViewId});

// back 的時候
var backHistoryId = $ionicHistory.currentHistoryId();
var backView = $ionicHistory.viewHistory().histories[backHistoryId].stack.filter(function (v) {
  return v.stateId === $stateParams.backViewId;
})[0];
$ionicHistory.backView(backView);
$ionicHistory.goBack();

Q: ionic serve 或在實機調試時開啓了 livereload 功能時的跨域問題
A:道理還是因爲這兩種狀態下, APP 實際是在訪問電腦上的一個網站,任何指向其他地方的鏈接都是跨域。實機不開 livereload 則不存在這個問題。
簡單的方法就是用實機調試且不開 livereload 。
複雜點的比如設置 Ionic 自帶的代理服務器,參考鏈接。需要詳細瞭解這個問題也可以看一遍。

Q: 如何在某個界面中去掉導航欄?
A: 如果某個界面上不想要導航欄,可以簡單地在最頂端的標籤中添加hide-nav-bar=”true”

如何在ionic中加載本地圖片?
A: 對於css文件夾中的樣式文件中如果要調用本地的圖片的話,從該css文件所在的文件夾開始算,例如www/css/style.css要加../,否則在瀏覽器中可以正常顯示,在設備上不行,結構如下所示:

.login-page {
  background:url(../img/signup_bg.png);
  background-size: cover;
  background-repeat: no-repeat;
}

但是對於在頁面中定義的圖片路徑,從www路徑開始算,否則瀏覽器中可顯示,但設備上不行,img文件夾和index.html在一級,如:

<img src="img/commander.jpg">

Q: 如何在ionic中嵌入網頁代碼?
A: 使用ng-bind-html這個類,不過它會過濾原始html的標籤,我們可以引入sce sce.trustAsHtml()方法信任我們獲取的網頁

Q: 如何將template加載到某個tab或某個sidemenu項目下?
A:<ion-nav-view name="menuContent">可以指定name,然後在子狀態中使用該name,ionic就知道該把該狀態的template渲染到哪邊了。例如:

 // signup page
 .state('auth.signup', {
   url: '/signup',
   views: {
       'auth-signup': {
           templateUrl: 'templates/auth-signup.html',
           controller: 'SignUpCtrl'
       }
   }
 })

另有一個tabs中聲明該auth-signup:

 <ion-tab title="Sign Up" icon-on="ion-ios-personadd"
   icon-off="ion-ios-personadd-outline" href="#/auth/signup">
   <ion-nav-view name="auth-signup"></ion-nav-view>
</ion-tab>

Q: 運行serve命令時ionic報錯?
A:

ionic $ An uncaught exception occured and has been reported to Ionic

看看你是不還有一個終端在運行着serve呢?

Q: 用docker跑ionic的時候,不能把地址綁定到0.0.0.0怎麼處理?
A:可以用ionic serve -all的方法解決

Q: 加載頁面的時候會看到雙括號一閃而過?
A: angularjs在使用雙括號的時候,第一個加載的頁面,也就是應用中的index.html,其未被渲染好的模版可能會被用戶看到。用ng-bind就不會遇到這個問題。造成這種現象的原因是,瀏覽器需要首先加載HTML頁面,渲染它,然後Angular纔有機會把它解釋成你期望看到的內容。不過好消息是,在大多數的模版中你依然可以使用雙括號.但是對於index.html頁面中的數據綁定操作,建議使用ng-bind
ng-bind使用方式如下: <p ng-bind="greeting"></p>

Q: 更新了數據,如何讓界面更新呢?
A: 可以用廣播,注意broadcast emit的區別

Q: 如何實現IonicView中card上面有一列分割線的效果?
A: 在css裏定義

#info-up {
  border-top: 4px solid #f06336;
}

Q: controller.js和service.js文件越來越大怎麼辦?
A:所有的控制器不必都放在controllers.js這一個文件中,可以新建controllers文件夾,
然後把每個controller都建一個.js文件,同理services和utils等都是.但注意要在index.html中head部分聲明.但是爲了避免他們相互覆蓋,第一個加載的js中模塊中要加[…],其他都不需要。如:

// File : /js/directives/mainDirective.js
angular.module('app.directives',[]);

// File : /js/directives/myGreatDirective.js
angular.module('app.directives')  
    .directive('myGreatDirective', function(){
        return {
            //...
        }
    });

// File : /js/directives/myBetterDirective.js
angular.module('app.directives')  
    .directive('myBetterDirective', function(){
        return {
            //...
        }
    });

...

angularjs-code-organization瞭解更多,嗯這篇文章寫的還不是best practice,因爲你還得記着自己把[]寫到那個模塊裏了,統一地寫在app.js中即可,在app.js最下面加上類似:

angular.module('fcws.controllers',['ionic', 'fcws.services']);
angular.module('fcws.services', []);

可以達到和上面一樣的效果,而且可以統一管理.

Q: 如何尋找優秀的範例代碼?
A:目前有些ionic 的app沒有進行代碼混淆,至少ionic官方的ionic view沒有進行代碼混淆,下載他們的app,文件名改成zip,解壓,所有的 www文件都在assets文件夾中,相當於開源了有木有,看看那些最優秀的practice。看中哪些優秀的app,下下來,如何在googleplay上下載?把googleplay應用的地址貼到apps.evozi中。

Q: 如何顯示相對時間?
A:如幾分鐘前,幾天前等,可以用momentjs,看這篇教程

Q:發佈應用的時候如果遇到翻譯錯誤即MissingTranslation怎麼辦?
A:暫時的解決方法是,不進行翻譯校正, 在 /platforms/android/build.gradle 中的android {}節中加入:

lintOptions {
    disable 'MissingTranslation'
    disable 'ExtraTranslation'
}

Q: 如何在列表右下方添加時間等信息?
A:span可以用來將時間之類的附加信息顯示到列表右邊,如下面會將創建時間顯示在name的右邊:

<ion-item class="item item-avatar-left " ng-repeat="message in messages">
    <img src="../../img/commander.jpg">
    <span class="item-note">{{message.create_at}}</span>
    <h2 >{{message.name}}</h2>
    <p >  {{message.content}}</p>
</ion-item >

Q:如何回到上一頁面?
A:用$ionicHistory這個模塊,引入該模塊後使用goBack([backCount]),backCount指定回去多少個頁面(-1代表回去一個頁面),默認爲-1

Q:如何關閉應用?
A:

ionic.Platform.exitApp();

Q: 在安卓設備上如何讓title居中?
A: 在headerbar中添加align-title=”center”,如:

<ion-header-bar class="bar-positive" align-title="center">
     <h1 class="title">{{username}}</h1>
</ion-header-bar>

不過這個設置對ion-view無效,親測,如果要統一讓所有navbar上的title居中(包括上面的headerbar),可以在config裏設置,如:

.config(function($stateProvider, $urlRouterProvider,$ionicConfigProvider) {
  $ionicConfigProvider.navBar.alignTitle('center');
  ...

如果要讓某一個view title居中,可以用ionicNavBarDelegate[ionic](http://ionicframework.com/docs/api/service/ ionicNavBarDelegate/)

Q: 如何讓在sidemenu中的headerbar能夠顯示頭像等其他信息?
A: 解決方案是去掉headerbar,添加一個avatar到sidemenu content中,如:

<ion-side-menu side="left">
    <ion-content class="bar-positive">
        <ion-list>
            <ion-item class="item item-avatar item-positive" href="#">
                <img src="img/commander.jpg">
                <h2 class=" light">
                    <i class="icon ion-ios-star"></i>{{title}}
                </h2>
                <a>{{username}}</a>
            </ion-item>

Q: ionic的subheader擋住了內容區域怎麼辦?
A: 解決方案是給加類has-subheader,同理也可以加has-header。如下:

<ion-content class="has-header has-subheader">

Q:對於需要添加數據的list,在添加數據後頁面不能及時刷新造成卡頓怎麼辦?
A:可以使用$ionicScrollDelegate.resize();在添加數據後手動進行重新刷新,記得添加依賴

Q:ionic如何處理回退按鈕?例如詢問用戶是否真的要退出應用
A:可以在app.js的.run方法中增加對硬件回退按鈕的註冊處理,這裏我在大部分頁面都想註冊該事件,除去有二級歷史頁面的我單獨判斷了下,注意增加依賴。

$ionicPlatform.registerBackButtonAction(function(e) {
    var current_state_name = $state.current.name;
    if(current_state_name !== 'sidemenu.post'
     && current_state_name !== 'sidemenu.contact_town' &&
    current_state_name !== 'sidemenu.contact_people'){
        $ionicPopup.confirm({
            title: '退出應用',
            template: '您確定要退出xxxx嗎?'
        }).then(function (res) {
            if (res) {
                //ionic.Platform.exitApp();
                navigator.app.exitApp();
            } else {
                console.log('You are not sure');
            }
        });
        e.preventDefault();
        return false;
    }else{
        navigator.app.backHistory();
    }
},100);

Q:ionic如何實現對每個請求都添加認證信息或認證失敗自動重新登錄?
A:在應用的註冊或者登錄部分,不記名token響應了這個請求並且這個token被存儲到本地存儲中。當你向後端請求一個服務時,你需要把這個token放在頭部中。你可以在app.js的.config方法中使用AngularJS的攔截器實現這個。每次請求都會被攔截並且會把認證頭部和值放到頭部中,同理如果服務器端響應401或403,跳轉到重新登錄頁面.

$httpProvider.interceptors.push(function ($q, $location, User, $rootScope) {
    return {
        'request': function (config) {
            config.headers = config.headers || {};
            if (User.getToken()) {
                config.headers.Authorization = 'Bearer ' + User.getToken();
            }
            return config;
        },
        'responseError': function (response) {
            if (response.status === 401 || response.status === 403) {
                //如果之前登陸過
                if (User.getToken()) {
                    $rootScope.$broadcast('unAuthenticed');
                }
            }
            return $q.reject(response);
        }
    };
});

Q:ionic如何實現搜索框內的全部清除按鈕?
A:在label中的input不能嵌入按鈕,因爲ionic對於label中的tap事件會進行重定向到input上。解決方案是將label替換成span或div。如下面的搜索框,注意ng-model需要是一個對象才能置空,變量不行:

<span class="item-input-wrapper">
    <i class="icon ion-ios-search placeholder-icon"></i>
    <input type="search" placeholder="請輸入姓名前綴" ng-model="search.key">
    <i class="icon ion-close-circled placeholder-icon" style="vertical-align: middle;"
        on-tap="clearSearch()" ng-if="search.key.length"></i>
</span>
發佈了116 篇原創文章 · 獲贊 92 · 訪問量 112萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章