一、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裏,調用
高度無法最大化的問題–兩個步驟解決:
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" }
}
常識與技巧篇
- list 有延遲,可以在ion-content處使用 overflow-scroll=”true”嘗試
- 在上用ng-click上是沒效果的
- 快捷修改背景色style=”background-color: #212326;”
- 能用ng-if就用ng-if,ng-if的效率比ng-show和ng-hide高
- 直接在ion-list中的ion-item中並不能觸發ng-click事件,可以在item中的元素上再套一層div
- 可以用ng-class=”{‘important’: post.important}”配合css 根據列表元素顯示不同的效果
- 獲取日期用
filter,varpostdate= filter(‘date’)(date, ‘yyyy-MM-dd HH:mm:ss’); - 列表中的元素不能寫成 id : 4,應寫成 id : “4”,注意在創建id變量的時候也需要轉成string,如:
var id = InfoListService.getListLength()+1+"";
- 使用
log進行log輸出,爲什麼用 log而不是console.log呢?可以看看這個 - 在安卓上的體驗比較差,動畫有延遲?可以試試ionic集成的crosswalk
- controllers和services 的文件名可能會重合,但是他們意義差不多,可以將controllers中的文件名小寫,對應的services中的文件名大寫進行區分,或者加後綴xxxControler,xxxService
- 安裝cordova插件的時候用ionic plugin add …的方式添加,這樣會在package.json中添加這個插件的條目,如果有人clone了你的項目想在本地運行,可以用ionic state restore它會根據cordovaPlugins條目安裝對應的插件。如果直接用cordova plugin add 安裝則不會更新package.json。
- 上傳base64編碼的時候如果提示413錯誤,是因爲文件過大導致的,可以在nodejs中設置bodyparser的文件限制:
- 使用
var bodyParser = require('body-parser');
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
- img 中 base64編碼的圖片無法顯示?在源碼中發現angular添加了unsafe標籤?需要在白名單中添加data:image
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|content|file|assets-library):|data:image\//);
- 有時候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的標籤,我們可以引入
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: 可以用廣播,注意
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居中,可以用
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>