Anatomy of an Ionic 2 app

0 before starting

Before going through this tutorial, you should know at least some basic Ionic 2 concepts. You must also have Ionic 2 installed on your machine.

1 Create a new Ionic 2 app

We will use the tutorial template created by the Ionic team , which can be found in the official tutorial, to create our application. To do this, you need to run the following commands:

ionic start ionic2-tutorial tutorial --v2

Now your application will start building itself. To run subsequent commands, you should make the project directory the current working directory:

cd ionic2-tutorial

To briefly glance at the applied effect, use the serve command:

ionic serve

As mentioned above, these commands should be executed in the current project directory.

2 Directory structure

If you look at the generated files and folders, it all looks very similar to an Ionic 1 original app. This is also a very typical Cordova -style project structure.

If you look again in the src folder, things start to look a little different:



 
Directory structure - src

Usually in an Ionic 1 app, people have all their Javascript files (controllers, services, etc.) in one folder, all their templates in another folder, and then all their styles are included in an app.scss file.

The default structure of an Ionic 2 application is organized by functionality, so that all logic, templates, and styles for a particular component (in the example above we have a basic page component, a component list, and an item details component) are kept together . This is a perfect application of the Angular 2 methodology, everything is independent components that can be easily reused elsewhere or in projects. The old Ionic 1 approach can become very cumbersome if you want to reuse a specific function, or have a lot of people working on the same project.

The idea of ​​organizing code according to features is not the prerogative of Angular 2 & Ionic 2, in fact people use and advocate the feature based approach in Ionic 1, it's just that most people don't (the trend is hard to break). With the way Angular 2 works, feature-based structure is used by default, so it's not difficult to implement this structure.

index.html

It's a convention that the first file a browser opens is index.html . So let's take a look at what it looks like in Ionic 2:

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="UTF-8">
  <title>Ionic App</title>
  <meta name="viewport" content="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/x-icon" href="assets/icon/favicon.ico">
  <link rel="manifest" href="assets/manifest.json">
  <meta name="theme-color" content="#4e8ef7">

  <!-- un-comment this code to enable service worker
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('service-worker.js')
        .then(() => console.log('service worker installed'))
        .catch(err => console.log('Error', err));
    }
  </script>-->

  <link href="build/main.css" rel="stylesheet">

</head>
<body>

  <!-- Ionic's root component and where the app will load -->
  <ion-app></ion-app>

  <!-- cordova.js required for cordova apps -->
  <script src="cordova.js"></script>

  <!-- The polyfills js is generated during the build process -->
  <script src="build/polyfills.js"></script>

  <!-- The bundle js is generated during the build process -->
  <script src="build/main.js"></script>

</body>
</html>

This looks pretty neat, no different from the Ionic 1. The big difference here is that instead of appending ng-app to the body tag (the purpose is to let Ionic know where the app exists), I use:

<ion-app></ion-app>

The root component will be created here, usually your entry point application is injected here. The reference of cordova.js allows us to use Cordova to create applications (package the application as a native application, which can be submitted to the App Store), polyfill.js is a basic patch for certain features of the browser, and main.js is our application binding code.
Basically, this looks like a very normal web page.

assets

The assets directory is used to store static files used in your project, such as images, JSON data files, etc. Anything in this folder will be overwritten to your build directory every time the application is built.

theme

The theme directory contains the global.scss and variables.scss files for your application . Most styling in your application is done by using each component's own .scss file, but you can define any custom styles using the global.scss file. In a different way, you can also modify SASS variables in the variables.scss file to Modify the styles you apply.

app

All Ionic 2 Apps have a root component . This is not a difference from other components in your app, an obvious difference is that it is in its own app folder and is named app.component.ts .
If you're a little unsure of what a component is, let's take a look:

@Component({
    templateUrl: 'my-component.html'
})
export class Something {
  // ...snip
}

This is a component with something functional, technically a component is associated with a view, otherwise the class might be better considered services . Whether it is a component or a servece , the creation is similar, and it can be imported into your application.

The root component root component is the first to be loaded, let's see how the root component is defined and works. Let's take a look at the entire file, and then break it down:

import { Component, ViewChild } from '@angular/core';
import { Platform, MenuController, Nav } from 'ionic-angular';
import { StatusBar } from 'ionic-native';
import { HelloIonicPage } from '../pages/hello-ionic/hello-ionic';
import { ListPage } from '../pages/list/list';

@Component({
  templateUrl: 'app.html'
})
export class MyApp {
  @ViewChild(Nav) nav: Nav;

  // make HelloIonicPage the root (or first) page
  rootPage: any = HelloIonicPage;
  pages: Array<{title: string, component: any}>;

  constructor(
    public platform: Platform,
    public menu: MenuController
  ) {
    this.initializeApp();

    // set our app's pages
    this.pages = [
      { title: 'Hello Ionic', component: HelloIonicPage },
      { title: 'My First List', component: ListPage }
    ];
  }

  initializeApp() {
    this.platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      StatusBar.styleDefault();
    });
  }

  openPage(page) {
    // close the menu when clicking a link from the menu
    this.menu.close();
    // navigate to the new page if it is not the current page
    this.nav.setRoot(page.component);
  }
}

1.imports

import { Component, ViewChild } from '@angular/core';
import { Platform, MenuController, Nav } from 'ionic-angular';
import { StatusBar } from 'ionic-native';
import { HelloIonicPage } from '../pages/hello-ionic/hello-ionic';
import { ListPage } from '../pages/list/list';

Initially, there are some imports defined. We use to load other components or services into this component. For all components, except the root component, you can see the class definition like this:

export class Something {

}

Very simple, our expoet component is to be able to import in other places . In this example, we imported the Platform , Nav and MenuController services from the Ionic library. Platform provides information about the platform on which the application is running, Nav provides references to navigation within the application, and MenuController allows us to provide control menus.

We import Component and ViewChild from Angular 2 . Component is almost everywhere because we are used to create the component and ViewChild is used to get the definition of the element in the component.

We also imported the HelloIonicPage and ListPage components we created ourselves . These components are defined in src/pages/hello-ionic/hello-ionic.ts and src/pages/list/list.ts (according to the path corresponding to the import statement). Note that we did not include the src path in the import because it is relative to the current file, and we are already in the src directory. Since we are in a subfolder called app , we use ../ to go to the parent directory.

Next we see importing StatusBar from ionic-native , because we use Cordova through Ionic2 to access native functionality like controlling the status bar. Ionic Native is a service provided by Ionic to facilitate the use of Cordova plugins. Although you don't have to include the Native functionatilty in order to use Ionic Native, you can use the Cordova plugin directly.

2. Decorator

Decorators, like @Component and @Directive , add metadata (augmentation information) to our components by using the class definition, look at the root component I bought :

@Component({
  templateUrl: 'app.html'
})

Here we use templateUrl to let the component know which file to use as the view (you can also use template as an inline template instead of templateUrl ).

3. Class definition

All of the previous ones didn't really do some functionality, just a setup and build. Now we're going to start defining some behaviors, let's take a look:

export class MyApp {
  @ViewChild(Nav) nav: Nav;

  // make HelloIonicPage the root (or first) page
  rootPage: any = HelloIonicPage;
  pages: Array<{title: string, component: any}>;

  constructor(public platform: Platform, public menu: MenuController) {
    this.initializeApp();

    // set our app's pages
    this.pages = [
      { title: 'Hello Ionic', component: HelloIonicPage },
      { title: 'My First List', component: ListPage }
    ];
  }

  initializeApp() {
    this.platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      StatusBar.styleDefault();
    });
  }

  openPage(page) {
    // close the menu when clicking a link from the menu
    this.menu.close();
    // navigate to the new page if it is not the current page
    this.nav.setRoot(page.component);
  }
}

First we define a new class MyApp , classes is a new feature of ES6.

We pass in some parameters to the constructor : platform and menu then their types are Platform and MenuController . In this way, we inject these services through the constructor (for example, the MenuController will be used as a menu), and by using the public keyword, the scope is made in the entire class; it means that we can access it anywhere in this class through this.menu or this.platform they.

The Platform service provides information about the platform the program runs on (for example: width and height, horizontal and vertical, resolution, etc.), here we use it to determine whether the app is ready.

The MenuController service allows us to create and manage a sliding menu.

Above the constructor, we also define several member variables to hold the rootPage and pages in our class. By defining above the constructor, we can use this.rootPage or this.pages in the whole class.

We define the rootPage as the HelloIonicPage component, as the first page to display first (you can also simply change this to use ListPage instead).

Outside the constructor, we define a method called openPage , pass in a page parameter, and set it to the current page by calling the setRoot method. Note that we get this.nav reference in a strange way. Normally, we import NavController in the same way as MenuController and Platform and then call its setRoot, but you can't call it from the root component, instead we get a reference to @ViewChild provided by Angular2.

One thing that is especially confusing for beginners is this:

rootPage: any = HelloIonicPage;
pages: Array<{title: string, component: any}>;

As mentioned before, here is the creation of member variables. But you might wonder what the hell Array<{title: string, component: any}> is. You should know that Ionic 2 uses TypeScript and these ghosts are types. Type simply means "these variables should only contain data of these types". Here, we can say that rootPage can contain any type of data, pages can only contain arrays, and these arrays can only contain objects consisting of string title and any type of component. This is a very complex type, and you can simply handle it like this:

rootPage: any = HelloIonicPage;
pages: any;

Or you can use no types at all. The benefit of using types is that it adds error checking and a basic level of testing to your application - if your pages array is passed a number, your application will break, and this will be intuitive to understand and deal with.

Root Components template

When we create the root component we provide a template to the component, which is the content to be rendered to the screen. 1). Here is what our root component looks like at browser runtime:



 

Now let's look at the template HTML in a little more detail.

<ion-menu [content]="content">

  <ion-header>
    <ion-toolbar>
      <ion-title>Pages</ion-title>
    </ion-toolbar>
  </ion-header>

  <ion-content>
    <ion-list>
      <button ion-item *ngFor="let p of pages" (click)="openPage(p)">
        {{p.title}}
      </button>
    </ion-list>
  </ion-content>

</ion-menu>

<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>

Look at the first line first:

<ion-menu [content]="content">

This is the content attribute of the menu element content . Remember that "content" here is an expression and not a string. Instead of setting the content property to the string "content", we are setting the variable "content". If you jump to the bottom of the file you will see:

<ion-nav id="nav" [root]="rootPage" #content swipe-back-enabled="false"></ion-nav>

By adding #content in the above code , we create a variable named content that points to this component, which is also the variable used by the content property of menu. So, the menu will use <ion-nav> as its main content. Here we set the root property to the rootPage we defined in the class ( app.ts ) .

Next let's see what's interesting:

<button ion-item *ngFor="let p of pages" (click)="openPage(p)">
  {{p.title}}
</button>

Angular 2 syntax is squeezed into this little piece of code. Create a button for each page defined in the constructor, the number syntax means that it will create an embedded template for each page (it does not render the above code in the DOM, but uses the template to create it), by using * let p We can get a reference to a specific page, which is passed to the openPage method (defined in the root module) when the click event occurs. Going back and looking at the openPage method can see that this parameter is used to set the rootPage**:

this.nav.setRoot(page.component);

App Module

We've covered some root module details, but there's also a mysterious file called app.modules.ts in the app directory.

In order to use pages and services in our program, we need to add them to the app.module.ts file. All pages we create need to be added to the declarations and entryComponents arrays, all services need to be added to the providers array, and all custom components or pipes just need to be added to the declarations array.

You will also find the main.dev.ts and main.prod.ts files in the same directory. Only one of them will be used (depending on whether you are developing or releasing the build). Actually it's responsible for launching your application (it's kind of like index.html in that sense). It will import the app module and start the application.

page

The root component is a special case, we use the ListPage component to see how to add a normal view to an Ionic2 application. You can see this page by selecting the "My First List" menu in the application to view this page:




 

The code is sauce purple:

import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { ItemDetailsPage } from '../item-details/item-details';

@Component({
  templateUrl: 'list.html'
})
export class ListPage {
  selectedItem: any;
  icons: string[];
  items: Array<{title: string, note: string, icon: string}>;

  constructor(public navCtrl: NavController, public navParams: NavParams) {
    // If we navigated to this page, we will have an item available as a nav param
    this.selectedItem = navParams.get('item');

    this.icons = ['flask', 'wifi', 'beer', 'football', 'basketball', 'paper-plane',
    'american-football', 'boat', 'bluetooth', 'build'];

    this.items = [];
    for(let i = 1; i < 11; i++) {
      this.items.push({
        title: 'Item ' + i,
        note: 'This is item #' + i,
        icon: this.icons[Math.floor(Math.random() * this.icons.length)]
      });
    }
  }

  itemTapped(event, item) {
    this.navCtrl.push(ItemDetailsPage, {
      item: item
    });
  }
}

Like the root component, we have some import statements, and then we also have the @Component modifier:

@Component({
  templateUrl: 'list.html'
})

Then something like this:

export class ListPage {

}

There is a prefix export here but not in the root component. This allows our page components to be imported ( import ) elsewhere .
In this view, a component called NavParams is added through the constructor. We can return the details of this view when navigating, let's check the value first:

this.selectedItem = navParams.get('item');

At this time it is undefined , because this page is set as rootPage (set by the openPage method in the root component), we do not need to navigate to this page through the navigation stack.

In Ionic 2, if you want to add a view, and save the page navigation history to return at any time, then you need to push the page to n
navigation stack, and use pop to remove it .

In the ListPage component, we push the ItemDetailsPage through the itemTapped method ( in the ListPage template, but triggered when a record is clicked) :

itemTapped(event, item) {
  this.navCtrl.push(ItemDetailsPage, {
    item: item
  });
}

The above pushes the ItemDetailsPage component to the navigation stack, making it the current active view, and then passes the clicked item to the details page.

Now we look at the details of the details page ItemDetailsPage component, we can use NavParams to get the details of the incoming record, like this (after all we pushed something to the navigation stack):

this.selectedItem = navParams.get('item');
console.log(this.selectedItem);

This is the basic mode of Ionic2 master-slave composite.
Also keep in mind that you can easily create pages from the command line:

ionic g page MyPage

This will automatically create the page file you need.

Summarize

There is no doubt that Ionic 2 and Angular 2 have made huge strides in organization and performance, but they also look scary. Although it initially seemed like a lot of learning and confrontation, I thought it made a lot of sense.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326399277&siteId=291194637
Recommended