Error googlemaps when try add platform iOS

My project run fine on browser, but when I try add platform iOS, I get this error (code below)

My project run fine on browser, but when I try add platform iOS, I get this error (code below)

Apparently the problem is “cordova-plugin-googlemaps”

I have removed the platform. Then added CocoaPods ($ “sudo gem install cocoapods”), then add:

export LANG=en_US.UTF-8

but I still getting the error.

What is wrong? Can you help me?

Thanks.

--[cordova-plugin-googlemaps]------------------------
From version 2.4.5, the cordova-plugin-googlemaps uses CocoaPod.
No longer necessary com.googlemaps.ios plugin.
Automatic uninstalling com.googlemaps.ios plugin...

Failed to install 'cordova-plugin-googlemaps': undefined
CordovaError: Promise rejected with non-error: '/Library/Ruby/Gems/2.3.0/gems/claide-1.0.2/lib/claide/command.rb:439:in help!\':\u001b[31m[!] You cannot run CocoaPods as root.\u001b[39m (CLAide::Help)\n\n\u001b[4mUsage:\u001b[24m\n\n $ \u001b[32mpod\u001b[39m \u001b[32mCOMMAND\u001b[39m\n\n CocoaPods, the Cocoa library package manager.\n\n\u001b[4mCommands:\u001b[24m\n\n \u001b[32m+ cache\u001b[39m Manipulate the CocoaPods cache\n \u001b[32m+ env\u001b[39m Display pod environment\n \u001b[32m+ init\u001b[39m Generate a Podfile for the current directory\n \u001b[32m+ install\u001b[39m Install projectdependencies according to versions from a\n Podfile.lock\n \u001b[32m+ ipc\u001b[39m Inter-process communication\n \u001b[32m+ lib\u001b[39m Develop pods\n \u001b[32m+ list\u001b[39m List pods\n \u001b[32m+ outdated\u001b[39m Show outdated project dependencies\n \u001b[32m+ repo\u001b[39m Manage spec-repositories\n \u001b[32m+ setup\u001b[39m Setup the CocoaPods environment\n \u001b[32m+ spec\u001b[39m Manage pod specs\n \u001b[32m+ update\u001b[39m Update outdated project dependencies and create new Podfile.lock\n\n\u001b[4mOptions:\u001b[24m\n\n \u001b[34m--silent\u001b[39m Show nothing\n \u001b[34m--version\u001b[39m Show the version of the tool\n \u001b[34m--verbose\u001b[39m Show more debugging information\n \u001b[34m--no-ansi\u001b[39m Show output without ANSI codes\n \u001b[34m--help\u001b[39m Show help banner of specified command\n\tfrom /Library/Ruby/Gems/2.3.0/gems/cocoapods-1.5.3/lib/cocoapods/command.rb:47:in run'\n\tfrom /Library/Ruby/Gems/2.3.0/gems/cocoapods-1.5.3/bin/pod:55:in &lt;top (required)&gt;\'\n\tfrom /usr/local/bin/pod:22:in load'\n\tfrom /usr/local/bin/pod:22:in `<main>'\n'
at cli.catch.err (/usr/local/lib/node_modules/cordova/bin/cordova:30:15)
at process._tickCallback (internal/process/next_tick.js:68:7)
[ERROR] An error occurred while running subprocess cordova.

    cordova platform add ios --save exited with exit code 1.

    Re-running this command with the --verbose flag may provide more information.


React Apps with the Google Maps API and google-maps-react

React Apps with the Google Maps API and google-maps-react

This tutorial aims at integrating the google maps API to your React components and enabling you to display maps on your website.

This tutorial aims at integrating the google maps API to your React components and enabling you to display maps on your website.

Introduction to Google Maps API.

At some point of our lives, we’ve all gotten a chance to use and interact with the google maps, either through finding directions, viewing our current location, ordering cabs or estimating distance and time from one point to another.

Table of Contents

Including the Google maps API in a React App is actually far much easier than expected due to its rich documentation which I highly suggest that you look into, and the npm package by Fullstack React.

Prerequisite:

Go on ahead and grab your API key here.

  1. Simply click on the Get Started button
  2. Tick on the maps checkbox
  3. Click Create A New Project
  4. Once you’ve named your project (whatever you want) you can set up your billing info which will automatically enable your the API and generate the API key.

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.

Project Setup

For some quick setup, we are going to use facebook’s create-react-app which saves us the hassle of having to configure webpack or babel.

So go on ahead and run this command

npm i -g create-react-app
create-react-app my-googlemap
cd my-googlemap

Before we add any code, let’s go ahead and install our dependency.

npm install --save google-maps-react

Let’s go on and edit our src folder and remove files and imports that we do not need ie

  1. Simply click on the Get Started button
  2. Tick on the maps checkbox
  3. Click Create A New Project
  4. Once you’ve named your project (whatever you want) you can set up your billing info which will automatically enable your the API and generate the API key.

Time to create our component.

We will need to edit our App.js file and instead have our component that will load our google map.

import React, { Component } from 'react';
import { Map, GoogleApiWrapper } from 'google-maps-react';

const mapStyles = {
  width: '100%',
  height: '100%'
};

export class MapContainer extends Component {
  render() {
    return (
      <Map
        google={this.props.google}
        zoom={14}
        style={mapStyles}
        initialCenter={{
         lat: -1.2884,
         lng: 36.8233
        }}
      />
    );
  }
}

export default GoogleApiWrapper({
  apiKey: 'YOUR_GOOGLE_API_KEY_GOES_HERE'
})(MapContainer);

For a simple Google Map, this is literally all the code you need. Go ahead and run your app and ensure that the map loads to the browser.

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.
The GoogleApiWrapper is simply a Higher Order Component(HOC) that provides wrapper around Google APIs. Alternatively, the GoogleApiWrapper HOC can be configured by passing a function that will be called with the wrapped component’s props and should return the configuration object like so.

export default GoogleApiWrapper(
  (props) => ({
    apiKey: props.apiKey
  }
))(MapContainer)

Markers and infoWindow

Wouldn’t it be exciting to have a Marker and an infoWindow on our map showing our set initialCenter position? So let’s add this functionality to our code.

First we need to import Marker and infoWindow components from the google-maps-react library inorder to help us achieve loading of the two.

import { GoogleApiWrapper, InfoWindow, Marker } from 'google-maps-react';

Notice that our component before was stateless?, We need to add state for state management.

[...]

export class MapContainer extends Component {
   state = {
    showingInfoWindow: false,  //Hides or the shows the infoWindow
    activeMarker: {},          //Shows the active marker upon click
    selectedPlace: {}          //Shows the infoWindow to the selected place upon a marker
  };

Next, we need to add event handlers for when the map and the marker are clicked.

  onMarkerClick = (props, marker, e) =>
    this.setState({
      selectedPlace: props,
      activeMarker: marker,
      showingInfoWindow: true
    });

  onClose = props => {
    if (this.state.showingInfoWindow) {
      this.setState({
        showingInfoWindow: false,
        activeMarker: null
      });
    }
  };

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.* Prerequisite:

Let’s complete our component by adding our Marker and InfoWindow components to our render method

  render() {
    return (
      <Map
        google={this.props.google}
        zoom={14}
        style={style}
        initialCenter={{ lat: -1.2884, lng: 36.8233 }
      >
        <Marker
          onClick={this.onMarkerClick}
          name={'Kenyatta International Convention Centre'}
        />
        <InfoWindow
          marker={this.state.activeMarker}
          visible={this.state.showingInfoWindow}
          onClose={this.onClose}
        >
          <div>
            <h4>{this.state.selectedPlace.name}</h4>
          </div>
        </InfoWindow>
      </Map>
    );
  }
}

[...]

Run your app and ensure you have the one marker with the infoWindow upon click.

Browser’s current location

Let’s spice things up by having our map pick our browser’s current location. We will be using navigator which is a read-only property that returns a Geolocation object that gives Web content access to the location of the device.

In our src folder create a new file and name it Map.js and create a component named CurrentLocation this is where all our functionality to pick our browser’s location will lie.

We will begin by adding some default props to our CurrentLocation component, since we will need to set the map with a center incase the current location is not provided.This is handled by the boolean prop centerAroundCurrentLocation

import React from 'react';
import ReactDOM from 'react-dom';

const mapStyles = {
  map: {
    position: 'absolute',
    width: '100%',
    height: '100%'
  }
};

export class CurrentLocation extends React.Component {

[...]

}
export default CurrentLocation;

CurrentLocation.defaultProps = {
  zoom: 14,
  initialCenter: {
    lat: -1.2884,
    lng: 36.8233
  },
  centerAroundCurrentLocation: false,
  visible: true
};

Next, we need to make our component stateful,

[...]

export class CurrentLocation extends React.Component {
  constructor(props) {
    super(props);

    const { lat, lng } = this.props.initialCenter;
    this.state = {
      currentLocation: {
        lat: lat,
        lng: lng
      }
    };
  }

[...]

}

Let’s also update our CurrentLocation component to cater for the instance when the Map is first loaded as we cannot solely depend upon the google API being always available, hence we need to check if it’s loaded. And also check if the browser’s current location is provided and recenter the map to it.

[...]

componentDidUpdate(prevProps, prevState) {
    if (prevProps.google !== this.props.google) {
      this.loadMap();
    }
    if (prevState.currentLocation !== this.state.currentLocation) {
      this.recenterMap();
    }
  }

[...]

Let’s define the recenterMap() function which only gets called when the currentLocation in the component’s state is updated and uses the .panTo() method on the google.maps.Map instance to change the center of the map.

[...]

 recenterMap() {
    const map = this.map;
    const current = this.state.currentLocation;

    const google = this.props.google;
    const maps = google.maps;

    if (map) {
      let center = new maps.LatLng(current.lat, current.lng);
      map.panTo(center);
    }
  }

[...]

Next, we need to handle the instance when the map has already loaded.This will be handled by the componentDidMount() Lifecycle method which will set a call back to fetch the current location.

[...]

 componentDidMount() {
    if (this.props.centerAroundCurrentLocation) {
      if (navigator && navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(pos => {
          const coords = pos.coords;
          this.setState({
            currentLocation: {
              lat: coords.latitude,
              lng: coords.longitude
            }
          });
        });
      }
    }
    this.loadMap();
  }

[...]

Notice the loadMap() function? Let’s go on ahead and define it.

[...]

loadMap() {
    if (this.props && this.props.google) {
      // checks if google is available
      const { google } = this.props;
      const maps = google.maps;

      const mapRef = this.refs.map;

      // reference to the actual DOM element
      const node = ReactDOM.findDOMNode(mapRef);

      let { zoom } = this.props;
      const { lat, lng } = this.state.currentLocation;
      const center = new maps.LatLng(lat, lng);
      const mapConfig = Object.assign(
        {},
        {
          center: center,
          zoom: zoom
        }
      );

      // maps.Map() is constructor that instantiates the map
      this.map = new maps.Map(node, mapConfig);
    }
  }

Basically, the loadMap() function is only called after the component has been rendered and grabs a reference to the DOM component to where we want our map to be placed.

Our CurrentLocation component is almost looking up, But we need to ensure that our previous Marker picks our currenct location ie the browsers current location and so we need to introduce Parent-Child concept through the renderChildren() method which will be responsible for actually calling the method on the child component.

Read more about Parent-Child communication here

[...]

 renderChildren() {
    const { children } = this.props;

    if (!children) return;

    return React.Children.map(children, c => {
      if (!c) return;
      return React.cloneElement(c, {
        map: this.map,
        google: this.props.google,
        mapCenter: this.state.currentLocation
      });
    });
  }

[...]

And finally, let’s add our render() method

[...]

render() {
     const style = Object.assign({}, mapStyles.map);
    return (
      <div>
        <div style={style} ref="map">
          Loading map...
        </div>
        {this.renderChildren()}
      </div>
    );
  }

[...]

Lastly, before we wind up we need to update our MapContainer component to include our new changes. So let’s change it to this

import React, { Component } from 'react';
import { GoogleApiWrapper, InfoWindow, Marker } from 'google-maps-react';

import CurrentLocation from './Map';

export class MapContainer extends Component {
  state = {
    showingInfoWindow: false,
    activeMarker: {},
    selectedPlace: {}
  };

  onMarkerClick = (props, marker, e) =>
    this.setState({
      selectedPlace: props,
      activeMarker: marker,
      showingInfoWindow: true
    });

  onClose = props => {
    if (this.state.showingInfoWindow) {
      this.setState({
        showingInfoWindow: false,
        activeMarker: null
      });
    }
  };

  render() {
    return (
      <CurrentLocation
        centerAroundCurrentLocation
        google={this.props.google}
      >
        <Marker onClick={this.onMarkerClick} name={'current location'} />
        <InfoWindow
          marker={this.state.activeMarker}
          visible={this.state.showingInfoWindow}
          onClose={this.onClose}
        >
          <div>
            <h4>{this.state.selectedPlace.name}</h4>
          </div>
        </InfoWindow>
      </CurrentLocation>
    );
  }
}

export default GoogleApiWrapper({
  apiKey: 'YOUR_GOOGLE_API_KEY_GOES_HERE'
})(MapContainer);

Run your app; heading over to our browser, our map should first load with our initialCenter then reload to pick our browser’s current location with the marker positioned to this location and Voilà we are done.

Conclusion

In this article we’ve been able to load our google maps React component, add a marker and have an infoWindow onto it and also have the map pick our current location. While we did not dive into adding more functionalities to our map such as, having polylines and polygons or adding event listeners, I highly recommend that you look into them

Learn More

Full Stack Developers: Everything You Need to Know

React Hooks Tutorial for Beginners: Getting Started With React Hooks

Learn React.js for Beginners

Learn React - React Crash Course 2019 - React Tutorial with Examples

React Router: Add the Power of Navigation

React - The Complete Guide (incl Hooks, React Router, Redux)

Modern React with Redux [2019 Update]

React Native - The Practical Guide

Ionic 4, Angular 7 and Cordova Crop and Upload Image

Ionic 4, Angular 7 and Cordova Crop and Upload Image

The comprehensive step by step tutorial on crop, and upload Image using Ionic 4, Angular 7 and Cordova

The comprehensive step by step tutorial on crop, and upload Image using Ionic 4, Angular 7 and Cordova. We will use Native Ionic Cordova Crop, File Transfer plugins and it’s dependencies. In this tutorial, we will use our existing uploader API that you can find on our GitHub which it uses Node.js, Express.js, and Multer. Or, you can use your own backend or API that using HTML Form Encoding multipart/form-data. The scenario is in the Ionic 4 App, click the Camera button inside the preview image then it will open image picker. After image picked then it will go to cropping popup that you can crop anyway you like then upload to the API. After the successful upload, the Ionic 4 app will preview the Image by URL that saved to the API server.

Table of Contents:

The following tools, frameworks, and modules are required for this tutorial:

Before going to the main steps, we assume that you have to install Node.js. Next, upgrade or install new Ionic 4 CLI by open the terminal or Node command line then type this command.

sudo npm install -g ionic

You will get the latest Ionic CLI in your terminal or command line. Check the version by type this command.

ionic --version
4.12.0

1. Create a New Ionic 4, Angular 7 and Cordova App

To create a new Ionic 4 App, type this command in your terminal.

ionic start ionic4-crop blank --type=angular

If you see this question, just type N for because we will installing or adding Cordova later.

Install the free Ionic Appflow SDK and connect your app? (Y/n) N

Next, go to the newly created app folder.

cd ./ionic4-crop

As usual, run the Ionic 4 App for the first time, but before run as lab mode, type this command to install @ionic/lab.

npm install --save-dev @ionic/lab
ionic serve -l

Now, open the browser and you will the Ionic 4 App with the iOS, Android, or Windows view. If you see a normal Ionic 4 blank application, that’s mean you ready to go to the next steps.

2. Install and Configure Image Crop, File Transfer Plugins, and Dependencies

We will install all required plugins for this tutorial. First, we have to install Native Cordova plugins and Ionic 4 Angular 7 Modules by running these commands.

ionic cordova plugin add cordova-plugin-crop
npm install @ionic-native/crop
ionic cordova plugin add cordova-plugin-camera
npm install @ionic-native/camera
ionic cordova plugin add cordova-plugin-file-transfer
npm install @ionic-native/file-transfer
ionic cordova plugin add cordova-plugin-file
npm install @ionic-native/file

Next, open and edit src/app/app.module.ts then add these imports.

import { ImagePicker } from '@ionic-native/image-picker/ngx';

Add that import to @NgModule Providers.

providers: [
&nbsp; StatusBar,
&nbsp; SplashScreen,
&nbsp; { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
&nbsp; ImagePicker
],

3. Implementing Image Crop and File Upload/Transfer

We will be using the existing Home component or page to implementing Image Preview, Picker, Crop and Upload. For that, open and edit src/app/home/home.page.html then replace all HTML tags with these.

<ion-header>
&nbsp; <ion-toolbar>
&nbsp; &nbsp; <ion-title>
&nbsp; &nbsp; &nbsp; Ionic 4 Crop Upload
&nbsp; &nbsp; </ion-title>
&nbsp; </ion-toolbar>
</ion-header>

<ion-content padding>
&nbsp; <ion-card>
&nbsp; &nbsp; <img *ngIf="!fileUrl" src="assets/no-image.jpeg"/>
&nbsp; &nbsp; <img *ngIf="fileUrl" src="{{fileUrl}}"/>
&nbsp; &nbsp; <ion-card-content>
&nbsp; &nbsp; &nbsp; <ion-button color="medium" size="large" (click)="cropUpload()">
&nbsp; &nbsp; &nbsp; &nbsp; <ion-icon slot="icon-only" name="camera"></ion-icon>
&nbsp; &nbsp; &nbsp; </ion-button>
&nbsp; &nbsp; </ion-card-content>
&nbsp; </ion-card>
</ion-content>

Next, open and edit src/app/home/home.page.ts then add these imports.

import { Crop } from '@ionic-native/crop/ngx';
import { ImagePicker } from '@ionic-native/image-picker/ngx';
import { FileTransfer, FileUploadOptions, FileTransferObject } from '@ionic-native/file-transfer/ngx';

Inject those imports to the constructor.

constructor(private imagePicker: ImagePicker,
&nbsp; private crop: Crop,
&nbsp; private transfer: FileTransfer) { }

Add the variables for hold image URL and response data.

fileUrl: any = null;
respData: any;

Create a function for crop and upload an image file to the API server.

cropUpload() {
&nbsp; this.imagePicker.getPictures({ maximumImagesCount: 1, outputType: 0 }).then((results) => {
&nbsp; &nbsp; for (let i = 0; i < results.length; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; console.log('Image URI: ' + results[i]);
&nbsp; &nbsp; &nbsp; &nbsp; this.crop.crop(results[i], { quality: 100 })
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newImage => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log('new image path is: ' + newImage);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const fileTransfer: FileTransferObject = this.transfer.create();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const uploadOpts: FileUploadOptions = {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileKey: 'file',
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileName: newImage.substr(newImage.lastIndexOf('/') + 1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fileTransfer.upload(newImage, 'http://192.168.0.7:3000/api/upload', uploadOpts)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.then((data) => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(data);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.respData = JSON.parse(data.response);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(this.respData);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.fileUrl = this.respData.fileUrl;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}, (err) => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(err);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;});
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; error => console.error('Error cropping image', error)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );
&nbsp; &nbsp; }
&nbsp; }, (err) => { console.log(err); });
}

As you can see, we use the IP address to access Express.js API from the device. The uploaded image file accessible from the device through [http://192.168.0.7:3000/images/filename](http://192.168.0.7:3000/images/filename "http://192.168.0.7:3000/images/filename") URL.

4. Run and Test Ionic 4, Angular 7 and Cordova App on iOS/Android Devices

We assume that you have cloned the Node.js, Express.js and Multer image uploader here https://github.com/didinj/node-express-image-uploader.git. Next, open a new Terminal or cmd-tab then go to the cloned Express image uploader.

npm install
nodemon

Next, to run on Android devices type this command while the device connected.

ionic cordova platform add android
ionic cordova run android

To run on iOS simulator or device, we have to build it first.

ionic cordova platform add ios
ionic cordova build ios

Then open and run the iOS app from the XCode. You will this view from your Android device or iOS simulator.

That it’s, the Ionic 4, Angular 7 and Cordova Crop and Upload Image tutorial. You can get the full source code from out GitHub.

How to Create Mobile Apps using Ionic 4, Angular 8 and Cordova?

How to Create Mobile Apps using Ionic 4, Angular 8 and Cordova?

In this Ionic 4 tutorial, we will show you how to create mobile apps quickly using Ionic 4, Angular 8, and Cordova

In this Ionic 4 tutorial, we will show you how to create mobile apps quickly using Ionic 4, Angular 8, and Cordova. In other words, this tutorial is the starting point to learn Ionic Framework, Angular, and Cordova. What you will get is not just create and running Ionic-Angular apps, but a little bit meaningful by displaying data in the Ionic list or grid, details, and Google maps. For more details about accessing data from REST API, Firebase, or Google Maps related, we have written the tutorials for you.

Table of Contents:

The following tools, frameworks, and modules are required for this tutorial:

  1. Node.js (Choose the recommended version)
  2. Ionic 4 with the type of Angular
  3. Cordova
  4. Terminal or Node Command Line
  5. IDE or Text Editor (We are using Visual Studio Code)
Step #1. Preparation

The first step of this tutorial prepares the environment to create the Ionic 4 apps. We will use the Mac OS of the Windows environment (choose one of them) and download the Node.js (recommended version). Install the downloaded Node.js to your Mac or Windows machine then check the version of Node.js by typing this command in the terminal or command line.

node -v
v10.15.1
npm -v
6.11.3

Next, we will install the Ionic CLI which now is version 5 and Cordova. Type this command in the terminal or command line to install it.

sudo npm i -g ionic cordova

As you see at the end of the Ionic and Cordova installation, we have Ionic-CLI 5 and Cordova 9.

+ [email protected]
+ [email protected]

Now, we have a ready to use Ionic Frameworks and Cordova environment in our machine.

Step #2. Create an Ionic 4 App with the Type of Angular

We will create an Ionic 4 app with the type of Angular and default template using Tabs. To do that, type this command in the terminal or command line.

ionic start ionic-tutorial tabs --type=angular

We will add Cordova later before we test the Ionic-Angular apps to the Android/iOS devices. Next, go to the newly created Ionic-Angular application then run the Ionic-Angular apps for the first time.

cd ./ionic-tutorial
ionic serve -l

If there's a question to install the @ionic/lab just type "Y". Now, the Ionic-Angular apps will open the default browser automatically and here the apps looks like in Lab mode.

Step #3. Modify Angular 8 Routes

We need to pass ID parameters to Tab2 and Tab3. So, we will modify the default or generated src/app/tabs/tabs-routing.module.ts. Just change the Angular routes constant to be like this.

const routes: Routes = [
  {
    path: 'tabs',
    component: TabsPage,
    children: [
      {
        path: 'tab1',
        children: [
          {
            path: '',
            loadChildren: () =>
              import('../tab1/tab1.module').then(m => m.Tab1PageModule)
          }
        ]
      },
      {
        path: 'tab2',
        children: [
          {
            path: '',
            loadChildren: () =>
              import('../tab2/tab2.module').then(m => m.Tab2PageModule)
          }
        ]
      },
      {
        path: 'tab2/:id',
        children: [
          {
            path: '',
            loadChildren: () =>
              import('../tab2/tab2.module').then(m => m.Tab2PageModule)
          }
        ]
      },
      {
        path: 'tab3',
        children: [
          {
            path: '',
            loadChildren: () =>
              import('../tab3/tab3.module').then(m => m.Tab3PageModule)
          }
        ]
      },
      {
        path: 'tab3/:id',
        children: [
          {
            path: '',
            loadChildren: () =>
              import('../tab3/tab3.module').then(m => m.Tab3PageModule)
          }
        ]
      },
      {
        path: '',
        redirectTo: '/tabs/tab1',
        pathMatch: 'full'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/tabs/tab1',
    pathMatch: 'full'
  }
];
Step #4. Display Data to the Ionic List/Grid

To display data as Ionic List or Grid, first, we have to create an array of objects. We will put this array of objects in a separate file. For that, create a new Typescript file src/app/player.ts then add these lines of the export constant array.

export const Player = [
    { id: 1, name: 'Robbie Fowler', team: 'Liverpool', photo: 'assets/imgs/photo1.jpeg', lat: 53.430855, lng: -2.960833 },
    { id: 2, name: 'Paul Ince', team: 'Manchester United', photo: 'assets/imgs/photo2.jpeg', lat: 53.463097, lng: -2.291351 },
    { id: 3, name: 'Eric Cantona', team: 'Manchester United', photo: 'assets/imgs/photo3.jpeg', lat: 53.463097, lng: -2.291351 },
    { id: 4, name: 'Thierry Henry', team: 'Arsenal', photo: 'assets/imgs/photo4.jpeg', lat: 51.554902, lng: -0.108449 },
    { id: 5, name: 'Alan Shearer', team: 'Newcastle United', photo: 'assets/imgs/photo5.jpeg', lat: 54.975593, lng: -1.621678 },
    { id: 6, name: 'Dennis Bergkamp', team: 'Arsenal', photo: 'assets/imgs/photo6.jpeg', lat: 51.554902, lng: -0.108449 },
    { id: 7, name: 'Didier Drogba', team: 'Chelsea', photo: 'assets/imgs/photo7.jpeg', lat: 51.481683, lng: -0.190956 },
    { id: 8, name: 'Jurgen Klinsmann', team: 'Tottenham Hotspur', photo: 'assets/imgs/photo8.jpeg', lat: 51.604320, lng: -0.066395 },
    { id: 9, name: 'Robin Van Persie', team: 'Arsenal', photo: 'assets/imgs/photo9.jpeg', lat: 51.554902, lng: -0.108449 },
    { id: 10, name: 'David Beckham', team: 'Manchester United', photo: 'assets/imgs/photo10.jpeg', lat: 53.463097, lng: -2.291351 },
    { id: 11, name: 'Steven Gerrard', team: 'Liverpool', photo: 'assets/imgs/photo11.jpeg', lat: 53.430855, lng: -2.960833 },
    { id: 12, name: 'Ian Rush', team: 'Liverpool', photo: 'assets/imgs/photo12.jpeg', lat: 53.430855, lng: -2.960833 },
];

Next, open and edit src/app/tab1/tab1.page.ts then add these imports of Player array and Angular Router.

import { Router } from '@angular/router';
import { Player } from '../player';

Inject the Angular Router in the constructor params.

constructor(public router: Router) {}

Add the variables before the constructor to handle an array of objects and view type (list/grid).

persons = Player;
viewType = 'list';

Add these functions after the constructor to switch the view of the list or grid.

changeToGrid() {
  this.viewType = 'grid';
}

changeToList() {
  this.viewType = 'list';
}

Next, open and edit src/app/tab1/tab1.page.html then add these lines of HTML tags that display the list/grid from the array of objects.

<ion-header>
  <ion-toolbar>
    <ion-title>
      Player
    </ion-title>
    <ion-buttons slot="end">
      <ion-button *ngIf="viewType === 'grid'" (click)="changeToList()">
        <ion-icon slot="icon-only" name="list-box"></ion-icon>
      </ion-button>
      <ion-button *ngIf="viewType === 'list'" (click)="changeToGrid()">
        <ion-icon slot="icon-only" name="grid"></ion-icon>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-list *ngIf="viewType === 'list'">
    <ion-list-header>
      Premier League Legend
    </ion-list-header>
    <ion-item *ngFor="let p of persons" [routerLink]="['/tabs/tab2/', p.id]">
      <ion-avatar slot="start">
        <img src="{{p.photo}}">
      </ion-avatar>
      <ion-label>
        <h2>{{p.name}}</h2>
        <h3>{{p.team}}</h3>
      </ion-label>
    </ion-item>
  </ion-list>

  <ion-grid *ngIf="viewType === 'grid'">
    <ion-row>
      <ion-col *ngFor="let p of persons" size-lg="3" size-md="4" size-sm="6" size="12">
        <ion-card>
          <img src="{{p.photo}}" />
          <ion-card-header>
            <ion-card-subtitle>{{p.name}}</ion-card-subtitle>
            <ion-card-title>{{p.team}}</ion-card-title>
          </ion-card-header>
        </ion-card>
      </ion-col>
    </ion-row>
  </ion-grid>
</ion-content>

As you can see, there are the buttons in the toolbar that show and hide based on the list/grid types. The click action will trigger the change of list/grid view types. So, there are 2 type of or which hide by "viewType" variable.

Don't forget to change the Tabs name in src/app/tabs/tabs.page.html to make tabs and its content is related.

<ion-tab-bar slot="bottom">
  <ion-tab-button tab="tab1">
    <ion-icon name="flash"></ion-icon>
    <ion-label>Player</ion-label>
  </ion-tab-button>

  <ion-tab-button tab="tab2">
    <ion-icon name="apps"></ion-icon>
    <ion-label>Details</ion-label>
  </ion-tab-button>

  <ion-tab-button tab="tab3">
    <ion-icon name="send"></ion-icon>
    <ion-label>Maps</ion-label>
  </ion-tab-button>
</ion-tab-bar>
Step #5. Display Data to the Ionic Card

We make two options to display details in Ionic Card in Tab 2. First, we will display a default static Ionic Card content if there's no data comes from other tabs. Second, we will display the Ionic Card of details that come from the other tabs. To do that, open and edit src/app/tab2/tab2.page.ts then add or modify these imports of Angular OnInit, ActivatedRoute, Router, and Player array.

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Player } from '../player';

Add implementation of Angular OnInit to the class name.

export class Tab2Page implements OnInit {
...
}

Inject the Angular ActivatedRoute and Router to the constructor.

constructor(public route: ActivatedRoute, public router: Router) {}

Add the variables before the constructor that hold details object and Player array.

details = { id: null, name: '', team: '', photo: '', lat: null, lng: null };
persons = Player;

Add an Angular ngOnInit function that finds an object from the player array.

ngOnInit() {
  if (this.route.snapshot.paramMap.get('id') !== 'null') {
    const id = parseInt(this.route.snapshot.paramMap.get('id'), 0);
    this.details = this.persons.find(x => x.id === id);
  }
}

We use ngOnInit because want to load the details from the array every tab 2 showed up. Next, add a function to navigate to the Tab 3 or show the Google Maps.

showMap(id: any) {
  this.router.navigate(['/tabs/tab3/', id]);
}

Next, open and edit src/app/tab2/tab2.page.html then replace all HTML tags with these.

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

<ion-content>
  <ion-card *ngIf="!details">
    <img src="assets/imgs/anfield.jpg" />
    <ion-card-header>
      <ion-card-subtitle>Anfield Stadium</ion-card-subtitle>
      <ion-card-title>Liverpool</ion-card-title>
    </ion-card-header>
    <ion-card-content>
      Anfield is a football stadium in Anfield, Liverpool, Merseyside, England, which has a seating capacity of 54,074, making it the seventh largest football stadium in England. It has been the home of Liverpool FC since their formation in 1892\. It was originally the home of Everton FC from 1884 to 1891, before they moved to Goodison Park after a dispute with the club president.
    </ion-card-content>
  </ion-card>

  <ion-card *ngIf="details">
    <img src="{{details.photo}}" />
    <ion-card-header>
      <ion-card-subtitle>{{details.name}}</ion-card-subtitle>
      <ion-card-title>{{details.team}}</ion-card-title>
    </ion-card-header>
    <ion-card-content>
      <ion-button (click)="showMap(details.id)">
        <ion-icon slot="icon-only" name="map"></ion-icon>
      </ion-button>
    </ion-card-content>
  </ion-card>
</ion-content>

As you see, there are two that display static details and details from objects based on details object's existence.

Step #6. Display Google Maps

In this step, we will show the Google Maps that come from the static latitude/longitude or dynamic latitude/longitude from the object. For that, open and edit src/app/tab3/tab3.page.ts then add or modify these imports of Ionic ViewChild, ElementRef, OnInit, Platform, ActivatedRoute, and Player object.

import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { Platform } from '@ionic/angular';
import { ActivatedRoute } from '@angular/router';
import { Player } from '../player';

Declare the Google Map and map variable before the @Component.

declare var google;
let map: any;

Add the implementation of Angular OnInit in the class name line.

export class Tab3Page implements OnInit {
...
}

Inject the ActivatedRoute and Platform to the constructor.

constructor(public route: ActivatedRoute, public platform: Platform) {}

Add the variables of @ViewChild, details object, and Player array before the constructor.

@ViewChild('map', {static: false}) mapElement: ElementRef;
details = { id: null, name: '', team: '', photo: '', lat: null, lng: null };
persons = Player;

Add the Angular ngOnInit function that loads the Google Maps inside the Platform ready method.

ngOnInit() {
  if (this.route.snapshot.paramMap.get('id') === null) {
    this.platform.ready().then(() => {
      map = new google.maps.Map(this.mapElement.nativeElement, {
        center: { lat: -6.930560, lng: 107.558439 },
        zoom: 15
      });
    });
  } else {
    const id = parseInt(this.route.snapshot.paramMap.get('id'), 0);
    this.details = this.persons.find(x => x.id === id);
    this.platform.ready().then(() => {
      map = new google.maps.Map(this.mapElement.nativeElement, {
        center: { lat: this.details.lat, lng: this.details.lng },
        zoom: 15
      });
    });
  }
}

The Google Maps coordinate based on data that come from the previous Tab, if the data is null then we put the static coordinate. Next, open and edit src/app/tab3/tab3.page.html then replace all HTML tags with these.

<ion-header>
  <ion-toolbar>
    <ion-title>
      Show Google Maps
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <div #map id="map"></div>
</ion-content>

As you see, there's just a

that holds the Google Maps. To make this Google Maps working, we need to import or load the Google Maps Javascript API library to the src/index.html before the end of the tag.

<body>
  <app-root></app-root>
  <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBgZZK8umUqJn8d5CoIZqWPJ_qtMfqD9q0"></script>
</body>

The Google Maps only working if the

have a height. So, add these CSS codes to the src/app/tab3/tab3.page.scss.

#map {
    height: 690px;
}
Step #7. Run and Test the Ionic 4/Angular 8 Apps

In this step, we will be working with the Cordova before run and test the Ionic 4/Angular 8 apps to the Android and iOS devices. First, remove and add all platforms from this Ionic app project by typing these commands.

ionic cordova platform rm android
ionic cordova platform add android
ionic cordova platform rm ios
ionic cordova platform add ios

Now, you can run the Ionic 4/Angular 8 apps to the Android and iOS devices. Type this command to run directly to the Android device and make sure the Android device connected to your computer.

ionic cordova run android

For iOS, we need to build this app first.

ionic cordova build ios

Open the platforms/ios/MyApp.xcworkspace in your XCode application then run it to iOS device or Simulator. The app will look like this.




That it's, the Ionic 4 Tutorial: How to Create Mobile Apps Quickly. As usual, we always provide full working source codes from our GitHub.