Writing in the front

The APP was launched in time for Spring Festival, so this time we share our experience building a Hybird APP using Ionic3 + Angular5. What the Hybird App is and the selection of some technologies are not discussed here. I write part after part, so some articles are a little long. If there are mistakes, thank you for correcting ~

Why did you chooseIonic ?

Some of my friends say That Angular/Ionic is not very good, but I feel that there is no good or bad technology. Ionic has been in the Hybird App development space for many years. It’s quite mature, and I think it’s better than most of the solutions. Secondly, because our App is of a weakly interactive and multi-display type, Ionic meets our needs. Finally, Ionic is the way to go if you want to build an APP without the support of the Android team or the IOS team. Since Ionic4 is still in beta and is a company project, the stable 3.x version is still used.

Note: Non-basic introductory tutorial, So suggest you had better know before reading this article presents (https://www.angular.cn/guide/quickstart), (TS) (https://www.tslang.cn/docs/home.html), The basic knowledge of [Ionic] (https://ionicframework.com/docs/), here is mainly in the hope that we can go less detours when using Ionic.

Because MY own use is not very skilledRxjsI didn’t write this one. I’ll do it laterRxjsMore profound understanding of plus

Angular Summary section

Since it’s based on Angular, let’s take a look at Angular first. This place is the accumulation of Angular bits and pieces. If the content is too much, it will be split into separate parts later

Angular component lifecycle

The Angular lifecycle

Hooks official Introduction

  • constructor(): called before any other lifecycle hooks. You can use it to inject dependencies,But don’t do business here.
  • ngOnChanges(changes: SimpleChanges) => void: called when the value of the bound input property changes,The first call must occur atngOnInit()before
  • ngOnInit() => void: In the first roundngOnChanges()Call when done.Only called once
  • ngDoCheck() => void: called during each change detection cycle,ngOnChanges()ngOnInit()after
  • ngAfterContentInit() => void:AngularCalled after projecting external content into the component/directive view. Think of it as external content initialization
  • ngAfterContentChecked() => void:AngularCalled after change detection of the projected component content is completed. Think of it as an external content update
  • ngAfterViewInit() => void: every timeAngularInitialize theComponent view and its subviewsCall later.Only called once.
  • ngAfterViewChecked() => void: every timeAngularAfter checking for changes to component views and subviews,ngAfterViewInit()And every timengAfterContentChecked()It will be called afterwards.
  • ngOnDestroy() => void: before Angular destroys the directive/component.

Content mapping (slot) implementation in Angular

  • Default mapping This content mapping direction is from the parent component to the child component. This is equivalent to slot in vUE, and uses the same way:

    <! -- Parent component -->
    <child-component>I am mapped by default from the contents of the parent component</child-component>
    <! -- Subcomponent -->
    <! - slot - >
      <ng-content>
        
      </ng-content>
    Copy the code

    This is the simplest way to use the default mapping

  • Targeted mapping (named slots) We can also implement our named slots via the SELECT attribute. This one can be filled based on conditions. Select attributes are supported based on CSS selectors (ELement, Class, [attribute]…). To match your elements, accept all if not set, as follows:

    <! -- Parent component -->
    <child-component>I am mapped by default from the contents of the parent component<header>I mapped it from the header</header>
      <div class="class">I'm mapping by class</div>
      <div name="attr">I'm mapping from attR</div>
    </child-component>
    
    <! -- Subcomponent -->
    <! -- named slot -->
    <ng-content select="header"></ng-content>
    <ng-content select=".class"></ng-content>
    <ng-content select="[name=attr]"></ng-content>
    Copy the code
  • NgProjectAs, those are all mappings and they’re all mappings as direct child elements, but what if they weren’t? I want to put another layer on the outside?

    <! -- Parent component -->
    <child-component>
      <! -- ngProjectAs --> ngProjectAs -->
      <div>
        <header>I mapped it from the header</header>
      </div>
    </child-component>
    Copy the code

    With ngProjectAs, it can be applied to any element.

    <! -- Parent component -->
    <child-component>
      <div ngProjectAs="header">
        <header>I mapped it from the ngProjectAs header</header>
      </div>
    </child-component>
    Copy the code
  • Ng-content has an @contentChild decorator that can be used to invoke and project content. Note, however, that elements queried through ContentChild can only be successfully retrieved during the ngAfterContentInit declaration cycle.

Now that ng-content is mentioned, let’s talk about ng-template and ng-Container

  • ng-template

    The element is the best choice for dynamically loading components because it does not render any additional output

    <div class="ad-banner-example">
      <h3>Advertisements</h3>
      <ng-template ad-host></ng-template>
    </div>
    Copy the code
  • Ng-container is a syntax element that the Angular parser recognizes and handles. It is not a directive, component, class, or interface, more like the curly braces in JavaScript if blocks. Commonly used to group sibling elements, it does not pollute style or element layout because Angular doesn’t put it in the DOM at all.

    <p>
      I turned the corner
      <ng-container *ngIf="hero"><! -- ng-container will not be rendered -->
        and saw {{hero.name}}. I waved
      </ng-container>
      and continued on my way.
    </p>
    Copy the code

Presents the instructions

Angular directives are divided into component, property, and structural directives. Attribute directives are used to change the appearance or behavior of a DOM element, such as NgStyle. Structural directives are responsible for HTML layout. They shape or reshape the STRUCTURE of the DOM, such as adding, removing, or maintaining elements such as NgFor and NgIf.

  1. Attribute instruction
  • throughDirectiveThe decorator marks a class asAngularDirective, which provides configuration metadata that determines how the directive is to be processed, instantiated, and used at run time.@Directive
  • throughElementRefGets the DOM object of the bound element,ElementRef.
  • throughHostListenerBinds an event to a host listener in response to user-raised events and provides configuration metadata. When a host element emits a specific event, Angular executes the provided handler method and uses the result to update the element to which it is bound. If the event handler returnsfalseIs executed on the bound elementpreventDefault.HostListener
  • throughInputDecorators mark a class field as an input property and provide configuration metadata. Declare an input property that is available for data binding. During change detection,AngularIt automatically updates it,@Input.
@Input('appHighlight') highlightColor: string;
Copy the code

Here is a complete example of an attribute directive

import {Directive, ElementRef, HostListener, Input} from '@angular/core';

@Directive({
  selector: '[sxylight]'
})
export class SxylightDirective {
  constructor(private el: ElementRef) {
    el.nativeElement.style.backgroundColor = 'yellow';
  }
  // The value of the instruction binding
  @Input('sxylight') highlightColor: string;
  // Inside the directive, this property is called highlightColor. Outside, where you bind to it, it is called sxylight, which is the alias of the binding

  // The value of the directive host binding
  @Input() defaultColor: string;
  // Listen for host events
  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || this.defaultColor || 'red');
  }
  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }
  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color; }}Copy the code
  1. Structural instruction
  • Asterisks (*) prefix: This thing is syntax sugar,Angular*ngIfAttributes translated into one<ng-template>Element is used to wrap the host element.
  • <ng-template>It is an Angular element that renders HTML. It never shows up directly. In fact, Before rendering a view, Angular replaces its contents with a comment.
  • <ng-container>: It is a grouped element, but it does not pollute the style or element layout becauseAngularI’m not gonna put it inDOMIn the.
  • TemplateRef: AvailableTemplateRefachieve<ng-template>The content of theTemplateRef
  • ViewContainerRef: YesViewContainerRefTo access this view container,ViewContainerRef.

Complete sample

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
/** * Input: passes values * TemplateRef: represents an embedded template that can be used to instantiate embedded views. To instantiate an embedded view from a template, use the createEmbeddedView() method of ViewContainerRef. * ViewContainerRef: Represents a container that can attach one or more views to a component. * /
@Directive({
  selector: '[structure]' // Attribute selector
})
export class StructureDirective {
  private hasView = false
  @Input(a)set structure(contion: boolean) {
    console.log(contion)
    if(! contion && !this.hasView) {
      this.viewCon.createEmbeddedView(this.template) // Instantiate the inline view and insert it into the container
      this.hasView = true
    } else if (contion && this.hasView) {
      this.viewCon.clear() // Destroy all attempts in the container
      this.hasView = false}}constructor(
    private template: TemplateRef<any>,
    private viewCon: ViewContainerRef
  ) {
    console.log('Hello StructureDirective Directive'); }}Copy the code

Presents the Module

First let’s look at the NgModule

interface NgModule {
  // Providers: This option is an array that requires us to list the services that our module needs to share
  // Then we can use dependency injection in various components of this module.
  providers : Provider[]
  // declarations: an array type option that declares directives, pipes, and so on that belong to the module.
  // Then we can use them in this module.
  declarations : Array<Type<any>|any[] >// imports: Array type option that our module needs to rely on some other modules for the purpose of making our module
  // You can directly use some instructions, components, etc provided by other modules.
  imports : Array<Type<any>|ModuleWithProviders|any[] >// exports: array type options. This module exports components, directives, modules, etc.
  // If another module imports our module,
  // Then other modules can directly use the components we export here, instruction modules, etc.
  exports : Array<Type<any>|any[] >// entryComponents: An array type option that specifies a list of components that will be compiled when the module is defined
  Angular creates a ComponentFactory for each component and stores it in ComponentFactoryResolver
  entryComponents : Array<Type<any>|any[] >// Bootstrap: An array type option that specifies which components should be started when the module is started. Of course these components are automatically added to entryComponents
  bootstrap : Array<Type<any>|any[] >// Schemas: Elements or attributes of components or directives that do not belong to Angular need to be declared here.
  schemas : Array<SchemaMetadata|any[] >// id: string option, the module's hidden ID, which can be a name or a path; Used to distinguish modules in getModuleFactory if the property is undefined
  // Then the module will not be registered.
  id : string
}
Copy the code
  • app.module.ts
App. The module. Ts └ ─ ─ ─ @ NgModule └ ─ ─ ─ declarations// Tell Angular which modules belong to the NgModule│ ─ ─ ─ imports// Import the required modules│ ─ ─ ─ the bootstrap// Start the module│ ─ ─ ─ entryComponents// Define components that should be compiled when building└ ─ ─ ─ will// Service configuration
Copy the code

EntryComponents :Angular uses entryComponents to enable tree-shaking, that is, compiling only components that are actually used in the project, not all components that are declared in the ngModule but never used. The offline template compiler (OTC) only generates the components that are actually used. If the component is not directly used in the template, OTC does not know if it needs to be compiled. With entryComponents, you can tell OTC to also compile these components so that they are available at run time.

Ionic Engineering directory structure

First, look at the project catalog

Ionic - frame │ build// Package the extension
│   platforms               // Android/IOS platform code
│   plugins                 / / cordova plug-in│ resources └ ─ ─ ─ the SRC// Business logic code
│   │   app                 // Start the component
│   │   assets              / / resources
│   │   components          // Public components
│   │   config              // Config file
│   │   directive           // Public directive
│   │   interface           // interface configuration center
│   │   pages               / / page│ │ will/ / public service
│   │   service             // business logic service
│   │   shared              // Shared module
│   │   theme               // Style module│ │ index which s// Declaration file└ ─ ─ ─ the WWW// Static resources after packaging
Copy the code

Ionic View lifecycle

The importance of the life cycle goes without saying, according to Ionic’s website

  • constrctor => voidThe constructor starts, and the constructor is fired before ionViewDidLoad
  • ionViewDidLoad => void: Triggered when resources are loaded. IonViewDidLoad only fires the first time the page is enteredOnly trigger once
  • ionViewWillEnter => void: the page is about to be triggered upon entryIt’s triggered every time
  • ionViewDidEnter => void: Go after entering the viewIt’s triggered every time
  • ionViewWillLeave => void: Triggered when you are about to leave (only to trigger the action to leave)It’s triggered every time
  • ionViewDidLeave => void: Triggered when the page has been leftIt’s triggered every time
  • ionViewWillUnload => void: Triggered when the page is about to be destroyed and its elements deleted
  • ionViewCanEnter => boolean: runs before the view can be entered. This can be used as a “protection” in authenticated views, where you need to check permissions before the view can be entered
  • ionViewCanLeave => boolean: runs before the view can leave. This can be used as a “guard” in authenticated views, where you need to check permissions before the view leaves

Note: when you want to useionViewCanEnter/ionViewCanLeaveTo intercept a route, you need to return oneBoolen. returntrueGo to the next view and returnfasleStay in the current view.

You can get a sense of the order of the life cycle by following the code below

constructor(public navCtrl: NavController) {
  console.log('Trigger constructor')}/** * When the page is loaded, "load completed" means that the required resources for the page have been loaded, but the page has not entered the state (the user still sees the previous page). */ will only be called once throughout
ionViewDidLoad () {
  console.log(` Ionic triggered ionViewDidLoad `);
  // Step 1: Create Chart object
  const chart = new F2.Chart({
    id: 'myChart'.pixelRatio: window.devicePixelRatio // Specify the resolution
  })
  // Step 2: Load the data source
  chart.source(data)
  chart.interval().position('genre*sold').color('genre')
  chart.render()
}
/** * Preprocessing the page data will trigger */ every time it enters the Ionic view
ionViewWillEnter(){
  console.log(` Ionic triggered ionViewWillEnter `)}/** * will trigger */ every time it enters the Ionic view
ionViewDidEnter(){
  console.log(` Ionic triggered ionViewDidEnter `)}/** * fires when the page is about to (has finished)
ionViewWillLeave(){
  console.log(` Ionic triggered ionViewWillLeave `)}/** * The page has been fired when it leaves, leaving the page inactive. */ is triggered each time
ionViewDidLeave(){
  console.log(` Ionic triggered ionViewDidLeave `)}/** * the resource in the page is about to be destroyed
ionViewWillUnload(){    
  console.log(` Ionic triggered ionViewWillUnload `)}// Guard navigation hook: returns true or false
/** * run before the view can be entered. This can be used as a "protection" in authenticated views, where you need to check permissions */ before the view can be entered
ionViewCanEnter(){
  console.log(` Ionic triggered ionViewCanEnter `)
  const date = new Date().getHours()
  console.log(date)
  if (date > 22) {
    return false
  }
  return true
}
/** * run before the view can leave. This can be used as a "guard" in authenticated views, where you need to check permissions */ before the view leaves
ionViewCanLeave(){
  console.log(` Ionic triggered ionViewCanLeave `)
  const date = new Date().getHours()
  console.log(date)
  if (date > 10) {
    return false
  }
  return true
}
Copy the code

Project profile Settings

Ionic3.X does not provide the corresponding configuration file, so we need to manually add the configuration file to the project by following the steps below.

  1. newconfigdirectory
src
  |__config
      |__config.dev.ts
      |__config.prod.ts
Copy the code

config.dev.ts / config.prod.ts

export const CONFIG = {
  BASE_URL            : 'http://XXXXX/api'./ / API address
  VERSION             : '1.0.0'
}
Copy the code
  1. Add it in the root directorybuildFolder, add to folderwebpack.config.jsThe config file
const fs = require('fs')
const chalk =require('chalk')
const webpack = require('webpack')
const path = require('path')
const defaultConfig = require('@ionic/app-scripts/config/webpack.config.js')

const env = process.env.IONIC_ENV
@param {*} env */
function configPath(env) {
  const filePath = `./src/config/config.${env}.ts`
  if(! fs.existsSync(filePath)) {console.log(chalk.red('\n' + filePath + ' does not exist! '));
  } else {
    returnfilePath; }}// Locate the current file
const resolveDir = filename= > path.join(__dirname, '.. ', filename)
// Other folder aliases
let alias ={
  "@": resolveDir('src'),
  "@components": resolveDir('src/components'),
  "@directives": resolveDir('src/directives'),
  "@interface": resolveDir('src/interface'),
  "@pages": resolveDir('src/pages'),
  "@service": resolveDir('src/service'),
  "@providers": resolveDir('src/providers'),
  "@theme": resolveDir('src/theme')}console.log("The current APP environment is:+process.env.APP_ENV)
let definePlugin =  new webpack.DefinePlugin({
  'process.env': {
    APP_ENV: '"'+process.env.APP_ENV+'"'}})// Set the alias
defaultConfig.prod.resolve.alias = {
  "@config": path.resolve(configPath('prod')), // Config file. alias } defaultConfig.dev.resolve.alias = {"@config": path.resolve(configPath('dev')),
  ...alias
}

// Other environments
if(env ! = ='prod'&& env ! = ='dev') {
  defaultConfig[env] = defaultConfig.dev
  defaultConfig[env].resolve.alias = {
    "@config": path.resolve(configPath(env))
  }
}
/ / remove sourceMaps

module.exports = function () {
  return defaultConfig
}
Copy the code
  1. tsconfig.jsonCooperate, add the following content in configuration this place is ridiculousthispathRelevant needs to be placedtsconfig.jsonThe top of the
"baseUrl": "./src",
  "paths": {
    "@app/env": [
      "environments/environment"
    ]
  }
Copy the code
  1. Modify thepackage.json. The following information is added at the end of the configuration
"config": {
  "ionic_webpack": "./config/webpack.config.js"
}
Copy the code
  1. Using configuration variables
import {CONFIG} from "@app/env"
Copy the code

If we want to modify other WebPack configurations in the Ionic, we can do so in the form above.

// Get the default configuration of WebPack
const defaultConfig = require('@ionic/app-scripts/config/webpack.config.js');
// Modify the configuration like this
defaultConfig.prod.resolve.alias = {
  "@config": path.resolve(configPath('prod'))
}
defaultConfig.dev.resolve.alias = {
  "@config": path.resolve(configPath('dev'))}Copy the code

Ionic routing

  • Home Page Settings Sometimes we need to set the page that we display for the first time. Then we need to use NavController to set it up

    // app.component.ts
    public rootPage: any = StartPage; // 
    Copy the code
  • Routing hop

    1. Href jump: Specifies the page to jump to directly in the DOMtabsTake the code in
    <! -- single jump button [root]="HomeRoot" is the most important -->
    <ion-tab [root] ="HomeRoot" tabTitle="Home" tabIcon="home"></ion-tab>
    Copy the code
    import { HomePage } from '.. /home/home'
    export class TabsPage {
      // Declare the variable address
      HomeRoot = HomePage
      constructor() {}}Copy the code
    1. Programmatic navigation: We will probably use programmatic navigation more often. Here is a basic example

    Programmatic navigation is controlled by the NavController

    NavController is the base class for navigation controller components such as Nav and Tab. You can use the navigation controller to navigate to pages in your application. At the basic level, a navigation controller is an array of pages that represent a particular history, such as a Tab. This array can be manipulated to navigate through the application by pushing and popping up pages or inserting and deleting them anywhere in the history. The current page is the last page in the array, the top of the stack if we think of it that way. Pushing a new page to the top of the navigation stack causes the new page to be animated, while popping up the current page navigates to the previous page in the stack.

    Unless you use directives like NavPush or require a specific NavController, most of the time you will inject and use references to the most recent NavController to manipulate the navigation stack.

    / / introduce NavController
    import { NavController } from 'ionic-angular';
    import { NewsPage } from '.. /news/news'
    export class HomePage {
      / / injection NavController
    constructor(public navCtrl: NavController) {
      // this.navCtrl.push(LoginPage)
    }
    goNews () {
        this.navCtrl.push(NewsPage, {
          title : 'Test parameter Transfer'}}})Copy the code
  • Related common apis

    1. navCtrl.push(OtherPage, param): Jump page
    2. navCtrl.pop(): Removing a viewRemoving the current View is equivalent to returning to the previous page
    3. Parameters in the route are related
    • push(Page, param)Passant: This is very simple and clear
    this.navCtrl.push(NewsPage, {
      title : 'Test parameter Transfer'
    })
    Copy the code
    • [navParams]Properties:HTMLCoordinate for parameter transmission
    import {LoginPage } from'./login';
    @Component()
    class MyPage {
      params;
      pushPage: any;
      constructor() {this.pushPage= LoginPage;
        this.params ={ 
          id:123.name: "Carl"}}}Copy the code
    <button ion-button [navPush] ="pushPage" [navParams] ="params">
      Go
    </button>
    <! -- Pass arguments to root page as follows:
    <ion-tab [root] ="tab1Root"  tabTitle="home" tabIcon="home"  [rootParams] ="userInfo">
    </ion-tab
    Copy the code
    • To obtain parameters
    //NavController is a controller used to manage and navigate pages
    constructor(public navCtrl: NavController, public navParams: NavParams) {
      //1: the NavParams get method is used to get a single object
      this.titleName = navParams.get('name')
      //2: get all parameters directly
      this.para = navParams.data
    }
    Copy the code

Use the provider (service)

When a method in a class is repeatedly needed, it can be encapsulated as a service class for reuse, such as HTTP.

Provider, also called service. The former is ionic, the latter ng. Learn more about Angular

  • createProvider IonicCreate instructions are provided
ionic g provider http 
Copy the code

Note that you need to inject a decorator into the app.module first, and then decorate it with decorators, so that the class can be injected as a Provider into other classes to use:

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

export class StorageService {
  constructor() {
    console.log('Hello StorageService');
  }
  myAlert(){
    alert("Methods of the service class")}}Copy the code
  • useprovider

If it is a top-level service (global universal service), register it with app.module.ts providers and use it

import { StorageService } from '. /.. /.. /service/storage.service';
export class LoginPage {

  userName: string = 'demo'
  password: string = '123456'

  constructor(
    public storageService: StorageService
    ) {
    
  }
  doLogin () {
    const para = {
      userName: this.userName,
      password:  this.password
    }
    console.log(para)
    if (para.userName === 'demo' && para.password === '123456') {
      this.storageService.setStorage('user', para)
    }
    setTimeout((a)= > {
      console.log(this.storageService.getStorage('user'))},3000)}}Copy the code

Ionic event system

Events is a publish-subscribe style event system for sending and responding to application-level Events in your application.

This is the core of communication between different pages. Mainly used for component communication. You can also use Events to pass data to any page.

Events instance method

  • publish(topic, eventData): Release aevent
  • subscribe(topic, handler): Subscribe to oneevent
  • unsubscribe(topic, handler)Unsubscribe oneevent
// Publish event login.ts
// Publish event events
submitEvent (data) {
  console.log(1)
  this.event.publish('user:login', data)
}
// Subscribe to the message.ts page
constructor(public event: Events ) {
  // Subscribe to event events
  event.subscribe('user:login', (data) => {
    console.log(data)
    let obj = {
      url: 'assets/imgs/logo.png'.name: data.username
    }
    this.messages.push(obj)
  })
}
Copy the code

Note: 1: the subscription must be published before, otherwise it cannot be received. To use an analogy: for example, the wechat public account, you have to pay attention to receive its tweets, otherwise it no matter how many tweets, you will not receive. 2: Subscribe this is a bit of a problem, we need to pay attention to this.

User Operation Event

Basic gestures can be accessed from HTML by binding to tap, press, pan, swipe, rotate, and pinch events.

Ionic’s explanation of the gesture incident is largely glib.

Intercomponent communication

Communication between components: it’s time for a componentized framework to play 6. The component’s previous communication is understood as a prerequisite. In Ionic, we do this the way Angular does.

  • Parent => child: @input ()

    • throughInput bindingPass data from parent to child: This is the most widely and commonly used, andrecatThe props in
    // The parent component defines the value (to be passed)
    export class NewsPage {
      father: number = 1 // Parent component data
      /** * Ionic lifecycle function */
      ionViewDidLoad() {
        // Parent component data changes
        setTimeout((a)= > {
          this.father ++ 
        }, 2000)}}// Subcomponent defines attributes (for receiving)
    @Input() child: number // The @input decorator identifies child as an Input property
    Copy the code
    <! -- Parent component use -->
    <backtop [child] ="father"></backtop>
    <! -- Subcomponent definition -->
    <div class="backtop">
      <p (click) ="click()">back</p>{{child}}</div>
    Copy the code
    • throughget, setIntercepts the parent’s data in the child to achieve the desired result
    // Intercepts the parent component's value
    private _showContent: string 
    @Input()
    // set value
    set showContent(name: string) {
      if(name ! = ='4') {
        this._showContent = 'no'
      } else {
        this._showContent = name
      }
    }
    // get value
    get showContent () :string {
      return this._showContent
    }
    Copy the code
    • throughngOnChangesMonitoring is worth changing
    // Listen for all attributes worth changing
    ngOnChanges(changes: SimpleChange): void {
      Class SimpleChange {constructor(previousValue: any, currentValue: any, firstChange: Boolean) previousValue: any // currentValue: any // currentValue firstChange: Boolean isFirstChange(): Boolean // Check if the new value is from the first assignment. } * /
      // Changes props collection object
      console.log(changes['child'].currentValue) // 
    }
    Copy the code
    • The parent component passes the child componentThe local variableinteractive

    A parent component cannot use data binding to read a child component’s properties or call its methods. In the parent component template, however, you can create a local variable to represent the child, and then use this variable to read the child’s properties and call its methods.

    Define this component with #childComponent. Then call it directly using childComponent.xxx. This is a little more powerful, but this communication is at the page level. Limited to defining local variables in HTML and then manipulating and communicating in HTML. That is, parent-child connections must all be made in the parent’s template. The parent component’s own code has no access to the child component.

    <! -- Parent component -->
    <button ion-button color="secondary" full  (click) ="childComponent.fromFather()">Testing local variables</button>
    <backtop #childComponent [child] ="father" [showContent] = "father" (changeChild) ="childCome($event)"></backtop>
    Copy the code
    / / child component
    // Parent components interact with local variables
    fromFather () {
      console.log(`I am from father`)
      this.show  = !this.show
    }
    Copy the code
    • Parent component call@ViewChild()interactive

    If the parent component’s class needs to read property values or call methods of the child component, it can inject the child component as a ViewChild into the parent component.

    In other words, @viewChild () is designed to address the above shortcomings.

    / / the parent component
    import { Component, ViewChild } from '@angular/core';
    export class NewsPage {
      // Define child component data
      @ViewChild(BacktopComponent)
      private childComponent: BacktopComponent
      ionViewDidLoad() {
        setTimeout((a)= > {
          // Call child component methods with child
          this.childComponent.formChildView()
        }, 2000)}}Copy the code
  • Child => parent: @output () : the most commonly used method

The child component exposes an EventEmitter property that it uses emits(catapult up) events when an event occurs. The parent component binds to this event property and responds when the event occurs.

/ / the parent component
// Receives the value of the child component and assigns the value of the child to the father
childCome (data: number) {
  this.father =  data
}
/ / word components
// The child passes the event object to the parent
@Output() changeChild: EventEmitter<number> = new EventEmitter() // Define the event propagator object
// Execute the child component to communicate with its parent
click () {
  this.changeChild.emit(Awesome!)}Copy the code
<! -- Parent component -->
<backtop [child] ="father" [showContent] = "father" (changeChild) ="childCome($event)"></backtop>
Copy the code

Gets the parent component instance

Sometimes we can be violent enough to get an instance of the parent component and use it (unverified).

constructor@Host() @inject (forwardRef() => NewsPage)) father: NewsPage) {this.text = 'Hello World';
    setTimeout((a)= > {
      // Modify the parent component directly through the object
      father.father++
    }, 3000)}Copy the code
  • Parent <=> child: Parent components communicate through services

    If we limit the scope of a service instance to parent and child components, components outside the component subtree will not be able to access or communicate with the service. Fathers and sons share a service, so we can use that service to achieve two-way communication within the family.

    // service
    import { Injectable } from '@angular/core'; // mark metadata
    // Use service to communicate between parent and child components
    @Injectable()
    export class MissionService {
      familyData: string = 'I am family data'
    }
    Copy the code
    // father component
    import { MissionService } from '. /.. /.. /service/mission.service';
    export class NewsPage {
      constructor( public missionService: MissionService) {
      }
      ionViewDidLoad() {
        // Parent component data changes
        setTimeout((a)= > {
          // The call modifies the data in the service
          this.missionService.familyData = 'change familyData'
        }, 2000)}}// child component
    import { Component} from '@angular/core';
    import { MissionService } from '. /.. /.. /service/mission.service';
    @Component({
      selector: 'backtop'.templateUrl: 'backtop.html'
    })
    export class BacktopComponent {
      constructor(
        public missionService:MissionService
      ) {
        console.log(missionService)
        this.text = 'Hello World';
      }
      // Execute the child component to communicate with its parent
      click () {
        // Modify share information
        this.missionService.familyData = 'change data by child'}}Copy the code
    <! -- Parent component used directly -->
    {{missionService.familyData}}
    <! -- Subcomponent -->
    <div>Servicedata: {{missionService. FamilyData}}</div>
    Copy the code

    Using subscriptions in a service can do the same for data communication

    // mission.service.ts
    import { Subject } from 'rxjs/Subject';
    import { Injectable } from '@angular/core'; // mark metadata
    // Use service to communicate between parent and child components
    @Injectable()
    export class MissionService {
      familyData: string = 'I am family data'
      // Subscribe to shared data
      private Source = new Subject()
      Status$=this.Source.asObservable()
      statusMission (msg: string) {
        this.Source.next(msg)
      }
    }
    
    / / the parent component
    // Submit information through a subscription to the service
    emitByService () {
      this.missionService.statusMission('emitByService')}/ / child component
    // Return a subscriber
    this.subscription = missionService.Status$.subscribe((msg:string) = > {
      this.text = msg
    })
    ionViewWillLeave(){
      // Unsubscribe
      this.subscription.unsubscribe()
    }
    Copy the code
  • Advanced communication

    1. We can useIonic - presents the EventsModule to doFather <=> son, brother <=> younger brotherAdvanced communication.EventsModule has unique advantages in communication. For details, see the example above
    2. useEventEmitterThe module
    // service
    import { EventEmitter } from '@angular/core'; // mark metadata
    // Use service to communicate between parent and child components
    @Injectable(a)export class MissionService {
      // The Event communication comes from Angular
      serviceEvent = new EventEmitter()
    }
    
    / / the parent component
    // Receive information through the Events module advanced communication
    this.missionService.serviceEvent.subscribe((msg: string) = > {
      this.messgeByEvent = msg
    })
    
    / / child component
    // Send new through advanced communication via emit
    emitByEvent () {
      this.missionService.serviceEvent.emit('emit by event')}Copy the code

Shared component

Public component Settings. Angular advocates modular development, so public component registrations may be slightly different.

Here we use the CommonModule module provided by Angular to see what it does:

  1. It imports theCommonModuleBecause the module needs some common instructions.
  2. It declares and exports a number of instrumental pipes, directives, and component classes.
  3. It’s reexportedCommonModuleFormsModule
  4. CommonModuleFormsModuleCan replaceBrowserModuleTo use the
  • Defined in thesharedNew foldershared.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; 

// By re-exporting CommonModule and FormsModule, any other modules that import this SharedModule can access directives such as NgIf and NgFor from CommonModule. You can also bind to properties from [(ngModel)] in the FormsModule.
// Custom modules and directives
import { ComponentsModule } from '. /.. /components/components.module';
import { DirectivesModule } from '. /.. /directives/directives.module';

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    FormsModule
  ],
  exports:[
    // Export the module
    CommonModule,
    FormsModule,
    ComponentsModule,
    DirectivesModule
  ],
  entryComponents: [

  ]
})
export class SharedModule {}
Copy the code

Note: Services are handled through a separate dependency injection system, not a modular system

Using shared modules only needs to be referenced in xxx.module.ts, and then you can use all the common modules introduced in shared.

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { XXXPage } from './findings';
import { SharedModule } from '@shared/shared.module';

@NgModule({
  declarations: [
    XXXPage,
  ],
  imports: [
    SharedModule,
    IonicPageModule.forChild(FindingsPage),
  ]
})
export class XXXPageModule {}
Copy the code

HTTP part

The HTTP module in Ionic uses the HttpClient module directly. This is nothing to say, we just need to change the Service to suit our needs, such as changing HTTP to a more flexible Promise pattern. You can also use Rxjs patterns to do this. Here is a simple implementation:

import { TokenServie } from './token.service';
import { StorageService } from './storage.service';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
import { Injectable, Inject } from '@angular/core'
import {ReturnObject, Config} from '.. /interface/index' // Returns the data type and configuration file
/*
Generated class for the HttpServiceProvider provider.
*/
@Injectable()
export class HttpService{
  /** * @param CONFIG * @param http * @param navCtrl */
  constructor(
    @Inject("CONFIG") public CONFIG:Config, 
    public storage: StorageService,
    public tokenService: TokenServie,
    public http: HttpClient
    ) {
      console.log(this.CONFIG)
  }
  /** * key to 'name='qweq'' * @param key * @param value */
  private toPairString (key, value): string {
    if (typeof value === 'undefined') {
      return key
    }
    return `${key}=The ${encodeURIComponent(value === null ? ' ' : value.toString())}`
  }
  /** * objetc to url params * @param param */
  private toQueryString (param, type: string = 'get') {
    let temp = []
    for (const key in param) {
      if (param.hasOwnProperty(key)) {
        let encodeKey = encodeURIComponent(key)
        temp.push(this.toPairString(encodeKey, param[key]))
      }
    }
    return `${type === 'get' ? '? ' : ' '}${temp.join('&')}`
  }
  /** * set http header */
  private getHeaders () {
    let token = this.tokenService.getToken()
    return new HttpHeaders({
      'Content-Type':  'application/x-www-form-urlencoded'.'tokenheader': token ? token : ' '})}/** * HTTP post request for promise * @param URL * @param body */
  public post (url: string, body ? : any): Promise<ReturnObject> {
    const fullUrl = this.CONFIG.BASE_URL + url
    console.log(this.toQueryString(body, 'post'))
    return new Promise<ReturnObject>((reslove, reject) = >{
      this.http.post(fullUrl, body, {
        // params,
        headers: this.getHeaders()
      }).subscribe((res: any) = > {
        reslove(res)
      }, err => {
        // this.handleError(err)
        reject(err)
      })
    })
  }
  /** * get request return promise * @param URL * @param param */
  public get(url: string, params: any = null) :Promise<ReturnObject> {
    const fullUrl = this.CONFIG.BASE_URL + url
    let realParams = new HttpParams()
    for (const key in params) {
      if (params.hasOwnProperty(key)) {
        realParams.set(`${key}`, params[key])
      }
    }
    // add time map
    realParams.set(
      'timestamp', (new Date().getTime()).toString()
    )
    return new Promise<ReturnObject>((reslove, reject) = >{
      this.http.get(fullUrl, {
        params,
        headers: this.getHeaders()
      }).subscribe((res: any) = > {
        console.log(res)
        reslove(res)
      }, err => {
        // this.handleError(err)
        reject(err)
      })
    })
  }
}
Copy the code

Cordova is used as a plug-in

Ionic offers a wealth of cordova add-ons, which are easy to use, according to the Ionic website.

Download the Cordova plug-in

cordova add plugin plugin-name -D
npm install @ionic-native/plugin-name
Copy the code

Use plugins (imported from @ionic-native/plugin-name)

import { StatusBar } from '@ionic-native/status-bar';
constructor(private statusBar: StatusBar) {
    // Immersion and suspension transparent
    statusBar.overlaysWebView(true);
    // Set the status bar color to black by default, suitable for light background
    statusBar.styleDefault() 
    // Light color status bar is suitable for dark background
    // statusBar.styleLightContent() 
}
Copy the code

To optimize the part

Finished the project, do not optimize the heart strange uncomfortable.

  • AppStartup page experience optimization

Ionic Apps are, after all, a hybrid App that hasn’t cracked the second barrier yet. So at this time we need the startup page to help us improve the user experience, first in config. XML gametes our startup page related configuration

<preference name="ShowSplashScreenSpinner" value="false" /> <! -- Hide the loader when loading -->
<preference name="ScrollEnabled" value="false" /> <! -- Disable startup screen scrolling -->
<preference name="SplashMaintainAspectRatio" value="true" /> <! -- If the value is set to true, the image will not stretch to fit the screen. If set to false, it will be stretched -->
<preference name="FadeSplashScreenDuration" value="1000" /><! Fade duration -->
<preference name="FadeSplashScreen" value="true" /><! -- Fade animation -->
<preference name="SplashShowOnlyFirstTime" value="false" /><! -- Display only the first time -->
<preference name="AutoHideSplashScreen" value="false" /><! -- Auto hide SplashScreen -->
<preference name="SplashScreen" value="screen" />
<platform name="android">
    <allow-intent href="market:*" />
    <icon src="resources/android/icon/icon.png" />
    <splash src="resources/android/splash/screen.png" /><! -- Start page path -->
    <! Below is the compatibility for each resolution -->
    <splash height="800" src="resources/android/splash/screenh.png" width="480" />
    <splash height="1280" src="resources/android/splash/screenm.png" width="720" />
    <splash height="1600" src="resources/android/splash/screenxh.png" width="960" />
    <splash height="1920" src="resources/android/splash/screenxxh.png" width="1280" />
    <splash height="2048" src="resources/android/splash/screenxxxh.png" width="1536" />
</platform>
Copy the code

I turned off the autohide SplashScreen here, because her decision condition is to hide the App once the crash is over, which obviously doesn’t meet our requirements. What we need is for our Ionic WebView app to start and then hide. So we do this in app.component.ts with @ionic-native/splash-screen.

platform.ready().then((a)= > {
      // Delay for 1s to hide the startup screen
      setTimeout((a)= > { 
        splashScreen.hide()
      }, 1000)})Copy the code

That way we can perfectly trick the user and make the experience better.

Packaging optimization

  • Added –prod parameter
    "build:android": "ionic cordova build android --prod --release".Copy the code
    • AOT compilation: Precompile Angular component templates.
    • Production mode: Enable production mode and deploy to the production environment.
    • Baling (Bundle) : String these modules together into a single bundle (bundle).
    • Minimize: Remove unnecessary Spaces, comments, and optional tokens (Token).
    • Obfuscation: Rewrite code with short, meaningless variable and function names.
    • Eliminate dead code: Remove unreferenced modules and unused code.

The App package

I think packaging APK is a bit of a struggle for front-end engineers who don’t know the server and Android. Let’s talk about this part in more detail.

Environment configuration

The first step is to configure each environment

  1. Node installs/configures environment variables (I’m sure you’ve done this already)

  2. JDK installation (no need to configure environment variables)

    The JDK is Java development environment support, you can download here, extract code: 9p74.

    After the download is complete, unzip the file and install it directly as prompted. The global point is confirmed. The final installation path is C: Program Files\Javajdk. I have modified the installation path, if you are not familiar with it or do not modify the installation path. If the following log appears, the installation is successful

  1. SDK installation/configuration of environment variables: This part is the focus and a bit more cumbersome.

To download first.

After decompressing the renamed folder, put it in a parent directory with JDK for easy search: C:\Program Files\SDK

Then configure environment variables, my computer — right click properties — Advanced System Settings — environment variables.

In the ** system variable (s)**, create a new key-value pair as follows:

name: ANDROID_HOME
key: C:\Program Files\SDK
Copy the code

Add global variables to path after creating system variables.

Enter android -h in the console. If the following log is displayed, the SDK is successfully installed

Next, we will use Android Studio for SDK download, Adnroid Studio download address, After studio installation, you need to install Android SDK Tools,Android SDK platform-tools,Android SDK build-tools and SDK platform

  1. gradleInstall/configure environment variables

After the SDK is installed, we can install and configure Gradle.

Download it on the official website or here

Then also installed in the JDK,SDK directory, easy to find. Configure the same environment variables as the SDK:

GRADLE_HOME = C: \ Program Files \ SDK \ gradle - 4.1; %GRADLE_HOME%\binCopy the code

Test command (view version) : Gradle -v If the following log is displayed, the installation is successful

package

The environment preparation work before packaging has been finished, next we proceed to package ‘APK.

  1. The installationcordova
npm i cordova -g
Copy the code
  1. Created in the projectAndroidEngineering,IonicExecute the following command in the project
ionic cordova platform add android
Copy the code

This may be a long process, you have to be patient, after all, the dawn is just around the corner.

  1. To create theAndroidProject after projectplatformThere will be an extra one under the folderandroidFolder. Then execute the package command.
ionic cordova build android
Copy the code

You will then see frantic console output, culminating in the image below showing that you have packed an unsigned installation package

  1. APKThe signatureAPKYou can’t post it without signing it. There are two ways to do this
  • usejdkSignature, not much to say here, if you want to know can readThis article
  • useAndroid StudioType a signature pack.

Create a signature file by selecting Generate Signed APK from the build toolbar above AS

After the generation, you can directly use AS to type the signature package

Click Locate to see our apK package. Now we are ok with Android, and we will fix IOS later.

Simple APP Server update (simple example)

Since Android’s requirements are not as strict as Apple’s, we can also update the application through our own server. The following is to implement a relatively simple update Service

We will use the following Cordova plugins

  • cordova-plugin-file-transfer / @ionic-native/file-transfer: Online file download and storage (officially recommended useXHR2If you are interested, take a look.)
  • cordova-plugin-file-opener2 / @ionic-native/file-opener: Used to open APK files
  • cordova-plugin-app-version / @ionic-native/app-version: Used to obtain the app version number
  • cordova-plugin-file / @ionic-native/file: Operates the file system on the app
  • cordova-plugin-device / @ionic-native/device: Obtains the current device information, which is used to distinguish platforms

After downloading the plug-in, we will implement a crude version of the service, which I will explain in the code comments. It is divided into two parts. One part is the specific update operation update.service.ts, and the other part is the data store data.service.ts data.service.ts

/* * @author: etongfu * @description: Device info * @youwant: add you want info here */
import { Injectable } from '@angular/core';
import { Device } from '@ionic-native/device';
import { File } from '@ionic-native/file';
import { TokenServie } from './token.service';
import { AppVersion } from '@ionic-native/app-version';

@Injectable()
export class DataService {
  / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * APP data module * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
  / / app package name
  private packageName: string = ' ' 
  // App version number
  private appCurrentVersion: string =  The '-'
  // App version code
  private appCurrentVersionCode:number = 0
  // The current program running platform
  private currentSystem: string
  / / the current userId
  // App download resource storage path
  private savePath: string
  // Current APP UUID
  private uuid: string

  / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * general data module * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
  constructor (
    public device: Device,
    public file: File,
    public app: AppVersion,
    public token: TokenServie,
    public http: HttpService
  ) {
    // Only after the device is ready
    document.addEventListener("deviceready", () = > {// The current running platform
      this.currentSystem = this.device.platform
      // console.log(this.device.platform)
      // App version information
      this.app.getVersionNumber().then(data= > {
        // Current APP version data, which stores the version number
        if (this.currentSystem) {
          // console.log(data)
          this.appCurrentVersion = data
        }
      }, error => console.error(error))
      this.app.getVersionCode().then((data) = > {
        // The current app version number
        if (this.currentSystem) {
          this.appCurrentVersionCode = Number(data)
        }
      }, error => console.error(error))
      / / app package name
      this.app.getPackageName().then(data= > {
          // packageName: data of the current application, where the packageName is stored
          if (this.currentSystem) {
            this.packageName = data;
          }
      }, error => console.error(error))
      // console.log(this.currentSystem)
      // Save path in file changes the address according to the platform
      this.savePath = this.currentSystem === 'iOS' ? this.file.documentsDirectory : this.file.externalDataDirectory;

    }, false);
  }
  /** * Get app package name */
  public getPackageName () {
    return this.packageName
  }
  /** * Get the current app version * @param hasV whether to add V identifier */
  public getAppVersion (hasV: boolean = true): string {
    return hasV ? `VThe ${this.appCurrentVersion}` : this.appCurrentVersion
  }
  /** * Obtain the version of nuamber 1.0.0 => 100 */
  public getVersionNumber ():number {
    const temp = this.appCurrentVersion.split('. ').join(' ')
    return Number(temp)
  }
  /** * Get app version code for comparison updates using */
  public getAppCurrentVersionCode (): number{
    return this.appCurrentVersionCode
  }
  /** * get the current operating platform */
  public getCurrentSystem (): string {
    return this.currentSystem
  }
  /**
   * 获取uuid
   */
  public getUuid ():string {
    return this.uuid
  }
  /** * get the storage address */
  public getSavePath ():string {
    return this.savePath
  }
}
Copy the code

update.service.ts

/* * @author: etongfu * @email: [email protected] * @description: APP simple update service * @youwant: add you want info here */
import { HttpService } from '. /.. /providers/http.service';
import { Injectable, Inject } from '@angular/core'
import { AppVersion } from '@ionic-native/app-version';
import { PopSerProvider } from './pop.service';
import { DataService } from './data.service';
import {Config} from '@interface/index'
import { FileTransfer, FileTransferObject } from '@ionic-native/file-transfer';
import { FileOpener } from '@ionic-native/file-opener';
import { LoadingController } from 'ionic-angular';

@Injectable()
export class AppUpdateService {

  constructor( @Inject("CONFIG") public CONFIG:Config, public httpService: HttpService, public appVersion: AppVersion, private fileOpener: FileOpener, private transfer: FileTransfer, private popService: PopSerProvider, / / it is a popup window service private dataService: dataService, private loading: LoadingController) {}* @param currentVersion Current app Version * @param serverVersion Version on the server */
  private hasUpdateByCode (currentVersion: number, serverVersion:number):Boolean {
    return serverVersion > currentVersion
  }
  @param noUpdateShow displays an alert when no update is made */
  public checkForUpdate (noUpdateShow: boolean = true) {
    // Intercept platform
    return new Promise((reslove, reject) = > {
      // http://appupdate.ymhy.net.cn/appupdate/app/findAppInfo?appName=xcz&regionCode=370000
      // Query app updates
      this.httpService.get(this.CONFIG.CHECK_URL, {}, true).then((result: any) = > {
        reslove(result)
        if (result.succeed) {
          const data = result.appUpload
          const popObj = {
            title: 'Version Update'.content: ` `
          }
          console.log('Current APP version:The ${this.dataService.getVersionNumber()}`)
          // There is an update
          if (this.hasUpdateByCode(this.dataService.getVersionNumber(), data.versionCode)) {
          // if (this.hasUpdateByCode(101, data.versionCode)) {
            let title = 'New version <b>V${data.appVersion}</b> available. Do you want to download it now? <h5 class="text-left"> Update log </h5> '
            // Update the log section
            let content = data.releaseNotes
            popObj.content = title + content
            // Generate a popover
            this.popService.confirmDIY(popObj, data.isMust === '1' ? true: false, () = > {this.downLoadAppPackage(data.downloadPath)
            }, ()=> {
              console.log('cancel'); })}else {
            popObj.content = 'This is the latest version! '
            if(! noUpdateShow) {this.popService.confirmDIY(popObj, data.isMust === '1' ? true: false)}}}else {
          // The interface responds to the latest version by default
          if(! noUpdateShow) {this.popService.alert('Version Update'.'This is the latest version! ')
          }
        }
        }).catch((err) = > {
          console.error(err)
          reject(err)
        })
      })
  }
  /** * download new version of App * @param url: string Download address */
  public downloadAndInstall (url: string) {
    let loading = this.loading.create({
      spinner: 'crescent'.content: 'Downloading'
    })
    loading.present()
    try {
      if (this.dataService.getCurrentSystem() === 'iOS') {
        // IOS goes to the corresponding download page
        // window.location.href = 'itms-services://? action=download-manifest&url=' + url;
      } else {
        const fileTransfer: FileTransferObject = this.transfer.create();
        fileTransfer.onProgress(progress= >{
          // Display the download progress
          const present = new Number((progress.loaded / progress.total) * 100);
          const presentInt = present.toFixed(0);
          if (present.toFixed(0) = = ='100') {
            loading.dismiss()
          } else {
            loading.data.content = ` downloaded${presentInt}% `}})const savePath = this.dataService.getSavePath() + 'xcz.apk';
        // console.log(savePath)
        // Download and save
        fileTransfer.download(url,savePath).then((entry) = > {
          //
          this.fileOpener.open(entry.toURL(), "application/vnd.android.package-archive")
          .then((a)= > console.log('Opening APK package successful! '))
          .catch(e= > console.log('Failed to open APK package! ', e))
        }).catch((err) = > {
          console.error(err)
          console.log("Download failed");
          loading.dismiss()
          this.popService.alert('Download failed'.'Download exception')}}}catch (error) {
      this.popService.alert('Download failed'.'Download exception')
      // Dismiss immediately if there are exceptions
      loading.dismiss()
    }
  }
}
Copy the code

App.component.ts can be updated by calling service directly

// Call update
this.appUpdate.checkForUpdate()
Copy the code

App real machine debugging

To be honest, debugging the Hybird is a real pain. At present, the following two debugging methods are popular

  • With Chrome’s powerful capabilities, we can fully display the content of the WebView in the App on the Chrome side. You can control the web pages in our app on the Web side, which is cool first. Here’s how to do it

    1. Open it in Chromechrome://inspect/#devices
    2. Connect the device, be carefulThe fan wall is required for the first connection, otherwise it will appear404And so on
    3. Install the App to be debugged on the connected device, and thenChromeIt will automatically find what you need to debugWebView
    4. Start debugging with pleasure
  • Use VConsole for debugging

    NPM install vconsole library and reference it in app.component.ts

    import VConsole from 'vconsole'
    export class MyApp {
    constructor() {
        platform.ready().then((a)= > {
          console.log(APP_ENV)
          // Debug the program
          APP_ENV === 'debug' && new VConsole()
        })
      }
    }
    Copy the code

    Results the following

Special part of Ionic (pit)

  • Static resource path problem

Note the following if static paths fail to load after the package is completed

<! -- the img tag in HTML refers directly to image processing -->
<img src="./assets/xxx.jpg"/>
<! -- or so -->
<img src="assets/imgs/timeicon.png" style="width: 1rem;">
Copy the code
/* Use the absolute path */ in the SCSS file
.bg{
  background-image: url(".. /assets/xxx.jpg")}Copy the code
  • The Android API version is modifiedThe current default SDK version in Ionic is too high, some of the lower versions of the machine have not been installed and need to be modified as follows
<! -- platforms/android/project.properties -->
target=android-26
<! - and platforms/android/CordovaLib/project properties - >
target=android-26
Copy the code
  • aboutSDKandcordovaPits in plugins (not written yet)

Take cordova-plugin-file-opener2 for example

  • AS3.0After packagingAndroid7.0The following phones cannot be installed

This is not an Ionic pit, but an Android Studio3.0 pit. I did not know that the following options were not checked when packing

I always thought it was a problem with the project code, but I never thought it was a problem with the Settings. After adding the V1 option, I could just type it. I checked the reasons as follows.

The options provided in the figure above are actually signed version options, which were added in AS3.0.

Android 7.0 introduced the APK Signature Scheme v2, v1 jar Signature from JDK V1: this should be verified by the ZIP entry, so that APK can make many changes after signing – you can move or even recompress the file. V2: Validates all bytes of the compressed file, not a single ZIP entry, and therefore cannot be changed after signing (including zipalign). Because of this, we now combine compression, adjustment, and signing in one step during compilation. The benefits are obvious: more security and the new signatures can speed up application installation by reducing the time it takes to validate on the device (no time-consuming decompression and validation).

If V1 is not selected, the installation will be displayed as not installed if the installation is less than 7.0. If the installation is more than 7.0, V2 authentication is used. If V1 is checked, the more secure and fast authentication method will not be used after 7.0.

It can also be configured in build.gradle in the app directory

signingConfigs {
    debug {
        v1SigningEnabled true
        v2SigningEnabled true
    }
    release {
        v1SigningEnabled true
        v2SigningEnabled true}}Copy the code

conclusion

Such a toss down, more to many pits. But it all worked out. The biggest takeaway with the Ionic is that TS+Angular’s modular development model is comfortable. The development speed is not too slow, and I think people who are interested in Angular can give it a try.

Spring Festival is coming soon, wish all developers happy Spring Festival away from bugs ~😁😁😁

Sample code please wait

Original address if feel useful words to ⭐ bar