2019年11月26日 星期二

[morse]Capacitor工作流程

1.開發和構建您的Web應用程序

Capacitor將您的Web應用程序轉換為每個平台的本機二進製文件。因此,您的大部分工作將包括開發然後構建針對移動設備的Web應用程序。

您將使用Capacitor的API(例如Camera),或者使用具有Capacitor的Cordova兼容性的現有Cordova插件,在下面與本機平台進行交互。

最後,您將使用類似於以下命令的命令來構建應用程序

npm run build

如果您使用的是框架,請遵循框架的構建過程。

例如,如果您使用的是Ionic,則為

ionic build




2.複製您的網絡資產
準備好在設備或模擬器上本地運行應用程序時,請使用

npx cap copy



3.打開您的本機IDE
Capacitor使用本機IDE來構建,模擬和運行您的應用程序。打開它運行

npx cap open



4.定期維護
您的Capacitor應用需要定期維護,例如更新依賴關係和安裝新插件。

要更新應用程序的依賴關係,請運行

npx cap update
要安裝新的插件(包括Cordova的插件),請運行

npm install really-cool-plugin
npx cap update



5.更新電容器
要檢查電容器本身是否有任何新更新,請運行npx cap doctor以打印出當前已安裝的依存關係以及查看最新的可用依存關係。

要更新電容器核心和CLI:

npm update @capacitor/cli
npm update @capacitor/core


要更新您正在使用的任何或所有平台,請執行以下操作:

npm update @capacitor/ios
npm update @capacitor/android
npm update @capacitor/electron




2019年11月19日 星期二

[morse]HTML5 中的 GPS 定位之 getCurrentPosition

html5 中的 GPS 定位功能主要用的是 getCurrentPosition, 該方法封裝在 navigator.geolocation 屬性裡,是 navigator.geolocation 對象的方法。

getCurrentPosition()函數簡介

getCurrentPosition(successCallback,errorCallback,positionOptions)
successCallback
表示調用getCurrentPosition函數成功以後的回調函數,該函數帶有一個參數,對象字面量格式,表示獲取到的用戶位置數據。該對象包含兩個屬性coords 和timestamp。其中coords 屬性包含以下7個值:
accuracy:精确度
latitude:纬度
longitude:经度
altitude:海拔
altitudeAcuracy:海拔高度的精确度
heading:朝向
speed:速度
errorCallback
和successCallback 函數一樣帶有一個參數,對象字面量格式,表示返回的錯誤代碼。它包含以下兩個屬性:
1message:错误信息
2 code:错误代码。

其中错误代码包括以下四个值:

1UNKNOW_ERROR:表示不包括在其它错误代码中的错误,这里可以在 message 中查找错误信息
2PERMISSION_DENIED:表示用户拒绝浏览器获取位置信息的请求
3 POSITION_UNAVALIABLE:表示网络不可用或者连接不到卫星
4TIMEOUT:表示获取超时。必须在options中指定了timeout值时才有可能发生这种错误
positionOptions
positionOptions 的數據格式為JSON,有三個可選的屬性:
1enableHighAcuracy  布尔值: 表示是否启用高精确度模式,如果启用这种模式,浏览器在获取位置信息时可能需要耗费更多的时间。
2timeout  整数: 表示浏览需要在指定的时间内获取位置信息,否则触发errorCallback
3maximumAge  整数/常量: 表示浏览器重新获取位置信息的时间间隔。

getCurrentPosition()函數定位應用

<!DOCTYPE HTML>
   <head>
      <script type="text/javascript">

         function showLocation(position) {
            var latitude = position.coords.latitude;
            var longitude = position.coords.longitude;
            alert("Latitude : " + latitude + " Longitude: " + longitude);
         }

         function errorHandler(err) {
            if(err.code == 1) {
               alert("Error: Access is denied!");
            }else if( err.code == 2) {
               alert("Error: Position is unavailable!");
            }
         }

         function getLocation(){

            if(navigator.geolocation){
               // timeout at 60000 milliseconds (60 seconds)
               var options = {timeout:60000};
               navigator.geolocation.getCurrentPosition(showLocation, errorHandler, options);
            }else{
               alert("Sorry, browser does not support geolocation!");
            }
         }

      </script>

   </head>

   <html>
      <body>
         <form>
            <input type="button" onclick="getLocation();" value="Get Location"/>
         </form>
      </body>
   </html>
點擊按鈕,就可以回提示是否獲取當前位置,允許之後,可以獲取你所在位置的經緯度!



[morse]Ionic Native Network (網絡)

Network(網絡)


Installation(安裝)

ionic cordova plugin add cordova-plugin-network-information
npm install @ionic-native/network


Usage(使用方法)


app.module.ts

import { Network } from '@ionic-native/network/ngx';

@NgModule( {
    ...
    providers: [
        Network
    ],
} )


.ts
import { Network } from '@ionic-native/network/ngx';

constructor(private network: Network) { }

...

// watch network for a disconnection
let disconnectSubscription = this.network.onDisconnect().subscribe(() => {
  console.log('network was disconnected :-(');
});

// stop disconnect watch
disconnectSubscription.unsubscribe();


// watch network for a connection
let connectSubscription = this.network.onConnect().subscribe(() => {
  console.log('network connected!');
  // We just got a connection but we need to wait briefly
   // before we determine the connection type. Might need to wait.
  // prior to doing any api requests as well.
  setTimeout(() => {
    if (this.network.type === 'wifi') {
      console.log('we got a wifi connection, woohoo!');
    }
  }, 3000);
});

// stop connect watch
connectSubscription.unsubscribe();








From" https://ionicframework.com/docs/native/network









2019年11月17日 星期日

[morse]Angular local store example

Local store example

  
import { Injectable } from '@angular/core';
import { Plugins } from '@capacitor/core';
const { Storage } = Plugins;
@Injectable({
    providedIn: 'root'
})
export class StorageService {
    constructor() { }

    // Store the value
    async store(storageKey: string, value: any) {
        const encryptedValue = btoa(escape(JSON.stringify(value)));
        await Storage.set({
            key: storageKey,
            value: encryptedValue
        });
    }

    // Get the value
    async get(storageKey: string) {
        const ret = await Storage.get({ key: storageKey });
        return JSON.parse(unescape(atob(ret.value)));
    }

    async removeStorageItem(storageKey: string) {
        await Storage.remove({ key: storageKey });
    }

    // Clear storage
    async clear() {
        await Storage.clear();
    }
}


2019年11月14日 星期四

[morse]Ionic 4 Native HTTP Plugin

與傳統的JavaScript HTTP調用相比,進行本機服務器調用具有優勢。在基於Android和IOS的本機應用程序中進行本機HTTP調用。它提供了更高的安全性和更好的呼叫處理功能。



它的一些優點如下:

1.後台線程:所有請求都像本地應用程序一樣在後台線程中完成。

2.HTTP代碼401的處理:在未經授權訪問服務器時,使用基於JavaScript的HTTP調用未正確處理該錯誤代碼401,但未正確訪問服務器。

3.SSL固定:固定用於提高服務的安全性。它用於創建進行通信的安全身份。


在這裡,我們將blank通過運行以下命令使用模板創建一個新的Ionic應用程序:

ionic start ionicHTTP blank
cd ionicHTTP


安裝本機HTTP插件

$ ionic cordova plugin add cordova-plugin-advanced-http
$ npm install @ionic-native/http


導入應用程序模塊

要在應用程序中使用HTTP方法,請打開app.module.ts文件以導入HTTP類,然後添加provider數組,如下所示:

//app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
 
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
 
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
 
import { HTTP } from '@ionic-native/http/ngx';
 
@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
  providers: [
    StatusBar,
    SplashScreen,
    HTTP,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}


要在組件中使用HTTP Native插件,請首先導入HTTP類,然後添加constructor來使用其方法:

//home.page.ts
import { Component } from '@angular/core';
 
import { HTTP } from '@ionic-native/http/ngx';
 
@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
 
  constructor(
    private http: HTTP
  ) {
...
...
}


此本機插件的 HTTP 類提供了一些同步和異步方法:


異步功能

這種功能的類別包括 sendRequest 將方法,如 post,get,put,patch,delete,head,uploadFile和 downloadFile。

它們與前兩個參數採用URL和對options像一起使用,後兩個參數與success和error回調一起使用。

檢查此常見格式以進行 sendRequest HTTP 調用:



this.http.sendRequest('https://google.com/',
      {
        method: 'post',
        data: { id: 12, message: 'test' },
        headers: { Authorization: 'OAuth2: token' },
        timeout: 5000
      }
    )
      .then(response => {
        // prints 200
        console.log(response.status);
      })
      .catch(response => {
        // prints 403
        console.log(response.status);
 
        // prints Permission denied
        console.log(response.error);
      });
 


成功回調

then()方法返回具有4個屬性的對象:

status是HTTP響應代碼,為數字值。

data是來自服務器的字符串響應。

url是任何重定向後以字符串形式獲得的最終URL。

標頭是帶有標頭的對象。



{
  status: 200,
  data: '{"id": 12, "message": "test"}',
  url: 'http://example.net/rest'
  headers: {
    'content-length': '247'
  }
}


失敗回調

catch()方法返回具有4個屬性的對象:

status是HTTP響應代碼,是一個數字值。

錯誤是服務器以字符串形式返回的錯誤響應。

url是任何重定向後以字符串形式獲得的最終URL。

標頭是帶有標頭的對象。



{
  status: 403,
  error: 'Permission denied',
  url: 'http://example.net/noperm'
  headers: {
    'content-length': '247'
  }
}


我們還可以使用特定的請求類型:

請求類型POST,GET,PUT,PATCH,DELETE和HEAD佔用3個參數對象:



網址:URL來發送請求

參數:參數與請求發送

標題:標題來設置此要求

例如,下面是使用POST來自API端點的JSON數據響應的方法:

    this.http.post(
      'https://google.com/',             //URL
      {id: 12, message: 'test'},         //Data 
      { Authorization: 'OAuth2: token' } // Headers
     )
     .then(response => {
        // prints 200
        console.log(response.status);
        try {
          response.data = JSON.parse(response.data);
          // prints test
          console.log(response.data.message);
        } catch(e) {
          console.error('JSON parsing error');
        }
     })
     .catch(response => {
       // prints 403
       console.log(response.status);
 
       // prints Permission denied
       console.log(response.error);
     });


上傳文件

為了將文件保存在服務器上,我們使用uploadFile帶有4個參數的方法:

網址:URL來發送請求

體:該請求的主體

頭:標頭為此請求設置

文件路徑:要上傳的文件的本地路徑

名:參數的名稱傳遞下去作為文件

    this.http.uploadFile(
      'https://google.com/',
      { id: '12', message: 'test' },
      { Authorization: 'OAuth2: token' },
      'file:///somepicture.jpg',
      'myPictureName'
    )
      .then(response => {
        // prints 200
        console.log(response.status);
      })
      .catch(response => {
        // prints 403
        console.log(response.status);
        // prints Permission denied
        console.log(response.error);
      });


下載文件

要從服務器下載文件,有一種downloadFile方法需要四個參數:



url:將請求發送到

正文的url :請求

標頭的正文:為此請求設置的標頭

filePath:下載文件的路徑,包括文件名。

    this.http.downloadFile(
      'https://google.com/',
      { id: '12', message: 'test' },
      { Authorization: 'OAuth2: token' },
      'file:///somepicture.jpg'
    )
      .then(response => {
        // prints 200
        console.log(response.status);
      })
      .catch(response => {
        // prints 403
        console.log(response.status);
        // prints Permission denied
        console.log(response.error);
      });

Ionic的Native HTTP插件還提供了同步功能,用於獲取身份驗證標頭,設置或獲取Cookie以及設置標頭。您可以在此處查看更多信息。




[morse]使用 Ionic Native 插件在 Ionic 4應用程序中打開設備 GPS,而無需離開應用程序

建立 ionic 專案  ionic start ionicGPS blank

選擇 Angular

切換到專案目錄  cd ionicGPS




在這裡,我們將創建一個新的Ionic 4應用程序,該應用程序將包含三個插件,這些插件將有助於提供用戶更加友好的行為。


cordova-plugin-android-permissions:
通過顯示權限對話框來獲取權限。我們將使用此插件來獲取 Geolocation 訪問權限,但這可用於任何類型的權限。
$ ionic cordova plugin add cordova-plugin-android-permissions
$ npm install @ionic-native/android-permissions



cordova-plugin-request-location-accuracy:
向用戶顯示一個對話框,以打開我們在下圖中顯示的 GPS,這樣用戶無需離開應用程序或進行設置。
$ ionic cordova plugin add cordova-plugin-request-location-accuracy
$ npm install @ionic-native/location-accuracy



cordova-plugin-geolocation:
最後,在獲得位置訪問權限並打開設備GPS之後,我們將使用此Geolocation插件獲取準確的設備Geolocation坐標。
$ ionic cordova plugin add cordova-plugin-geolocation
$ npm install @ionic-native/geolocation



接下來,我們需要將這些插件注入app.module.ts文件中,然後添加到 provider 數組中

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
 
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
 
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
 
import { AndroidPermissions } from '@ionic-native/android-permissions/ngx';
import { Geolocation } from '@ionic-native/geolocation/ngx';
import { LocationAccuracy } from '@ionic-native/location-accuracy/ngx';
 
 
@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
  providers: [
    StatusBar,
    SplashScreen,
    AndroidPermissions,
    Geolocation,
    LocationAccuracy,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}



現在,我們將在應用程序的home組件中使用它們。在home.component.ts文件中,我們將添加以下方法。

通過調用以下方法checkGPSPermission()檢查應用程序是否具有訪問設備位置的權限

//Check if application having GPS access permission  
checkGPSPermission() {
 this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.ACCESS_COARSE_LOCATION).then(
   result => {
  if (result.hasPermission) {

    //If having permission show 'Turn On GPS' dialogue
    this.askToTurnOnGPS();
  } else {

    //If not having permission ask for permission
    this.requestGPSPermission();
  }
   },
   err => {
  alert(err);
   }
 );
}



如果沒有,那麼我們將調用requestGPSPermission()方法來從用戶那裡即時獲取位置許可。

  requestGPSPermission() {
    this.locationAccuracy.canRequest().then((canRequest: boolean) => {
      if (canRequest) {
        console.log("4");
      } else {
        //Show 'GPS Permission Request' dialogue
        this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.ACCESS_COARSE_LOCATION)
          .then(
            () => {
              // call method to turn on GPS
              this.askToTurnOnGPS();
            },
            error => {
              //Show alert if user click on 'No Thanks'
              alert('requestPermission Error requesting location permissions ' + error)
            }
          );
      }
    });
  }



如果應用程序具有位置訪問權限,則我們將調用askToTurnOnGPS()方法。這是我們在這裡使用的方法😛此GPS顯示應用程序中的開啟對話

  askToTurnOnGPS() {
    this.locationAccuracy.request(this.locationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY).then(
      () => {
        // When GPS Turned ON call method to get Accurate location coordinates
        this.getLocationCoordinates()
      },
      error => alert('Error requesting location permissions ' + JSON.stringify(error))
    );
  }



用戶成功打開GPS後,我們將調用getLocationCoordinates()方法來獲取設備的準確位置。

  // Methos to get device accurate coordinates using device GPS
  getLocationCoordinates() {
    this.geolocation.getCurrentPosition().then((resp) => {
      this.locationCoords.latitude = resp.coords.latitude;
      this.locationCoords.longitude = resp.coords.longitude;
      this.locationCoords.accuracy = resp.coords.accuracy;
      this.locationCoords.timestamp = resp.timestamp;
    }).catch((error) => {
      alert('Error getting location' + error);
    });
  }



添加上述方法後,我們完整的Home Component ts文件將如下所示:

import { Component } from '@angular/core';
 
import { AndroidPermissions } from '@ionic-native/android-permissions/ngx';
import { Geolocation } from '@ionic-native/geolocation/ngx';
import { LocationAccuracy } from '@ionic-native/location-accuracy/ngx';
 
 
@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss']
})
export class HomePage {
 
  locationCoords: any;
  timetest: any;
 
  constructor(
    private androidPermissions: AndroidPermissions,
    private geolocation: Geolocation,
    private locationAccuracy: LocationAccuracy
  ) {
    this.locationCoords = {
      latitude: "",
      longitude: "",
      accuracy: "",
      timestamp: ""
    }
    this.timetest = Date.now();
  }
 
 
  //Check if application having GPS access permission  
  checkGPSPermission() {
    this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.ACCESS_COARSE_LOCATION).then(
      result => {
        if (result.hasPermission) {
 
          //If having permission show 'Turn On GPS' dialogue
          this.askToTurnOnGPS();
        } else {
 
          //If not having permission ask for permission
          this.requestGPSPermission();
        }
      },
      err => {
        alert(err);
      }
    );
  }
 
  requestGPSPermission() {
    this.locationAccuracy.canRequest().then((canRequest: boolean) => {
      if (canRequest) {
        console.log("4");
      } else {
        //Show 'GPS Permission Request' dialogue
        this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.ACCESS_COARSE_LOCATION)
          .then(
            () => {
              // call method to turn on GPS
              this.askToTurnOnGPS();
            },
            error => {
              //Show alert if user click on 'No Thanks'
              alert('requestPermission Error requesting location permissions ' + error)
            }
          );
      }
    });
  }
 
  askToTurnOnGPS() {
    this.locationAccuracy.request(this.locationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY).then(
      () => {
        // When GPS Turned ON call method to get Accurate location coordinates
        this.getLocationCoordinates()
      },
      error => alert('Error requesting location permissions ' + JSON.stringify(error))
    );
  }
 
  // Methos to get device accurate coordinates using device GPS
  getLocationCoordinates() {
    this.geolocation.getCurrentPosition().then((resp) => {
      this.locationCoords.latitude = resp.coords.latitude;
      this.locationCoords.longitude = resp.coords.longitude;
      this.locationCoords.accuracy = resp.coords.accuracy;
      this.locationCoords.timestamp = resp.timestamp;
    }).catch((error) => {
      alert('Error getting location' + error);
    });
  } 
}
 



在Home組件的HTML模板中,我們只有一個按鈕,該按鈕將調用checkGPSPermission方法,該方法已添加到上面的ts文件中,並且還顯示了收到的地理位置坐標。

< ion-grid fixed>
  < ion-row>
    < ion-col text-center>
      < ion-button (click)="checkGPSPermission()">
        Request GPS Accuracy
      < /ion-button>
    < /ion-col>
  < /ion-row>
  < ion-row>
    < ion-col size="4">Longitude< /ion-col>
    < ion-col> {{locationCoords.longitude}}< /ion-col>
  < /ion-row>
  < ion-row>
    < ion-col size="4">Latitude< /ion-col>
    < ion-col>{{locationCoords.latitude}}< /ion-col>
  < /ion-row>
  < ion-row>
    < ion-col size="4">Accuracy< /ion-col>
    < ion-col>{{locationCoords.accuracy}}< /ion-col>
  < /ion-row>
  < ion-row>
    < ion-col size="4">Timestamp< /ion-col>
    < ion-col>{{locationCoords.timestamp | date:'medium'}}< /ion-col>
  < /ion-row>
< /ion-grid> 


修改 index.html

  
< !doctype html>
< html lang="en">

< head>
  < meta charset="utf-8" />
  < title>ionic app< /title>

  < base href="./" />

  < meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  < meta name="format-detection" content="telephone=no" />
  < meta name="msapplication-tap-highlight" content="no" />

  < link rel="icon" type="image/png" href="assets/icon/favicon.png" />

  < !-- add to homescreen for ios -->
  < meta name="apple-mobile-web-app-capable" content="yes" />
  < meta name="apple-mobile-web-app-status-bar-style" content="black" />

  < script type="text/javascript" src="cordova.js">< /script>

< /head>

< body>
  < app-root>< /app-root>
< /body>

< /html>
  



就是這樣,在上面的教程中,我們了解瞭如何在為應用程序提供地理定位權限後打開設備GPS來更準確地獲取用戶設備的地理定位權限。





以下是 Android 及 IOS 的發佈建置參考指令:
創建 Android 應用

使用ionic tool 創建,測試,運行你的apps

$ cd myApp
$ ionic cordova platform add android
$ ionic cordova build android
$ ionic cordova emulate android



創建IOS應用

$ cd myApp
$ ionic cordova platform add ios
$ ionic build ios
$ ionic emulate ios

如果出現"ios-sim was not found."錯誤,可以執行以下命令:
npm install -g ios-sim




創建電腦應用

在電腦執行出現異常,提示: cordova_not_available

解法是新增一平台為browser

ionic cordova platform add browser

再以下列指令取代ionic serve即可


ionic cordova run browser





2019年11月9日 星期六

[morse]建立 Angular Library 函式庫專案 Part-I

我們先建立一個 Angular 專案

ng new emp20191109

上例中我把專案的名稱定為 "emp20191109",然而在這個新的專案中是可包含多個 Angular 專案在其中的, 你可以在這個專案的根目錄中找到 "angular.json", 如下 :

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "emp20191109": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/emp20191109",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "aot": false,
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb",
                  "maximumError": "10kb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "emp20191109:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "emp20191109:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "emp20191109:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "tsconfig.app.json",
              "tsconfig.spec.json",
              "e2e/tsconfig.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "emp20191109:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "emp20191109:serve:production"
            }
          }
        }
      }
    }},
  "defaultProject": "emp20191109"
}


其中你可以找到 projects 節點,這裡就是定義多專案的設定。


如果我們只想在專案中增加一個函式庫專案,那麼你可以採用下列的 Angular 指令:

ng g library morse-library --prefix=mos


上例中我把函式庫的名稱定義為 "morse-library", 對 Angular 應用程式來說,通常所有 Comeponts 與 Directives 都會以 app 作為前置詞(prefix),但開發給任意專案共用的函式庫,通常不會這樣命名。在上例中我用的前置詞是 mos 基本上它並沒有什麼特別限制,你可依你的想自已給它個名,這裡我個人建議是別用 "app"就好了。


在專案加入了函式庫後 "angular.json" 內容異動如下:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "emp20191109": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/emp20191109",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "aot": false,
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb",
                  "maximumError": "10kb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "emp20191109:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "emp20191109:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "emp20191109:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "tsconfig.app.json",
              "tsconfig.spec.json",
              "e2e/tsconfig.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "emp20191109:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "emp20191109:serve:production"
            }
          }
        }
      }
    },
    "morse-library": {
      "projectType": "library",
      "root": "projects/morse-library",
      "sourceRoot": "projects/morse-library/src",
      "prefix": "mos",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-ng-packagr:build",
          "options": {
            "tsConfig": "projects/morse-library/tsconfig.lib.json",
            "project": "projects/morse-library/ng-package.json"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "projects/morse-library/src/test.ts",
            "tsConfig": "projects/morse-library/tsconfig.spec.json",
            "karmaConfig": "projects/morse-library/karma.conf.js"
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "projects/morse-library/tsconfig.lib.json",
              "projects/morse-library/tsconfig.spec.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    }},
  "defaultProject": "emp20191109"
}

紅字部分是自動增加的內容。


專案目錄的配置如下:



增加了上圖中紅框的配置。

另在專案的幾個配置檔案也都有異動,例如:"tsconfig.json" 配置異動如下:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "module": "esnext",
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ],
    "paths": {
      "morse-library": [
        "dist/morse-library"
      ],
      "morse-library/*": [
        "dist/morse-library/*"
      ]
    }
  },
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  }
}

增加了上例的紅字內容。


在 "projects\morse-library\src" 目錄中的 "public-api.ts" 如下:

/*
 * Public API Surface of morse-library
 */

export * from './lib/morse-library.service';
export * from './lib/morse-library.component';
export * from './lib/morse-library.module';

這函式庫預設生成的內容,你也可以依需作增減,其本上它的內容是可以包含服務(service)、元件(compoment)、模組(module)...等。


Angular 專案中,我們可以透過引用 'MorseLibraryModule' 來 import 函式庫中的任何模組或服務元件,如下:

import { MorseLibraryModule } from 'projects/morse-library/src/public-api';


你可以調整專案的 "app.module.ts" 成如下,以達到整個函式庫引用的目的:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { MorseLibraryModule } from 'projects/morse-library/src/public-api';


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    MorseLibraryModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


紅字部分是調整的內容。


我試著增加函式於 "projects\morse-library\src\lib\morse-library.service.ts" 中,如下:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MorseLibraryService {

  constructor() { }

  getTitle(){
    return "Morse Library Project";
  }
}

紅字部分是增加的內容。


然後試著在專案的 "app.component.ts" 中叫用它 [getTitle()],如下

import { Component, OnInit } from '@angular/core';
import { MorseLibraryService } from 'projects/morse-library/src/public-api';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'emp20191109';

  constructor(
    private _morseLibrary: MorseLibraryService
  ){}

  ngOnInit(){
    this.title = this._morseLibrary.getTitle();
  }  
  
}

紅字部分是調整的內容。


執行結果:






這樣我們就為 Angular 專案完成了自定義函數庫的配置了。