Showing posts with label Ionic. Show all posts
Showing posts with label Ionic. Show all posts

Wednesday, 21 July 2021

Android Back Button to Exit App in Ionic

There are at least two ways to have Android hardware back button to exit app. I am using Ionic 5 at the time of writing this.

1) Using IonRouterOutlet to check through history stack
import { IonRouterOutlet, Platform } from '@ionic/angular';
import { Plugins } from '@capacitor/core';
const { App } = Plugins;

export class HomePage {
   constructor(private platform: Platform, private routerOutlet: IonRouterOutlet){
   
      . . .
   
      this.platform.backButton.subscribeWithPriority(-1, () => {
         if (!this.routerOutlet.canGoBack()) {
            navigator['app'].exitApp();
            //App.exitApp();   // if using Capacitor framework, we can use Plugins component
         }
      });
   }

   . . .
}

2) Using Location from Angular library
import { Platform } from '@ionic/angular';
import { AfterViewInit, OnDestroy } from "@angular/core";
import { Subscription } from "rxjs";
import { Location } from '@angular/common';

export class HomePage implements OnDestroy, AfterViewInit {
   backButtonSubscription: Subscription;
    
   constructor(private platform: Platform, private location: Location){   
      . . .
   }
   
   ngAfterViewInit() {
      this.backButtonSubscription = this.platform.backButton.subscribe(() => {
         if (this.location.isCurrentPathEqualTo("HOME_PAGE_URL")){
            navigator['app'].exitApp();
         }
      });
   }
   
   ngOnDestroy() {
      this.backButtonSubscription.unsubscribe();
   }

   . . .

}

References:
Ionic Framework Documentation - Hardware Back Button
How To Exit App On Back Press | Ionic 4

Further reading:
Ionic 5 handle back hardware event to confirm exit by user

Wednesday, 12 August 2020

Checking Host Platform in Ionic 5

We can use Platform service in Ionic library to check the host platform. Below are the codes:
import { Platform } from '@ionic/angular';   // first import this library

constructor(private plt: Platform) {
	this.plt.ready().then(() => {

		if (this.plt.is('android') || this.plt.is('ios')) {
			console.log("running on Android or ios device!");

		}

		if (this.plt.is('mobileweb')) {
			console.log("running in a browser on mobile!");
		}
		
		if (this.plt.is('desktop')) {
			console.log("running on desktop");
		}
	});
}
Possible values are (from Ionic website):
Platform Name Description
android a device running Android
capacitor a device running Capacitor
cordova a device running Cordova
desktop a desktop device
electron a desktop device running Electron
hybrid a device running Capacitor or Cordova
ios a device running iOS
ipad an iPad device
iphone an iPhone device
mobile a mobile device
mobileweb a web browser running in a mobile device
phablet a phablet device
pwa a PWA app
tablet a tablet device


Wednesday, 8 July 2020

Passing Data from Ionic PopoverController

Since Ionic version 5, Events is no longer supported and will give compilation error. I have posted Using Popover Controller in Ionic but there's a proper way to pass data back from the popover controller to the caller.

We should leverage onDidDismiss() function in the caller component and passing the data as an argument of dismiss() function in the popover.

Caller component create and show popover:
async showCalculateWeeklyRent(ev) {
 const popover = await this.popoverCtrl.create({
  component: CalculateRentComponent, // the popover component that we created
  event: ev,
  componentProps: { 
     // data to be passed
     . . .
  },
  cssClass: 'popoverClass',
 });

 // get data returned from the popover
 popover.onDidDismiss().then(returnedValue => {
  if (returnedValue.data) {   // need to access the 'data' property to get the returned value
   this.model.weeklyRent = returnedValue.data;
   this.calculateYearlyRent();
  }
 });

 return await popover.present();
}
Note that we need to use the data property to get the value.

Then on the popover component, we can simply pass an argument when closing it:
. . .
     
closeAndReturnData() {
    // pass data to caller
    this.popoverCtrl.dismiss(this.shouldBeWeeklyRent);
}

closePopOver() {
    // simply close it
    this.popoverCtrl.dismiss();
}

. . .

Wednesday, 20 November 2019

Using Popover Controller in Ionic

We will learn how to use Popover Controller in Ionic. Popover is more versatile compares to Alert Controller or Modal Controller. We can put different input fields and elements and have customised template with two ways data binding. Pretty much like a Component.

On this post we will learn how to create a popover with an input field, a select dropdown with a function and buttons. The popover takes data from main page and returns data to the main page.

This is how the popover looks like:


First, create the popover Component:
import { Component } from "@angular/core";
import { PopoverController, Events } from '@ionic/angular';

@Component({    
    templateUrl: './calculate-rent.component.html',
    styleUrls: ['../base/property.page.scss'],
})

export class CalculateRentComponent {
     // some variables to be bound to the template 
    shouldBeWeeklyRent: number;

    constructor(private popoverCtrl: PopoverController, private events: Events) {
        
    }
     
    popoverEvent() {
        this.events.publish('fromPopoverEvent', this.shouldBeWeeklyRent);
        this.popoverCtrl.dismiss();
    }

    closePopOver() {
        this.popoverCtrl.dismiss();
    }
}
We use Events to pass data back to the caller component. Note this.events.publish(‘[name]’, [data]) on the codes.

The template:
<ion-grid>
    <ion-row>
        <ion-col class="text-header" style="text-align: center">Calculate Weekly Rent</ion-col>
    </ion-row>
    <ion-row>
        <ion-col size="8">Desired rental yield:</ion-col>
        <ion-col size="3">
            <input type="text" class="textinput" style="border:1px solid #b2b2b2; background-color: #fff; padding: 0 3px 0 3px; text-align: right; width: 100%"
                   number-input [(ngModel)]="desiredYield" (ngModelChange)="calculateWeeklyRent()" />
        </ion-col>
        <ion-col size="1"><span style="padding-top: 6px; padding-left: 0px">%</span></ion-col>
    </ion-row>
    <ion-row>
        <ion-col size="8">From which type:</ion-col>
        <ion-col size="3">
            <select [(ngModel)]="desiredYieldType" (ngModelChange)="calculateWeeklyRent()">
                <option value="gross">Gross</option>
                <option value="net">Net</option>
            </select>
        </ion-col>
        <ion-col></ion-col>
    </ion-row>
    <ion-row>
        <ion-col size="8">New weekly rent:</ion-col>
        <ion-col><b>{{shouldBeWeeklyRent | currency}}</b></ion-col>
    </ion-row>
    <ion-row>
        <ion-col></ion-col>
    </ion-row>
    <ion-row>
        <ion-col size="4">
            <button type="submit" class="button custom-button" (click)="closePopOver()">
                Close
            </button>
        </ion-col>
        <ion-col size="8">
            <button type="submit" class="button custom-button" (click)="popoverEvent()">
                Put in Rental Field
            </button>
        </ion-col>
    </ion-row>
</ion-grid>

We then add variables for data binding and the function that will be called on the component.

Then on the main page:
async showCalculateWeeklyRent(ev) {
 const popover = await this.popoverCtrl.create({
  component: CalculateRentComponent, // the popover component that we created
  event: ev,
  componentProps: { // data to be passed
   totalCost: this.model.totalCost,
   totalExpense: this.model.totalExpense,
   totalIncomeWithoutRent: this.model.totalIncomeWithoutRent
  },
  cssClass: 'popoverClass',
 });

 // sync event from popover component
 this.events.subscribe('fromPopoverEvent', (shouldBeWeeklyRent) => {
  this.model.weeklyRent = shouldBeWeeklyRent;
  this.calculateYearlyRent();
 });

 return await popover.present();
}

Note the componentProps property can be used to pass data to popover component. It can contain objects that have more complex structure as well.
We use Events and its subscribe() method to receive data back.

On popover component, to receive data from the main page, we just need to declare local variables then they will be bound automatically with the data passed from the main page. We just need to make sure the variable names are similar.
// codes in main page
async showCalculateWeeklyRent(ev) {
 const popover = await this.popoverCtrl.create({
  . . .
  componentProps: { // data to be passed
   totalCost: this.model.totalCost,
   totalExpense: this.model.totalExpense,
   totalIncomeWithoutRent: this.model.totalIncomeWithoutRent
  },
  . . .
 });

 . . .
}

// codes in popover
export class CalculateRentComponent {
    . . .
    totalCost: number;
    totalExpense: number;
    totalIncomeWithoutRent: number;

    // note that even we don’t need to state the variables in the constructor
    constructor(private popoverCtrl: PopoverController, private events: Events) {
        . . .
    }
    . . .
}

Now the full codes in popover component:
import { Component } from "@angular/core";
import { PopoverController, Events } from '@ionic/angular';

@Component({    
    templateUrl: './calculate-rent.component.html',
    styleUrls: ['../base/property.page.scss'],
})
export class CalculateRentComponent {
    desiredYield: number;
    desiredYieldType: string = 'gross';
    shouldBeWeeklyRent: number;
    totalCost: number;
    totalExpense: number;
    totalIncomeWithoutRent: number;

    constructor(private popoverCtrl: PopoverController, private events: Events) {
    }
    
    calculateWeeklyRent() {
        . . .
        this.shouldBeWeeklyRent = …;
        . . .
    }

    popoverEvent() {
        this.events.publish('fromPopoverEvent', this.shouldBeWeeklyRent);
        this.popoverCtrl.dismiss();
    }

    closePopOver() {
        this.popoverCtrl.dismiss();
    }
}


Reference:
https://edupala.com/ionic-4-popover/

Thursday, 8 August 2019

Commands to Check Ionic, Cordova and Plugin Version

Cordova
see installed version
cordova -v
or
cordova --version
see the latest version available
npm info cordova
to upgrade to latest version
npm update -g cordova
-g means install globally

otherwise need to uninstall and reinstall
npm uninstall -g cordova
npm install -g cordova

Ionic CLI

installed version
ionic -v
or
ionic --version
latest version available
npm info ionic
to upgrade to latest version
npm update -g ionic
-g means install globally

otherwise need to uninstall and reinstall
npm uninstall -g ionic
npm install -g ionic

Plugin of an Cordova app
installed version
npm list thePluginName
latest version available
npm info thePluginName
to upgrade to latest version
npm update thePluginName

otherwise need to uninstall and reinstall

or you can try to use cordova-check-plugins plugin
to add to project
cordova plugin add thePluginName
or
ionic cordova plugin add thePluginName
to remove from project
cordova plugin remove thePluginName
or
ionic cordova plugin remove thePluginName

You can also use
ionic info
command in an App directory to check Ionic CLI, Ionic Framework, Cordova CLI, Platforms, Plugins installed and other information.

Monday, 14 January 2019

My Notes of Switching from Visual Studio to Ionic CLI

Recently, I tried to update my app in Google Play and Apple Store but failed to do so because they mentioned that I need to use later version of Android or iOS. I have been using Visual Studio 2013 then Visual Studio 2016 with Tools for Apache Cordova however when checking the latest supported Android and iOS versions, they have not progressed much in the last two years. After some research, I decided to use Apache Cordova directly, together with Ionic, Angular and Node.js. Apache Cordova and Ionic are still progressing and very much alive. I still use Visual Studio 2017 though for code editor only. All project configurations, simulator settings and others will be done at lower level, using Apache Cordova directly. As my app was written in JavaScript and HTML using Ionic 1 so I think it is a good choice.

The notes below are my journey to move from Visual Studio Tool for Apache Cordova to the CLI option. I use same machine to install the required new components. Hopefully it can help anyone who chooses a similar path as mine.


SETTING UP FRAMEWORKS
First I had Node.js, recent Cordova and Ionic installed. Once they were installed I tried to create a new test app by running:
ionic start myApp sidemenu
then it will show:
+ ionic@4.2.1
added 242 packages from 151 contributors in 43.756s
? Integrate your new app with Cordova to target native iOS and Android?
Chose yes and continued with the installation.
After installation:
cd myApp
ionic serve
I made sure the app could be compiled successfully and showed in a browser.

Then I added Android platform:
ionic cordova platform add android
To check the installed version run:
cordova platform version android
Then it showed:
7.1.1 is the latest version


SETTING UP EMULATOR
Next, I tried to set up the emulator with latest version of Android. But first I wanted to make sure that I could run an existing virtual device from AVD Manager.

When trying to run one, I got this error:
“Could not find an installed version of Gradle either in Android Studio,
or on your system to install the gradle wrapper. Please include gradle
in your path, or install Android Studio
[ERROR] An error occurred while running subprocess cordova.”
I installed Gradle from the website and set an environment variable for it.

The second error I got after trying to run the emulator:
“Could not unzip C:\Users\rical\.gradle\wrapper\dists\gradle-4.1-all\bzyivzo6n839fup2jbap0tjew\gradle-4.1-all.zip to C:\Users\rical\.gradle\wrapper\dists\gradle-4.1-all\bzyivzo6n839fup2jbap0tjew.
Reason: error in opening zip file
Exception in thread "main" java.util.zip.ZipException: error in opening zip file”
I deleted the zip folder and run the command again.

Then I got another error:
“Error occurred during initialization of VM
Could not reserve enough space for 2097152KB object heap”
I went to Start -> Control Panel -> System -> Advanced(tab) -> Environment Variables -> System Variables and add new variable:
Variable name: _JAVA_OPTIONS
Variable value: -Xmx512M

After trying to run the emulator again, another error shown up:
“A problem occurred configuring project ':CordovaLib'.
> You have not accepted the license agreements of the following SDK components:
[Android SDK Platform 27, Android SDK Build-Tools 26.0.2].
Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager.”
Went to SDK Manager and install SDK Platform 27 and Android SDK Build-Tools 26.0.2.

The emulator is working now, but I received a warning:
“Running an x86 based Android Virtual Device (AVD) is 10x faster. We strongly recommend creating a new AVD.”
Solved this by installing Intel x86 Atom_64 or Intel x86 Atom and setting the emulator (AVD) to use one of them.
I received another error “PANIC: Cannot find AVD system path. Please define ANDROID_SDK_ROOT”. I created a new environment variable called ANDROID_SDK_ROOT that has my SDK path, something like "C:\Program Files (x86)\Android\android-sdk". Then restarted the machine.

I wanted to use recent version of Android which is version 8 or 9 but after installing the system images, I got this error:
“This AVD's configuration is missing a kernel file! Please ensure the file "kernel-qemu" is in the same location as your system image”
I found a great article to solve the issue https://www.andreszsogon.com/using-android-8-and-9-emulators-without-android-studio
I followed the instructions:
- download emulator-windows-4848055.zip
- uninstall my current Android 8 and newer system images
- close my Android SDK Manager and AVD Manager tools if open
- extract the contents from the ZIP file into my android-sdk/tools
- download the desired emulator’s System Images from the SDK Manager
- create a new emulator from the AVD Manager
- start a virtual device

Received another error:
“emulator: ERROR: x86 emulation currently requires hardware acceleration!
Please ensure Windows Hypervisor Platform (WHPX) is properly installed and usable.
CPU acceleration status: HAXM is not installed on this machine”
Went to Turn Windows features on and off, checked Windows Hypervisor Platform.

Finally, I could run a virtual device from the AVD Manager.

When I tried to run from command prompt:
ionic cordova emulate --target=My_Android_9_Virtual_Device  android
I got another error:
“A problem occurred configuring project ':CordovaLib'.
> Failed to find Platform SDK with path: platforms;android-27”
I found out that cordova-android 7.1.1 only supports up to Android 8.1 (SDK 27). So I needed to download the SDK Platform and a choice of system image of the newer version.

Finally, the emulator from command prompt is working!

Thursday, 15 June 2017

Open Link on Browser in Ionic Framework

To be able to open a link in an Ionic Framework based app, we need to install InAppBrowser plugin. If you use Visual Studio Tools for Apache Cordova, you can open config.xml file and find in Plugins section.

After installing the plugin, we don’t need to pass any new module in the code function constructor. All we need to do is just to call the functions directly like:
cordova.InAppBrowser.open('http://www.google.com', '_system');
// or we can use
window.open('http://www.google.com', '_system');
_system target is used so that the link will be opened on system's web browser.

In HTML code, we can call like this:
<a href="#" onclick="window.open('https://www.google.com', '_system');">my link</a>
Don’t forget to include the ‘http://’ otherwise you will get an error like ‘Cannot display PDF (… cannot be opened).

Thursday, 9 February 2017

Ionic Modal with this Controller

Below is a simple example of using Ionic Modal with this controller (Controller As):
var vm = this;
    . . .
    . . .
    . . .

    /* modal */
    vm.showModal = function () {
        $ionicModal.show();
    };

    $ionicModal.fromTemplateUrl('my-modal.html', {
        scope: $scope,
        animation: 'slide-in-up'
    }).then(function (modal) {
        vm.modal = modal;
    });

    vm.openModal = function () {
        vm.modal.show();
    };

    vm.closeModal = function () {
        vm.modal.hide();
    };

    // Clean up the modal
    $scope.$on('$destroy', function () {
        vm.modal.remove();
    });

    // Execute action on hide modal
    $scope.$on('modal.hidden', function () {
        . . .
    });

    // Execute action on remove modal
    $scope.$on('modal.removed', function () {
        . . .
    });
Note that we still need to use $scope for particular function.

The modal template:
<ion-modal-view>
    <ion-header-bar>
        <h1 class="title">My Modal title</h1>
    </ion-header-bar>
    <ion-content>
        Hello!
        <button ng-click="vm.closeModal()">Close</button>
    </ion-content>
</ion-modal-view>

Tuesday, 8 November 2016

Quick Guide to Use Ionic Framework Side Menu

This post will guide you to quickly set side menu in Ionic Framework.

Firstly, we need to set up the routes in app.js file. In this case we have our menu template in menu.html file. We need to name the menu state (e.g. app) and then all other pages will have states with this syntax menuStateName.pageStateName. We will also need to set abstract: true for the menu state.

Below is an example:
ionicApp.config(['$stateProvider', '$urlRouterProvider',
    function ($stateProvider, $urlRouterProvider) {
        $stateProvider
            .state('app', {
                url: '/app',
                abstract: true,
                templateUrl: 'menu.html'
            })
          .state('app.add', { // add item page
              url: "/add",
              views: {
                  'menuContent': {
                      templateUrl: 'add-item.html'
                  }             
              }
          })
          .state('app.list', { // list items page
              url: "/list",
              views: {
                  'menuContent': {
                      templateUrl: 'list.html'
                  }             
              }
         })
         .state('app.edit', { // edit item page
             url: "/edit/:itemId",
             views: {
                 'menuContent': {
                     templateUrl: 'edit-item.html'
                 }             
             }
         });
        // if none of the above states are matched, use this as the fallback
        $urlRouterProvider.otherwise('/app/add');
    }
]);

On index.html, we just need to put <ion-nav-view></ion-nav-view>.
<body ng-app="ionicApp">
      <ion-nav-view></ion-nav-view>
  </body>

Then on each page file, we have ion-view and ion-content:
<ion-view view-title="PageTitle">
  <ion-content>
    ... your markup content ...
  </ion-content>
</ion-view>

Then on the menu markup file (menu.html), we have something like this:
<ion-side-menus enable-menu-with-back-views="true">
    <ion-side-menu-content>
        <ion-nav-bar class="bar">
            <!--<ion-nav-back-button>
            </ion-nav-back-button>-->
            <ion-nav-buttons side="left">
                <button class="button button-icon ion-navicon" menu-toggle="left"></button>
            </ion-nav-buttons>
        </ion-nav-bar>
        <ion-nav-view name="menuContent"></ion-nav-view>
    </ion-side-menu-content>
    <ion-side-menu side="left">
        <ion-header-bar class="custom-brown">
            <div class="title">Menu Title</div>
        </ion-header-bar>
        <ion-content>
            <ion-list>
                <ion-item menu-close href="#/app/add">
                    <b>Add Item</b>
                </ion-item>
                <ion-item menu-close href="#/app/list">
                    <b>List Items</b>
                </ion-item>
                <ion-item menu-close href=". . .">
                    <b>. . .</b>
                </ion-item>
                <ion-item menu-close href=". . .">
                    <b>. . .</b>
                </ion-item>
            </ion-list>
        </ion-content>
    </ion-side-menu>
</ion-side-menus>
On the example above, I use enable-menu-with-back-views="true" so that the menu icon will always be displayed. I also commented out <ion-nav-back-button></ion-nav-back-button> to hide the back button so only the menu icon will be displayed.

We can also have the menu items populated dynamically. We could use something like below:
                <ion-item menu-close ng-repeat="item in vm.items" ui-sref="app.edit({itemId: {{item.itemId}}})">
                    <b>Edit :</b> {{item.name}}
                </ion-item>

Monday, 27 July 2015

Automatically Generate Resized Icons and Splash Screens with Ionic Framework

Rather than manually generating or cropping different size of images of icons and splash screens for different resolutions, orientation and platforms, we can use Ionic Framework to do this work for us. Ionic will use its resizing and cropping server to do this. We will need a recent version of Ionic CLI.

Below are the steps to do this:
1. Prepare the source icon or splash screen file
For icon file:
- the file is named with 'icon' and has either .png, .psd or .ai extension type (i.e. icon.png, icon.psd or icon.ai)
- image's minimum dimensions should be 192 x 192 px
- rounded corners will be applied for specific platforms (e.g. iOS)

For splash screen file:
- the file is named with 'splash' and has either .png, .psd or .ai extension type (i.e. splash.png, splash.psd or splash.ai)
- image's minimum dimensions should be 2208 x 2208 px
- the artwork should be centered within inner 1200 x 1200 px area

2. Use a new or existing Ionic project to do this job
To have the Ionic server doing the resizing and cropping, we will need to put the file inside an Ionic project.

3. Make sure we have the intended platform on the Ionic project.
To add a platform, run ionic platform add command in the root folder of the project, for example:
C:\MyLabs\IonicTemplate\SideMenu>ionic platform add android

4. Create 'resources' folder on the root of the project

5. Put the icon or splash screen file in the folder

6. Run ionic resources command at the project root
To generate icons, run ionic resources --icon command (with double dash characters), below is a screenshot:


To generate splash screens, run ionic resources --splash command (with double dash characters), below is a screenshot:


7. Get the icons from 'resources/PLATFORM_NAME/icon' folder or the splash screens from 'resources/PLATFORM_NAME/splash' folder

Thursday, 16 April 2015

Form Validation in Ionic or AngularJS

On this post we will see how to do form validation in AngularJS or Ionic framework. At the moment, Ionic framework does not have its own validation features and relies on the underlying framework.

Basic Usage with Built-In Validators
To begin, first we need to make sure that we have a reference to angular.js file. Then we need to do the following steps:
- give a name to our form
- put novalidate="" attribute on the form
So we should have something like this on our form:
<form name="myForm" novalidate="" ng-submit="" >
 . . .
</form>
- add a unique name to each input field
- add validation attributes that we need on the input fields. AngularJS provides some validation directives such as these; required, pattern, minlength, maxlength, min and max. It also has basic built-in validators for these input types; text, number, url, email and date.
An example on an input field:
<input type="text" name="name" ng-model="vm.input.name" required />
- add an error message to be displayed for each validator checking the corresponding property name on input field's $error object in conditional statement like ng-if or ng-show. Properties under $error object with the same names as the validators' names exist for us to use. The properties are on this format; formName.fieldName.$error.builtInValidatorName. For example:
<span ng-show="myForm.name.$error.required == true">* required</span>
- on a method that is called in ng-submit, check whether the form is valid using formName.$valid before doing any action

Below is a complete example:
<!-- HTML page -->
<body ng-app="validationExample">
    <div ng-controller="MyCtrl as vm">
      <form name="myForm" novalidate="" ng-submit="vm.submitted(myForm, vm.input)">
        <div>
          <input type="text" name="name" ng-model="vm.input.name" required>
          <span ng-show="myForm.name.$error.required == true">test: * required</span>
        </div>
        <button>submit</button>
      </form>
    </div>
</body>

// JavaScript codes
var myApp = angular.module('validationExample', [])

myApp.controller('MyCtrl', [function () {
    var vm = this;
    vm.submitted = function(form, input) {
      if(form.$valid) {
        // post the form
        . . .
      }
    }
} ]);
See these codes on Plunker.


Creating Custom Validator
If we would like to create a custom validator, we would need to do these in addition of the above steps:
- create a directive
- on the directive's link function, use the new AngularJS $validators object on ngModel controller to do the validation. We need to create a function property on ngModel.$validators. By doing so, we register the function as a new validation rule. Each time the model changes, the validation rule is invoked. For example:
link: function(scope, element, attributes, ngModel) {
 ngModel.$validators.wrongInitial = function(modelValue) {
   . . .
 };
}
A corresponding property on input field's $error object is also created that can be used for checking validity.
- similar like in using built-in validator, we need to add an error message to be displayed by checking the newly created corresponding property on the input field's $error object:
<span ng-show="myForm.name.$error.wrongInitial == true">* must start with letter r</span>

Additional codes to be added from the first example:
<!-- HTML page -->
<input type="text" name="name" ng-model="vm.input.name" start-with-r />
<span ng-show="myForm.name.$error.wrongInitial == true">* must start with letter r</span>

// JavaScript codes
myApp.directive("startWithR", function() {
    return {
      restrict: "A",
      require: "?ngModel",
      link: function(scope, element, attributes, ngModel) {
        ngModel.$validators.wrongInitial = function(modelValue) {
          if (modelValue) {
            return modelValue.charAt(0).toUpperCase() == 'R';
          }
          else {
            return true;
          }
        };
      }
    };
});
See a complete example on Plunker.


Using ngMessages to Display Error Messages
In the recent version of AngularJS, started with version 1.3, there is a new feature that we could use to display messages including form error messages. To use this, we need to:
- include a reference to angular-messages.js file in our page
- inject ngMessages module to our AngularJS app
var myApp = angular.module('validationExample', ['ngMessages'])
- if there are only a few fields to be validated, we could specify each error message for each field like this:
<!-- use ng-messages with each field's $error object in this format; formName.fieldName.$error -->
<div ng-messages="myForm.name.$error" >  
        <!-- specify an error message for each validator -->
 <span ng-message="required">Name is required!</span>
 <span ng-message="wrongInitial">Name must start with letter r</span>
</div>
- however if we know that we are going to repeat this multiple times, we could use a template instead:
<!-- create a template by using script tag with type="text/ng-template" and give an Id -->
<script id="errors-template.html" type="text/ng-template" >  
        <!-- specify an error message for each validator -->
 <span ng-message="required">* required!</span>
 <span ng-message="wrongInitial">* must start with letter r!</span>
</script>
Then use the template to display error message(s) on each field:
<!-- use ng-messages with each field's $error object and ng-messages-include referring to the template Id -->
<span ng-messages="myForm.name.$error" ng-messages-include="errors-template.html" />
See the complete example on Plunker.


References:
Working with Validators and Messages in AngularJS
Validation in Ionic Framework Apps with ngMessages

Friday, 10 October 2014

Basic Navigation in Ionic

On this post we will see how to do basic navigation in Ionic Framework.

1. First we need to configure the routes in AngularJS:
app.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider
  .state('home', {
    url: '/',
    templateUrl: 'home.html'
  })
  .state('pageOne', {
    url: '/page1',
    templateUrl: 'one.html'
  })
  .state('anotherPage', {
    url: '/whatever',
    templateUrl: 'myhtml.html'
  });
  
   $urlRouterProvider.otherwise("/");
});
In the example we use UI Router to manage our routes. Each route has a state name, a defined url and an actual path for the view content. In the end, we also define the default url to be called when a url targeted does not match any in configurations.


2. Then on our main page, we just need to use
<ion-nav-view></ion-nav-view>
It will be useful to add a navigation bar with back button and also to have an animation effect like this:
<ion-nav-bar class="bar-positive">
        <ion-nav-back-button class="button-icon ion-arrow-left-c">
        </ion-nav-back-button>
    </ion-nav-bar>

    <ion-nav-view animation="slide-left-right"></ion-nav-view>
When we use Ionic navigation back button, the scripts behind it automatically do the job in storing page histories and pulling out the previous page as well as redirecting to the page when clicking the element.


3. Finally create each partial view using
<ion-view>
  <ion-content>
    . . .
  </ion-content>
</ion-view>


For a complete working example, please see here in Plunker.

Thursday, 2 October 2014

Getting Started with Cordova and Ionic in Visual Studio

On this post, we will see how to install Visual Studio Apache Cordova tool and Ionic framework and get started with a sample application.

1. First we need to download and install Apache Cordova tool from here. At the time of writing, the version is CTP 2.0.

2. Then once completed, open Visual Studio to create a new project. On the New Project dialog, go to JavaScript project templates then Multi-Device Hybrid App. Select Blank App (Apache Cordova) then create a project.

3. Install Ionic through NuGet. At the time of writing, the latest version is v1.0.0.0-beta.1. This will add many AngularJS and Ionic files into our project.

4. Install Ionic on local machine so that we could create some sample applications with particular templates and use some of the files in our project. To do this, open Node.js command prompt then run this command:
npm install ionic -g
-g is used to install the package globally

5. On the command prompt, go to a directory where you want to have Ionic sample applications then run this command:
ionic start SideMenuAppExample sidemenu
This command will generate a sample Ionic application with side menu navigation.

6. Go to the generated app folder then to 'www\js' folder then copy app.js and controllers.js into our project.

7. Also copy the whole 'templates' folder and its content into our project.

8. Then on our project index.html file:
- add a reference to 'Content/ionic.css'
- add a reference to 'scripts/ionic.bundle.js'
- add references to the two JavaScript files that we have copied over (app.js and controllers.js)
- add 'ng-app="starter"' in the body
- add <ion-nav-view></ion-nav-view> inside the body content

Once finished, we will have something like this on our index.html:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>BlankCordovaApp2</title>

    <link href="Content/ionic.css" rel="stylesheet" />
    <link href="css/index.css" rel="stylesheet" />
</head>
<body ng-app="starter">
    <ion-nav-view></ion-nav-view>


    <!--library scripts references-->
    <script src="scripts/ionic.bundle.js"></script>
    <script src="cordova.js"></script>
    <script src="scripts/platformOverrides.js"></script>
    
    <!--custom scripts references-->
    <script src="app.js"></script>
    <script src="controllers.js"></script>
</body>
</html>

Then run the application. We should see a sample app with side menu running.