<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tutorials Archives - Ionic and Django Tutorial</title>
	<atom:link href="https://www.ionicanddjangotutorial.com/category/ionic-tutorials/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.ionicanddjangotutorial.com/category/ionic-tutorials/</link>
	<description>Build a mobile application with Ionic  and Django</description>
	<lastBuildDate>Thu, 13 May 2021 05:59:29 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.0.9</generator>
	<item>
		<title>How to validate a phone number by sending an SMS code using Ionic,Django and SmsApi ?</title>
		<link>https://www.ionicanddjangotutorial.com/how-to-validate-a-phone-number-by-sending-an-sms-code-using-ionicdjango-and-smsapi/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Mon, 22 Feb 2021 13:25:17 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=907</guid>

					<description><![CDATA[<p>In this tutorial, we will learn how to enter an international phone number within a Ionic application, and how to&#8230; <a href="https://www.ionicanddjangotutorial.com/how-to-validate-a-phone-number-by-sending-an-sms-code-using-ionicdjango-and-smsapi/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-validate-a-phone-number-by-sending-an-sms-code-using-ionicdjango-and-smsapi/">How to validate a phone number by sending an SMS code using Ionic,Django and SmsApi ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this tutorial, we will learn how to enter an international phone number within a Ionic application, and how to validate this phone number by sending an SMS code using a Django backend and SMSAPI services.</p>


<p><strong><em>Please notice</em></strong>, you should have read previous tutorials to know how to create and manage API services with Ionic (like checking the network, displaying loading alert, passing data to pages) or how to manage API with Django and Rest framework.</p>

<p>
You can find source code on my <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/PhoneValidation">Github</a></p>
<p></p></p>

<h2>How to enter an international phone number using ionic ?</h2>


<figure class="wp-block-image size-large is-resized"><img src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-1-473x1024.png" alt="" class="wp-image-1004" width="237" height="512" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-1-473x1024.png 473w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-1-600x1299.png 600w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-1-139x300.png 139w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-1-709x1536.png 709w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-1-400x866.png 400w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-1.png 750w" sizes="(max-width: 237px) 100vw, 237px" /></figure>


<figure class="wp-block-image size-large is-resized"><img loading="lazy" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-473x1024.png" alt="" class="wp-image-998" width="237" height="512" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-473x1024.png 473w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-600x1299.png 600w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-139x300.png 139w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-709x1536.png 709w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone-400x866.png 400w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/phone.png 750w" sizes="(max-width: 237px) 100vw, 237px" /></figure>


<p>First we will create a new Ionic/Capacitor project called <strong>PhoneValidation</strong> </p>


<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start PhoneValidation blank --capacitor --project-id=phonevalidation --package-id=com.idevotion.phonevalidation</pre>


<p>Next we will install and use an <a href="https://github.com/azzamasghar1/ion-intl-tel-input" target="_blank" rel="noreferrer noopener">external library </a>to deal with international phone number (no need to re-invent the wheel).</p>


<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">npm install --force ion-intl-tel-input ionic-selectable flag-icon-css google-libphonenumber --save</pre>


<p>To use this library we need to import it. So we will modify the <strong>home.module.ts </strong>file to import the library and also to use <strong>Reactive Forms</strong> with Angular.</p>


<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule,ReactiveFormsModule } from '@angular/forms';
import { HomePage } from './home.page';
import { HomePageRoutingModule } from './home-routing.module';
import { IonIntlTelInputModule } from 'ion-intl-tel-input';

 @NgModule({
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    IonicModule,
    IonIntlTelInputModule,
    HomePageRoutingModule
  ],
  declarations: [HomePage]
})
export class HomePageModule {}
</pre>


<p>Then we will modify the <strong>home.page.html</strong> to use the library within a reactive form</p>


<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header [translucent]="true">
  &lt;ion-toolbar>
    &lt;ion-title>
      Phone Validation
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content [fullscreen]="true">
  &lt;form [formGroup]="form" (ngSubmit)="onSubmit()">
    &lt;ion-item>
      &lt;ion-label position="floating">Tel Input&lt;/ion-label>
      &lt;ion-intl-tel-input inputPlaceholder="Your phone number"  
      [defaultCountryiso]="FR"
      [preferredCountries]="preferredCountries"
        formControlName="phoneNumber" >
      &lt;/ion-intl-tel-input>
    &lt;/ion-item>
    &lt;ion-button mode="ios" expand="block" color="primary" type="submit">
      Validate
    &lt;/ion-button>
  &lt;/form>
&lt;/ion-content></pre>


<p>and the <strong>home.page.ts</strong> file to declare our form and select some preferred countries.</p>


<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  formValue = {phoneNumber: '', test: ''};
  form: FormGroup;
  preferredCountries = ["fr","gb","us"]
  constructor() { }

  ngOnInit() {
    this.form = new FormGroup({
      phoneNumber: new FormControl({
        value: this.formValue.phoneNumber
      })
    });
  }

  get phoneNumber() { return this.form.get('phoneNumber'); }

  onSubmit() {
    console.log(this.phoneNumber.value);
  }

}
</pre>


<p>To display country flag into the modal list of countries, we need to import a specific css file. To do so, we need to edit our <strong>global.scss</strong> file and add the line</p>


<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@import "node_modules/flag-icon-css/sass/flag-icon.scss";</pre>


<p>Ok so now we can enter a phone number and when clicking on the validate button, the <strong>onSubmit</strong> function will be called  and will display the phone number information</p>


<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{internationalNumber: "+34 695 96 XX XX", nationalNumber: "695 96 XX XX", isoCode: "es", dialCode: "+34"}</pre>


<p>At this point we need to send the phone number information to our Django backend, which will send an SMS with a code that the user will need to enter on a new page to validate his phone number. </p>


<p>Let&#8217;s see first how we can send an sms using Django and a sms provider like <a href="https://www.smsapi.com/en" target="_blank" rel="noreferrer noopener">www.smsapi.com</a> or whatever sms provider you will choose.</p>




<h2>How to send an sms code using Django and SMSAPI ?</h2>


<p>As we learned in previous tutorials how to create and manage a backend using Django (and deploying it using Clever cloud), we will just focus on how to send the Sms code.</p>


<p>To use SMSAPI with Django, we need to edit our requirements.txt file and add the library</p>


<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">smsapi-client==2.4.2</pre>


<p>Then we will create a new endpoint in our API by adding in our urls.py file</p>


<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> url(r'^sendSmsCode/$',
        sendSmsCode,
        name='sendSmsCode'),</pre>


<p>And then we will write the view <strong>sendSmsCode</strong> which will generate a code, send it by sms and return a JSON response to our Ionic application with the code generated:</p>


<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def generateSmsCode():
    # select a random sample without replacement
    codeSms = ''.join(random.choice(string.digits) for _ in range(4))
    return codeSms

@api_view(['GET'])
def sendSmsCode(request):
    from phonevalidation.settings import SMSAPI_API_KEY
    client = SmsApiComClient(access_token=SMSAPI_API_KEY)
    phone = request.GET.get('phone', None)
    code = generateSmsCode()
    message="SMS code:"+code
    try:
      client.sms.send(to=phone, message=message)
      smsSent = SMSSent()
      smsSent.phoneNumber = phone
      smsSent.codeEnvoye = code
      smsSent.save()
      json = {"status":"OK","code": code}
    except Exception as e:
        print(e)
        json = {"status": "KO"}

    return JsonResponse(json)</pre>


<p><em>Of course you need to create and set a valid SMSAPI_API_KEY</em> to use <a href="https://www.smsapi.com/en" target="_blank" rel="noreferrer noopener">smsapi.com</a> service.</p>




<h2>How to check and validate an SMS code using Ionic ?</h2>


<p>Now on our submit method, we can:</p>


<p>. check if  network is available </p>


<p>. call our new API and passed the phone number that the user entered. </p>


<p>. check our JSON response status. If it&#8217;s ok we can go to a new page/url : sms/code otherwise we should display an error to the end user.</p>


<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> onSubmit() {
    console.log(this.phoneNumber.value);
    if (this.apiService.networkConnected){
      let formatedNumber = this.phoneNumber.value.internationalNumber
      this.apiService.sendSmsCode(formatedNumber).subscribe((results)=>{
        if (results){
          let statuts = results["status"]
          if (statuts=="OK"){
            let code = results["code"]
            this.navData.setDataWithoutId({
                  "phoneNumber": formatedNumber,
                  "smscode": code
                })
                this.router.navigate(['/sms-code']).catch(err => console.error(err));
          }
          else{
            //Display error
          }

        }
        else{
          //Display error
        }
      })
    }    
  }</pre>


<p>Now we need to create the page that will ask the sms code received and will verify it with the code sent by our backend.</p>


<figure class="wp-block-image size-large is-resized"><img loading="lazy" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/validateSms-473x1024.png" alt="" class="wp-image-1008" width="237" height="512" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/validateSms-473x1024.png 473w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/validateSms-600x1299.png 600w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/validateSms-139x300.png 139w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/validateSms-709x1536.png 709w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/validateSms-400x866.png 400w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2021/02/validateSms.png 750w" sizes="(max-width: 237px) 100vw, 237px" /></figure>


<p>First let&#8217;s add our new page</p>


<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic g page SmsCode  </pre>


<p>Now let&#8217;s edit our sms-code.page.html </p>


<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header class="ion-no-border" mode="ios">
  &lt;ion-toolbar mode="ios">
 &lt;ion-buttons slot="start">
   &lt;ion-back-button defaultHref="home">&lt;/ion-back-button>
 &lt;/ion-buttons>
 &lt;ion-title>Code&lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content>
  &lt;div class="page-flex-align code">
    &lt;div class="top-content ion-text-center">
      &lt;h1>Validation code&lt;/h1>
      &lt;p>Please enter the code send to 
        &lt;br>Phone: {{phoneNumber}}&lt;/p>
      &lt;ion-list mode="ios" formCodeValidate lines="none">
        &lt;ion-item >
          &lt;ion-input #codesInpunt0 (keyup)="changeFocus(1)"  [(ngModel)]="code[0]" type="tel" maxlength="1"  >&lt;/ion-input>
        &lt;/ion-item>
        &lt;ion-item >
          &lt;ion-input #codesInpunt1 (keyup)="changeFocus(2)" [(ngModel)]="code[1]" type="tel" maxlength="1"  >&lt;/ion-input>
        &lt;/ion-item>
        &lt;ion-item >
          &lt;ion-input #codesInpunt2 (keyup)="changeFocus(3)" [(ngModel)]="code[2]" type="tel" maxlength="1"  >&lt;/ion-input>
        &lt;/ion-item>
        &lt;ion-item >
          &lt;ion-input #codesInpunt3  (keyup)="changeFocus(4)"   [(ngModel)]="code[3]" type="tel" maxlength="1"  >&lt;/ion-input>
        &lt;/ion-item>
      &lt;/ion-list>

    &lt;/div>
  &lt;/div>
&lt;/ion-content>

</pre>


<p>and the sms.code.page.scss tohave our digit input design</p>


<pre class="EnlighterJSRAW" data-enlighter-language="scss" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
ion-list {
    &amp;[form] {
        ion-item {
            &amp;.item {
                --border-width: 1px;
                --border-radius: 5px;
                --min-height: 60px;
                --ion-border-color: rgba(0, 0, 0, 0.10);

                ion-select {
                    --padding-start: 0;
                    width: 100%;
                    //font-size: 1.8rem;
                    max-width: unset;
                }

                &amp;+.item {
                    margin-top: 16px;
                }
            }

        }

        ion-row {
            &amp;[lrm] {
                margin-left: -8px;
                margin-right: -8px;
            }

            ion-col {
                padding: 8px;
                padding-top: 16px;
            }
        }
    }

 &amp;[formCodeValidate] {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    -webkit-box-pack: justify;
    -ms-flex-pack: justify;
    justify-content: space-between;
    max-width: 310px;
    margin-left: auto;
    margin-right: auto;

    ion-item {
        &amp;.item {
            --border-width: 1px;
            --border-radius: 5px;
            --min-height: 60px;
            max-width: 55px;
            text-align: center;
            font-size: 18px;
        }

    }
    }
}</pre>


<p>and the sms-code.page.ts to verify if the code entered by the user is matching the code sent by our backend</p>


<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { ChangeDetectorRef, Component, NgZone, OnInit, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { Platform, ToastController } from '@ionic/angular';
import { NavDataServiceService } from '../services/nav-data-service.service';

@Component({
  selector: 'app-sms-code',
  templateUrl: './sms-code.page.html',
  styleUrls: ['./sms-code.page.scss'],
})
export class SmsCodePage implements OnInit {

  phoneNumber= ''
  verificationId="1245";
  code = Array();
  started=false;
  verificationInProgress=false;
  @ViewChild('codesInpunt0') codesInpunt0;
  @ViewChild('codesInpunt1') codesInpunt1;
  @ViewChild('codesInpunt2') codesInpunt2;
  @ViewChild('codesInpunt3') codesInpunt3;

  constructor(
    public router: Router,
    public navData:NavDataServiceService,
    public platform : Platform,
    public toastCtrl: ToastController) {
      this.started=false;
  }

  ngOnInit() {

      let data = this.navData.getDataWithoutId()
      this.phoneNumber = data['phoneNumber'];
      this.verificationId = data['smscode'];
      console.log("route special "+this.phoneNumber+" verif code "+this.verificationId)

  }

  ionViewDidEnter() {
    this.codesInpunt0.setFocus();
  } 

  changeFocus(inputToFocus) {
    switch (inputToFocus) {
      case 1:
        this.codesInpunt1.setFocus();
        break;

      case 2:
        this.codesInpunt2.setFocus();
        break;

      case 3:
        this.codesInpunt3.setFocus();
        break;
       case 4:
        let enteredCode = this.code[0]+this.code[1] + this.code[2] + this.code[3]   ;
        this.resetCode()
        if (this.verificationInProgress==false){
          this.verificationInProgress=true;
          this.activate(enteredCode)
        }
       break;

    }

  }

  activate(enteredCode) {
    if (enteredCode){
      console.log("Compare code sms "+enteredCode+" avec "+this.verificationId)
      if (enteredCode.length == 4) {
        console.log(enteredCode);
        console.log("veificationCode is" + this.verificationId);
        if (enteredCode==this.verificationId){
          //Good job
        }
        else{

          this.presentToastError()
        }
      }
      else{
        this.presentToastError()
      }
    }
    else{
      this.presentToastAgain()
    }
  }

  resetCode(){
      this.code[0]="";
      this.code[1]="";
      this.code[2]="";
      this.code[3]="";

  }

  async presentToastAgain(){
    let toast = await this.toastCtrl.create({
      message: "Please enter code again",
      duration: 2000,
      position: 'bottom'
    });

    toast.present();
    this.verificationInProgress=false;
    this.resetCode()
    this.codesInpunt0.setFocus();
  }

  async presentToastError() {
    this.verificationInProgress=false;
    this.codesInpunt0.setFocus();
    let toast = await this.toastCtrl.create({
      message: "Code invalid",
      duration: 2000,
      position: 'bottom'
    });
   toast.present();
  }

}
</pre>


<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-validate-a-phone-number-by-sending-an-sms-code-using-ionicdjango-and-smsapi/">How to validate a phone number by sending an SMS code using Ionic,Django and SmsApi ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to scan QR Code and Barcode with Ionic &#038; Capacitor ?</title>
		<link>https://www.ionicanddjangotutorial.com/ionic-qrcode-scanning/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Fri, 27 Nov 2020 14:17:38 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=886</guid>

					<description><![CDATA[<p>In this tutorial we will learn how to scan a QR code or a barcode with Ionic / Capacitor and&#8230; <a href="https://www.ionicanddjangotutorial.com/ionic-qrcode-scanning/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/ionic-qrcode-scanning/">How to scan QR Code and Barcode with Ionic &#038; Capacitor ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this tutorial we will learn how to scan a QR code or a barcode with Ionic / Capacitor and then use the <a rel="noreferrer noopener" href="https://world.openfoodfacts.org/data" target="_blank">Open Food Fact API </a>to display information about the scanned product. </p>



<p>You can find the source code of this tutorial <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/qrcodescanner" target="_blank" rel="noreferrer noopener"> here</a> </p>



<span id="more-886"></span>



<h2>Creating our Ionic / Capacitor project</h2>



<p>To create our application we need to type in a terminal, the following command</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start qrcodescanner blank --capacitor --project-id=qrcodescanner --package-id=com.idevotion.qrcodescanner</pre>



<p>This will generate our Ionic project, then we can go in the created directory and launch our first build to verify everything is ok</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic build</pre>



<h2>Install Barcode Scanner library and Capacitor Http library</h2>



<p>Now we need to install the <a href="https://ionicframework.com/docs/native/barcode-scanner">Barcode Scanner </a><a href="https://ionicframework.com/docs/native/barcode-scanner" target="_blank" rel="noreferrer noopener">library</a> to use it with Ionic and Capacitor, and we will use the <a href="https://github.com/capacitor-community/http" target="_blank" rel="noreferrer noopener">Capacitor HTTP plugin</a> to do our request to the Open Food Fact API, so we need to install it as well.</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">npm install phonegap-plugin-barcodescanner
npm install @ionic-native/barcode-scanner
npm install @capacitor-community/http</pre>



<p>To use the barcodescanner plugin, we need to edit our <strong>app.module.ts</strong> file and import it</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { BarcodeScanner } from '@ionic-native/barcode-scanner/ngx';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
  providers: [
    StatusBar,
    SplashScreen,
    BarcodeScanner,
   
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}
</pre>






<h2>Using the Barcodescanner library with Ionic 5</h2>



<p>To use both libraries , we need to edit the default generated  <strong>home.page.ts</strong> file and import it as well</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component } from '@angular/core';
import { BarcodeScanner } from '@ionic-native/barcode-scanner/ngx';
import '@capacitor-community/http';
import { Plugins } from '@capacitor/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  productInformation=null
  barCode:any;
  constructor(public barcodeScanner : BarcodeScanner) {}

}
</pre>



<p>Let&#8217;s edit the <strong>home.page.html</strong> file to add a button that will launch the barcode scanner. </p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header>
  &lt;ion-toolbar color="primary">
    &lt;ion-title>
      Barcode Scanner tutorial
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content>
  &lt;ion-card>
    &lt;img src="productInformation.product.image_url"/>
    &lt;ion-card-content>
      &lt;ion-text>Scan a QRCode or Barcode to get the product information&lt;/ion-text>

      &lt;ion-button color="success" expand="full" (click)="openBarCodeScanner()">Scan code&lt;/ion-button>
    &lt;/ion-card-content>
  &lt;/ion-card>

  &lt;ion-grid>
    &lt;ion-row>
      &lt;ion-col class="justify-align-center"  *ngIf="barCode">
        &lt;ion-text>{{barCode}}&lt;/ion-text>
      &lt;/ion-col>
    &lt;/ion-row>
    &lt;ion-row>
      &lt;ion-col class="justify-align-center"  *ngIf="productInformation">
        &lt;ion-text>{{productInformation.product.product_name}} &lt;/ion-text>
      &lt;/ion-col>
    &lt;/ion-row>
  &lt;/ion-grid>
&lt;/ion-content></pre>



<p>The page will look like this</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/IMG_5E65B6530981-1-576x1024.jpeg" alt="scan barcode with ionic" class="wp-image-891" width="288" height="512" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/IMG_5E65B6530981-1-576x1024.jpeg 576w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/IMG_5E65B6530981-1-600x1067.jpeg 600w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/IMG_5E65B6530981-1-169x300.jpeg 169w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/IMG_5E65B6530981-1-400x711.jpeg 400w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/IMG_5E65B6530981-1.jpeg 750w" sizes="(max-width: 288px) 100vw, 288px" /></figure>



<p>If a code is detected it will be displayed in a row with all information related to the scan</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> &lt;ion-row>
      &lt;ion-col class="justify-align-center"  *ngIf="barCode">
        &lt;ion-text>{{barCode}}&lt;/ion-text>
      &lt;/ion-col>
    &lt;/ion-row></pre>



<p>Once a code is detected, we will call Open Food fact API to get information about the scan product. <a href="https://fr-en.openfoodfacts.org/" target="_blank" rel="noreferrer noopener">Open Food Fact</a> is an organisation referencing food products, so to get information you will need to scan something related to food. The product name will be displayed on another row</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> &lt;ion-row>
      &lt;ion-col class="justify-align-center"  *ngIf="productInformation">
        &lt;ion-text>{{productInformation.product.product_name}} &lt;/ion-text>
      &lt;/ion-col>
    &lt;/ion-row></pre>



<p>Ok now we can go back to the <strong>home.page.ts</strong> file and implement the <strong>openBarCodeScanner() </strong>method</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">openBarCodeScanner(){
    this.barcodeScanner.scan().then(barcodeData => {
      console.log('Barcode data', barcodeData);
      this.barCode = barcodeData
      //   Barcode data {"cancelled":0,"text":"8413384010008","format":"EAN_13"}
    }).catch(err => {
      console.log('Error', err);
    });
  }</pre>



<p>At this stage, if you try to scan something on a real device, you should see the Barcode scanner view and then obtain the result in barcodeData variable.</p>



<h2>Using Capacitor HTTP plugin to call Open Food Fact API</h2>



<p>Now if we have a result meaning a scanned bar code, we can make our call to the Open Food Fact API, to check if we have information about the product. Let&#8217;s modify our code to do so:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
  openBarCodeScanner(){
    this.barcodeScanner.scan().then(barcodeData => {
      console.log('Barcode data', barcodeData);
      this.barCode = barcodeData
      //   Barcode data {"cancelled":0,"text":"8413384010008","format":"EAN_13"}

      if (barcodeData) {
        let scanCode = barcodeData["text"];
        if (scanCode) {
          this.getProductWithBarCode(scanCode) 
        }
      }
      else{
        console.log("=== No data scanned !")
      }
    }).catch(err => {
      console.log('Error', err);
    });
  }</pre>



<p>If we have barcodeData, we can get the scan code like this</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">let scanCode = barcodeData["text"];</pre>



<p>and then call the API </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> if (scanCode) {
          this.getProductWithBarCode(scanCode) 
 }</pre>



<p>Let&#8217;s implement this <strong>getProductWithBarCode</strong> method</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
  async getProductWithBarCode(barcode){
    const { Http } = Plugins;
    let urlToCall = "https://fr.openfoodfacts.org/api/v0/produit/"+barcode+".json"
    let ret = await Http.request({
      method: 'GET',
      url: urlToCall,
      headers: {
        'content-type': 'application/json',
      }
    });
    console.log(ret.data)
    if (ret.data){
      this.productInformation = ret.data
      console.log("==Product ",this.productInformation.product)
    }
  }</pre>



<p>Nothing really complicated. We use the Capacitor HTTP plugin to make a GET request and passing the scanned bar code.</p>



<p>If we have a result, we can set the product information and displays it in our page.</p>



<p>The Open Food Fact API returns a JSON with all information collected about the product so it can be a very long json. It&#8217;s up to you to extract the information you are interested in and display it or not.</p>



<h2>Deploying on a device</h2>



<p>To deploy on iOS:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic build --prod
npx cap sync ios
npx cap open ios</pre>



<p>Xcode will open and you can run it on your iPhone.</p>



<p>To deploy on Android:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic build --prod
npx cap sync android
npx cap open android</pre>



<p>As always with Capacitor plugin, one more step is required with Android. You need to register the HTTP plugin</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.getcapacitor.plugin.http.Http;

public class MainActivity extends BridgeActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Initializes the Bridge
    this.init(savedInstanceState, new ArrayList&lt;Class&lt;? extends Plugin>>() {{
      // Additional plugins you've installed go here
      // Ex: add(TotallyAwesomePlugin.class);
      add(Http.class);
    }});
  }
}</pre>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/ionic-qrcode-scanning/">How to scan QR Code and Barcode with Ionic &#038; Capacitor ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to secure Ionic application and Django API with JWT authentication ?</title>
		<link>https://www.ionicanddjangotutorial.com/secure-api-for-ionic-application/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Fri, 06 Nov 2020 12:49:12 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=644</guid>

					<description><![CDATA[<p>Want to learn how to secure a Ionic application using JWT authentication on backend with Django ? This tutorial is&#8230; <a href="https://www.ionicanddjangotutorial.com/secure-api-for-ionic-application/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/secure-api-for-ionic-application/">How to secure Ionic application and Django API with JWT authentication ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Want to learn how to secure a Ionic application using JWT authentication on backend with Django ? This tutorial is for you ! We will see how to protect our API on backend but also how to automatically refresh expired tokens in our Ionic application to avoid logged out users.</p>



<p>The source code repository is <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/secureionic" target="_blank" rel="noreferrer noopener">here</a> </p>



<span id="more-644"></span>



<h2>Add JWT authentication to Django API</h2>



<p><strong>JWT (Json Web Token)</strong> is an authentication system based on secured token. To obtain a token, the user needs to provide his credentials. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token.</p>



<p><a href="https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html" target="_blank" rel="noreferrer noopener">Django Simple JWT</a> is a simple package to manage JWT authentication with Django. </p>



<p><a href="https://djoser.readthedocs.io/en/latest/" target="_blank" rel="noreferrer noopener">Django DJOSER</a> is another package with more features and which include Django Simple JWT. So we will use this package.</p>



<p>I&#8217;m assuming that you already know Django and have an existing project. To add Django DJOSER and required packages to our Django project, we add them to our requirements.txt file</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">djoser
djangorestframework_simplejwt
djangorestframework==3.11.0</pre>



<p> Then we can reference them in our INSTALLED_APP dictionnary</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">INSTALLED_APPS = [
      ... 
    'rest_framework',
    'djoser',
   ....
]</pre>



<p>and declare in our urls.py of our main django project the DJOSER endpoints</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">urlpatterns = [
    ...
    path('auth/', include('djoser.urls')),
    path('auth/', include('djoser.urls.jwt')),
    ...
]+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
</pre>



<p>Ok now with all that in place, we can use Django DJOSER to get our JWT token. Available endpoints can be found in the <a href="https://djoser.readthedocs.io/en/latest/getting_started.html#available-endpoints" target="_blank" rel="noreferrer noopener">documentation</a> but we will focus on the:</p>



<ul><li><code>/jwt/create/</code>&nbsp;(JSON Web Token Authentication)<ul><li>Use to authenticate and obtain a JWT TOKEN</li></ul></li><li><code>/jwt/refresh/</code>&nbsp;(JSON Web Token Authentication)<ul><li>Use to refresh an expired JWT Token</li></ul></li><li><code>/jwt/verify/</code>&nbsp;(JSON Web Token Authentication)<ul><li>Use to verify if a JWT Token is still valid</li></ul></li></ul>



<p>Usually JWT Tokens expires very quickly (5 minutes). It&#8217;s possible to modify this expiration value in the <a href="https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html" target="_blank" rel="noreferrer noopener">settings</a> </p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),</pre>



<p>But it&#8217;s not recommended to increase this value a lot. This is why a <strong>/verify </strong>endpoint is provided: to check if the token has expired or not. When a token expires, we can obtain a new one using the refresh token that was provided at the same time than the JWT token. This is the purpose of the<strong> /create</strong> endpoint. Let&#8217;s have a closer look. Imagine we have an existing user and we want to authenticate to obtain a JWT token. We can call the endpoint with a POST requests containing our user credentials:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X POST \
  https://app-4d83da5d-f938-46bf-8359-4d0c2fb1f9e7.cleverapps.io/auth/jwt/create/ \
  -H 'Content-Type: application/json' \
  -d '{"email": "myuser@gmail.com", "password": "mypassword"}'</pre>



<p>If credentials are correct, we will get a response with our tokens:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{
    "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwNDY1MzQyOSwianRpIjoiMGQyMTFjOWYwMzc5NDUwN2JiZTgzNTIxYjE3OTllNjMiLCJ1c2VyX2lkIjoiNWQ3MWY5N2QtNGU1Mi00NzQxLWEzZmItMzk5N2EyYTkyMWJjIn0.hXGIr_baGgMhjpFDlP0Aryf5LTWG6riHVX6B93RqYC0",
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjA0NjUzMTI5LCJqdGkiOiIxNmFjYjUxOWZjY2Y0ZWFmOWI2MjMwOWQzNmNjMmI3YyIsInVzZXJfaWQiOiI1ZDcxZjk3ZC00ZTUyLTQ3NDEtYTNmYi0zOTk3YTJhOTIxYmMifQ.Y50lo6KKlEY8v2uW5myKqaITWs_1pizIMlyx26kaHgU"
}</pre>



<p>The response contains the <strong>access</strong> token (our JWT token) and the <strong>refresh</strong> token to use when the access token expires.</p>



<p>If credentials are not correct, the endpoint will return an HTTP 401 response (unauthorised).</p>



<p>If we wait 5 minutes and check the access token validity we should obtain an HTTP 401 unauthorised response meaning the token is not valid anymore.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X POST \
  https://app-4d83da5d-f938-46bf-8359-4d0c2fb1f9e7.cleverapps.io/auth/jwt/verify/ \
  -H 'Content-Type: application/json' \
  -d '{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjA0NjUzMTI5LCJqdGkiOiIxNmFjYjUxOWZjY2Y0ZWFmOWI2MjMwOWQzNmNjMmI3YyIsInVzZXJfaWQiOiI1ZDcxZjk3ZC00ZTUyLTQ3NDEtYTNmYi0zOTk3YTJhOTIxYmMifQ.Y50lo6KKlEY8v2uW5myKqaITWs_1pizIMlyx26kaHgU"}'</pre>



<p>At this point we can use the <strong>/refresh </strong>endpoint and send the refresh token to get a new access token:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X POST \
  https://app-4d83da5d-f938-46bf-8359-4d0c2fb1f9e7.cleverapps.io/auth/jwt/refresh/ \
  -H 'Content-Type: application/json' \
  -d '{"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwNDY1MzQyOSwianRpIjoiMGQyMTFjOWYwMzc5NDUwN2JiZTgzNTIxYjE3OTllNjMiLCJ1c2VyX2lkIjoiNWQ3MWY5N2QtNGU1Mi00NzQxLWEzZmItMzk5N2EyYTkyMWJjIn0.hXGIr_baGgMhjpFDlP0Aryf5LTWG6riHVX6B93RqYC0"}'</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjA0NjUzMTc1LCJqdGkiOiI1Y2VmODc2OTFlODc0MzhkYjQ3OTI3ZmRkN2M3M2Y4NCIsInVzZXJfaWQiOiI1ZDcxZjk3ZC00ZTUyLTQ3NDEtYTNmYi0zOTk3YTJhOTIxYmMifQ.KHIDvKPwqCU0nPAHxbHv9ElISG1nvjH4VH7EBdiV_Ig", 
}</pre>



<p>Ok the response contains the new access token to use. The refresh token has also a lifetime that can be configured </p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),</pre>



<p>The value is a little bit more bigger (one day) but what happens after one day ? At this moment, the <strong>/refresh</strong> endpoint will return an HTTP 401 unauthorised response meaning there is no other solution to logout the user to ask him it&#8217;s credentials and then to begin a new cycle again by calling <strong>/create</strong> endpoint.</p>






<p>Ok but what the f&#8230;k here ? Don&#8217;t forget we want to add authentication in our Ionic application. We are not going to logout users every day. The user experience will be terrible&#8230; One solution could be to increase the refresh token validity such as one year:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=365),</pre>



<p>Meaning that every year our mobile application users would have to login again. Why not. </p>



<p>Another solution is to modify the configuration to obtain an access <strong>and</strong> refresh token on <strong>/refresh</strong> endpoint. with the <strong>ROTATE_REFRESH_TOKENS</strong> option </p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=180),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
</pre>



<p>With that in place we can always obtain a new access . We just need to save/replace both values (access and refresh tokens) and use it. Of course if a user don&#8217;t connect to the mobile application during 6 months (our refresh token lifetime), the refresh token will be expired and then we will have no other solution to logout user to ask him again it&#8217;s credentials. </p>



<h2>Secure our Ionic application with JWT</h2>



<p>Now that we have seen how to manage and obtain a JWT token from our django backend, let&#8217;s learn how to implement and use it within our ionic application.</p>



<p>I will assume that you already have a Ionic project ready to use with login and register pages. If not you can generate a new one with </p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start secureionic blank --capacitor --project-id=secureionic --package-id=com.idevotion.secureionic</pre>



<p>To shorten this tutorial, i will focus on main features but you will find the full source code on my <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/secureionic" target="_blank" rel="noreferrer noopener">github repository</a> as usual.</p>



<p>First step, we need to create a user and then we need to obtain a JWT token for the created user. Django Djoser exposes the <code><strong>/users/</strong></code> endpoint with a POST query to let us create users. Of course this endpoint can be called without authentication because at this stage we don&#8217;t have one. </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> let params = {
        "email": email,
        "password": password,
        "first_name": firstName,
        "last_name": lastName
      }
 this.createAccount(params)</pre>



<p>The email, password, firstName and lastName are parameters coming from our register form page.</p>



<p>Then we can send a request to create the account and once done to obtain the JWT credentials by login the user:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">createAccount(params) {
    if (this.apiService.networkConnected) {
      // Account creation
      this.apiService.registerUser(params).subscribe((resultat) => {
        let status = resultat["status"];
        if (status=="OK") {
          let data = resultat["data"]
          this.userManager.setUser(data)
            // create a token
              // Check email
              let paramsToLogin = {
                "email": params.email,
                "password": params.password,
              }
              this.authentificationService.login(paramsToLogin).then((resultat) => {
                this.apiService.stopLoading();
                if (resultat) {
                   //Authentified
                   this.router.navigateByUrl("/home")
                }
                else {
                  this.apiService.showError(this.translateService.instant("A technical error occured. Account creation impossible"))
                }
              })
        }
        else {
          this.apiService.stopLoading();
          let error = resultat["error"]
          if (error.status==400){
            this.apiService.showError(this.translateService.instant("An account already exists with this email. Please login !"))
          }
          else{
            this.apiService.showError(this.translateService.instant("A technical error occured. Account creation impossible"))
          }
         
        }
      })
    }
    else {
      this.apiService.showNoNetwork()
    }
  }</pre>



<p>The <strong>registerUser</strong> method is doing a POST query to the endpoint /auth/users and then return the results</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> registerUser(params){
        const options = {
            headers: new HttpHeaders({
                'Content-Type': 'application/json'
            })
        };
        this.getUserUrl  =  this.virtualHostName + "auth/users/"
        return new Observable(observer=>{
            this.http.post(this.getUserUrl, params, options).subscribe(
                (val) => {
                    observer.next({"status":"OK","data":val})
                    observer.complete()
                 },
                 response => {
                     console.log("POST call in error", response);
                     observer.next({"status":"KO","error":response})
                     observer.complete()
                 },
               
                () => {
                   
                });
        })
    }
</pre>



<p>Ok now that the user has been created, we need to obtain our JWT tokens (access and refresh) and store them. This is the role of the AuthenticationService that we will see soon. But first let&#8217;s implement our api login service</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">  login(params){
        const options = {
            headers: new HttpHeaders({
                'Content-Type': 'application/json'
            })
        };
         this.getLoginUrl =  this.virtualHostName + "auth/jwt/create/"
        return new Observable(observer=>{
            this.http.post(this.getLoginUrl, params, options).subscribe(
                (val) => {
                    observer.next(val)
                    observer.complete()
                 },
                 response => {
                     console.log("POST call in error", response);
                     observer.next()
                     observer.complete()
                 },
                () => {
                   
                });
        })
        
    }</pre>



<p> We call our <strong>/auth/jwt/create</strong> endpoint with our user credentials and if successfull we will get our access and refresh tokens that we need to save. We can use <strong><a href="https://capacitorjs.com/docs/apis/storage" target="_blank" rel="noreferrer noopener">Capacitor storage</a></strong> to do that.</p>



<p>The goal of the Authentication service is to manage these tokens, to save them, to logout our users or to refresh the tokens. Let&#8217;s implement it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, tap, switchMap } from 'rxjs/operators';
import { BehaviorSubject, from, Observable, Subject } from 'rxjs';
 
import { Plugins } from '@capacitor/core';
import { ApiserviceService } from './apiservice.service';
const { Storage } = Plugins;
 
const TOKEN_KEY = 'access';
const REFRESH_TOKEN_KEY = 'refresh';

 
@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
  // Init with null to filter out the first value in a guard!
  isAuthenticated: BehaviorSubject&lt;boolean> = new BehaviorSubject&lt;boolean>(null);
  token = '';
  refresh=''
  constructor(private apiService:ApiserviceService) {
    this.loadToken();
  }
 
  loadToken() {
    this.apiService.getLocalData(TOKEN_KEY).then((value:string)=>{
      if (value){
        console.log('set token: ', value);
        this.token = value["access"];
        this.apiService.getLocalData(REFRESH_TOKEN_KEY).then((value:string)=>{
          this.refresh=value["refresh"]
          this.isAuthenticated.next(true);
        })       
      }
      else{
        console.log("=PAS DE TOKEN PAS LOGGE")
        this.isAuthenticated.next(false);
      }
    })
   
  }

  login(params){
    return new Promise(async resolve => {
    this.apiService.login(params).subscribe((resultat)=>{
       if (resultat) {
          let accessToken = resultat["access"]
          let refreshToken = resultat["refresh"]
          this.apiService.setLocalData("access",{"access":accessToken})
          this.apiService.setLocalData("refresh",{"refresh":refreshToken})
          this.token = accessToken;
          this.refresh=refreshToken
          this.isAuthenticated.next(true);
         
         resolve(true)
        }
      else{
        resolve(false)
      }
      })
    })
  }
 
  logout(): Promise&lt;void> {
    this.isAuthenticated.next(false);
    return new Promise(async resolve => {
       this.apiService.removeLocalData(TOKEN_KEY).then(()=>{
          this.apiService.removeLocalData(REFRESH_TOKEN_KEY).then(()=>{
            resolve()
          }
        )
       });
    })
  }

  refreshToken(){
    return this.apiService.refreshToken(this.refresh)
  }
}</pre>



<p>On our login page, the process is simple, we just need to verify that the credentials are correct and then save them. So we can use our same Authentication service. </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">async login(){
   
      let email = this.credentials.value["email"]
      let password = this.credentials.value["password"]
      
      if (this.apiService.networkConnected){
        this.apiService.showLoading();
            // Check email
            let params = {
              "email":email,
              "password":password,
            }
            this.authentificationService.login(params).then((resultat)=>{
              this.apiService.stopLoading();
              if (resultat) {
                this.router.navigateByUrl('/home', { replaceUrl: true });
              }
              else{
                this.displayWrongLogin()
            }
          })
            
      }
      else {
        this.apiService.showNoNetwork();
      }
    }</pre>






<p>Ok now we have seen how to register or login a user and then go to our home page. But we still have lot of work to do. </p>



<p>First of all, when launching the app, we need to verify if the user is already login and them redirect him directly to the home page or if not redirect him to the login or register pages (your choice). To do so, we will modify our routing file and introduce some guards.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
    canLoad: [AuthGuard] 
  },
  {
    path: 'login',
    loadChildren: () => import('./pages/login/login.module').then( m => m.LoginPageModule),
    canLoad: [AutoLoginGuard]
  },
  {
    path: 'register',
    loadChildren: () => import('./pages/register/register.module').then( m => m.RegisterPageModule),
    canLoad: [AutoLoginGuard]
  },
  {
    path: '',
    redirectTo: 'register',
    pathMatch: 'full'
  }
];</pre>



<p>The <strong>AuthGuard</strong> will check that our user is authentified and can access the page. The <strong>AutoLoginGuard</strong> will check if the user is already identified and if so redirects him to the home page automatically (no need to login or register again). You can learn more <a href="https://devdactic.com/ionic-5-navigation-with-login/" target="_blank" rel="noreferrer noopener">here</a> thanks to Simon incredible tutorials.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { UserManagerProviderService } from './../services/user-manager-provider.service';
import { AuthenticationService } from './../services/authentication.service';
import { Injectable, NgZone } from '@angular/core';
import { CanLoad, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { filter, map, take } from 'rxjs/operators';
 
@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanLoad {
  constructor(private authService: AuthenticationService,private userManager:UserManagerProviderService,private router: Router) { }
 
  canLoad(): Observable&lt;boolean> {    
    return this.authService.isAuthenticated.pipe(
      filter(val => val !== null), // Filter out initial Behaviour subject value
      take(1), // Otherwise the Observable doesn't complete!
      map(isAuthenticated => {
        if (isAuthenticated) {  
          this.userManager.getUser().then((user)=>{
            
          })        
          return true;
        } else {          
          this.router.navigateByUrl('/login')
          return false;
        }
      })
    );
  }
}</pre>



<p>If we have a token the user is authentified and if not we redirect to the login page. Same behaviour for AutoLoginGuard</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { UserManagerProviderService } from './../services/user-manager-provider.service';
import { Injectable } from '@angular/core';
import { CanLoad, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from '../services/authentication.service';
import { filter, map, take } from 'rxjs/operators';
 
@Injectable({
  providedIn: 'root'
})
export class AutoLoginGuard implements CanLoad {
  constructor(private authService: AuthenticationService, private router: Router,private userManager:UserManagerProviderService) { }
 
  canLoad(): Observable&lt;boolean> {    
    return this.authService.isAuthenticated.pipe(
      filter(val => val !== null), // Filter out initial Behaviour subject value
      take(1), // Otherwise the Observable doesn't complete!
      map(isAuthenticated => {
        console.log('Found previous token ?, automatic login '+isAuthenticated);
        if (isAuthenticated) {
          //Load user 
          this.userManager.getUser().then((user)=>{
            console.log("Log user",user)
            
             // Directly open inside area       
           this.router.navigateByUrl('/home', { replaceUrl: true });
          })
         
        } else {          
          // Simply allow access to the login
          return true;
        }
      })
    );
  }
}</pre>



<p>Ok we have all logic in place except for the most important part !</p>



<p>To see the problem let&#8217;s display the information of the logged in user:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component } from '@angular/core';
import { ApiserviceService } from 'src/app/services/apiservice.service';
import { UserManagerProviderService } from 'src/app/services/user-manager-provider.service';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  infoAboutMe : any;
  title = "Authenticated"
  constructor(public userManager:UserManagerProviderService,
    public apiService:ApiserviceService) {
   

    //Get info 
    this.apiService.getUserMe().subscribe((data)=>{
      if (data["status"]=="OK"){
        this.infoAboutMe = data;
      }
      else{
        this.title ="Unauthenticated"
        this.infoAboutMe=null;
      }
    
    })  
  }
}
</pre>



<p>and our home.page.html</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header [translucent]="true">
  &lt;ion-toolbar>
    &lt;ion-title>
      eShop
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content [fullscreen]="true">
  &lt;ion-header collapse="condense">
    &lt;ion-toolbar>
      &lt;ion-title size="large">{{title}}&lt;/ion-title>
    &lt;/ion-toolbar>
  &lt;/ion-header>
  &lt;div *ngIf="infoAboutMe">
    Info about me:&lt;br>&lt;br>
    &lt;ion-label>Email: {{infoAboutMe.data.email}}&lt;/ion-label>&lt;br>
    &lt;ion-label>ID: {{infoAboutMe.data.id}}&lt;/ion-label>
  &lt;/div>
&lt;/ion-content>
</pre>



<p>Just after creating or login you should see</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="518" height="869" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124331.png" alt="Ionic JWT authenticated page" class="wp-image-689" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124331.png 518w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124331-179x300.png 179w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124331-400x671.png 400w" sizes="(max-width: 518px) 100vw, 518px" /></figure>



<p>Well nice. Now wait 5 minutes, and try to access again </p>



<figure class="wp-block-image size-large"><img loading="lazy" width="1024" height="436" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124858-1024x436.png" alt="Ionic unauthenticated" class="wp-image-696" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124858-1024x436.png 1024w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124858-600x255.png 600w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124858-300x128.png 300w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124858-768x327.png 768w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124858-1536x654.png 1536w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124858-2048x872.png 2048w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/image_2020-11-06_124858-400x170.png 400w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>Remember that tokens expire ? Here we are&#8230; The backend returns an HTTP 401 response code. Obviously we need to manage this behaviour. </p>



<h2>How to refresh expired JWT tokens automatically with Ionic ?</h2>



<p>We could modify every request code of our existing api (if any of course) to check the status return code and then make appropriate actions : call the refresh endpoint , obtain a new token and then make the request again. But it&#8217;s a lot to manage !</p>



<p>A better way to manage this is to use Angular Http Interceptors to do the job. I found this useful <a href="https://valor-software.com/articles/json-web-token-authorization-with-access-and-refresh-tokens-in-angular-application-with-node-js-server.html" target="_blank" rel="noreferrer noopener">article</a> and adapt it a little bit.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { UserManagerProviderService } from './user-manager-provider.service';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, filter, switchMap, take } from 'rxjs/operators';
import { AuthenticationService } from './authentication.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  private refreshingInProgress: boolean;
  private accessTokenSubject: BehaviorSubject&lt;string> = new BehaviorSubject&lt;string>(null);

  constructor(private authService: AuthenticationService,
    private userManager: UserManagerProviderService,
    private router: Router) { }

  intercept(req: HttpRequest&lt;any>, next: HttpHandler): Observable&lt;HttpEvent&lt;any>> {
    const accessToken = this.authService.token;
    // console.log("=== Intercept token ")
    // console.log(accessToken)
    return next.handle(this.addAuthorizationHeader(req, accessToken)).pipe(
      catchError(err => {
        // in case of 401 http error
        console.log("==== ERROR 401 url:" + req.url)
        if (req.url.indexOf("/refresh") > 0) {
          //REFRESHTOKEN KO 
          console.log("==== 401 LOGOUT CAR ERROR")
          // otherwise logout and redirect to login page
          return this.logoutAndRedirect(err);
        }
        if (err instanceof HttpErrorResponse &amp;&amp; err.status === 401) {
          // get refresh tokens
          const refreshToken = this.authService.refresh;

          // if there are tokens then send refresh token request
          if (refreshToken &amp;&amp; accessToken) {
            return this.refreshToken(req, next);
          }

          // otherwise logout and redirect to login page
          return this.logoutAndRedirect(err);
        }

        // in case of 403 http error (refresh token failed)
        if (err instanceof HttpErrorResponse &amp;&amp; err.status === 403) {
          // logout and redirect to login page
          return this.logoutAndRedirect(err);
        }
        // if error has status neither 401 nor 403 then just return this error
        return throwError(err);
      })
    );
  }

  private addAuthorizationHeader(request: HttpRequest&lt;any>, token: string): HttpRequest&lt;any> {
    if (token) {
      let bearer = <code data-enlighter-language="generic" class="EnlighterJSRAW">Bearer ${token}</code>;
      //console.log("=== Clone request et add header avec token ",bearer)

      return request.clone({ setHeaders: { Authorization: bearer } });
    }

    return request;
  }

  private logoutAndRedirect(err): Observable&lt;HttpEvent&lt;any>> {
    console.log("==== LOGOUT CAUSE ERROR")
    this.userManager.logoutUser().then(() => {

    })
    this.router.navigateByUrl('/login');
    return throwError(err);
  }

  private refreshToken(request: HttpRequest&lt;any>, next: HttpHandler): Observable&lt;HttpEvent&lt;any>> {
    if (!this.refreshingInProgress) {
      this.refreshingInProgress = true;
      this.accessTokenSubject.next(null);

      return this.authService.refreshToken().pipe(
        switchMap((res) => {
          this.refreshingInProgress = false;
          this.authService.token = res["access"]
          this.authService.refresh = res["refresh"]
          console.log("==== REFRESH SET VALUES")

          this.accessTokenSubject.next(res["access"]);
          // repeat failed request with new token
          return next.handle(this.addAuthorizationHeader(request, res["access"]));
        }), catchError((err, caught) => {
          console.log("=========== ERROR REFRESH TOKEN")
          // return throwError(err);
          return this.logoutAndRedirect(err);
        })
      );
    } else {
      // wait while getting new token
      return this.accessTokenSubject.pipe(
        filter(token => token !== null),
        take(1),
        switchMap(token => {
          // repeat failed request with new token
          return next.handle(this.addAuthorizationHeader(request, token));
        }));
    }
  }
}</pre>



<p> The code is quite long but to sum up on HTTP 401 status code detected, it will ask for new tokens, save them and retry the request automatically. It is explained in the article mentionned before.</p>



<p>I just adapt it for one use case : when the refresh token has expired too. In that case, we need to manage it otherwise the code will run in an infinite loop : detect HTTP 401, call refresh endpoint (which is now also returning an HTTP 401 because of expired refresh token), detect HTTp 401, call refresh, &#8230;</p>



<p>This is the purpose of these lines</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> // in case of 401 http error
        console.log("==== ERROR 401 url:"+req.url)
        if (req.url.indexOf("/refresh")>0){
          //REFRESHTOKEN KO 
          console.log("==== 401 LOGOUT CAR ERROR")
          // otherwise logout and redirect to login page
          return this.logoutAndRedirect(err);
        }</pre>



<p>If we get an HTTP 401 and the requested url was <strong>/refresh</strong> endpoint , we stop the process and log out our user.</p>



<p>To use our token.interceptor service, we just need to declare it in our app.module.ts file</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: (LanguageLoader),
        deps: [HttpClient]
      }
    }),
    IonicModule.forRoot({
      animated:false
    }),
      NativeHttpModule,
      AppRoutingModule,
    HttpClientModule,
 ],
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    TranslateConfigService,
    ScreenOrientation,
    HTTP,
     {provide: HttpBackend, useClass: NativeHttpFallback, deps: [Platform, NativeHttpBackend, HttpXhrBackend]},
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true } ,
  ],
  bootstrap: [AppComponent]
})</pre>



<p>Ok now eveything should be ok. You could log in, see your information, wait 5 minutes for HTTP 401 and you will see in your browser console log, that a new token will be obtain and you will be still logged in. Then wait more than 10 minutes, both access and refresh token will be expired, and you should be log out from the application (if you set the REFRESH Token expiration to ten minutes in django backend of course).</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">'REFRESH_TOKEN_LIFETIME': timedelta(minutes=10),</pre>



<p>And voila. We learned how to implement and manage JWT tokens with a Ionic application and a Django backend.</p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/secure-api-for-ionic-application/">How to secure Ionic application and Django API with JWT authentication ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to use Stripe checkout with Ionic ?</title>
		<link>https://www.ionicanddjangotutorial.com/stripe-checkout-and-ionic/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Sat, 24 Oct 2020 17:40:53 +0000</pubDate>
				<category><![CDATA[Stripe tutorials]]></category>
		<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=599</guid>

					<description><![CDATA[<p>Problem to solve to use Stripe checkout within Ionic application. Stripe Checkout is a prebuilt, hosted payment page that lets&#8230; <a href="https://www.ionicanddjangotutorial.com/stripe-checkout-and-ionic/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/stripe-checkout-and-ionic/">How to use Stripe checkout with Ionic ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2>Problem to solve to use Stripe checkout within Ionic  application.</h2>



<p>Stripe Checkout is a prebuilt, hosted payment page that lets you collect payments quickly. There are a lot of advantages to use <a href="https://stripe.com/docs/payments/checkout" target="_blank" rel="noreferrer noopener">Stripe checkout</a> such as: real time credit card validation, easily customisable, quick implementation, mobile ready with Apple pay and Google pay&#8230;</p>



<p>To implement Stripe checkout it is really easy, you just need to display a payment button on your page which on click will call an API on your server to create a checkout session and then on API response,  redirect to Stripe checkout url to display the payment form. Once the payment is done successfully or not, Stripe will notify you using a success redirect url or a cancel url. </p>



<p class="has-text-align-left">These success url or cancel url requirements are a big problem for a Ionic mobile application, since it is not possible to implement them. We could think to use a specific deeplink, but Stripe checkout requires HTTPS link so it is not an option.   </p>



<p>In this tutorial, i will show you how to use Stripe checkout with Ionic 5 and Django and implement the Stripe checkout worklow in a Ionic 5 mobile application.</p>



<p>Get the <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/stripecheckouttutorial" target="_blank" rel="noreferrer noopener">source code</a></p>



<span id="more-599"></span>



<h2>Proposed solution to use Stripe checkout and make payment with Ionic</h2>



<p>Since it is not possible to redirect Stripe success or cancel urls to our ionic mobile application, we need to implement these urls on a server and we will use Django for that. Then our Django backend will need to inform the Ionic application about payment information status. To do that, we will see that there are some solutions. </p>



<p>Ok enough talking. Let&#8217;s dive into the code and first let&#8217;s create our Ionic 5 application with command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start stripecheckout blank --capacitor --project-id=stripecheckout --package-id=com.idevotion.stripecheckout</pre>



<p>Then we will add Stripe javascript sdk into our generated ionic application</p>



<p></p>






<h2>Add Stripe checkout to Ionic page</h2>



<p>Let&#8217;s edit the <strong>index.html</strong> file and import Stripe javascript sdk</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;head>
  &lt;meta charset="utf-8" />
  &lt;title>Ionic App&lt;/title>

  &lt;base href="/" />

  &lt;meta name="color-scheme" content="light dark" />
  &lt;meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  &lt;meta name="format-detection" content="telephone=no" />
  &lt;meta name="msapplication-tap-highlight" content="no" />

  &lt;link rel="icon" type="image/png" href="assets/icon/favicon.png" />

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

  &lt;script src="https://js.stripe.com/v3/">&lt;/script>
   
&lt;/head></pre>



<p>Now let&#8217;s edit the <strong>home.page.html</strong> file to add a button which will trigger Stripe checkout on click</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header [translucent]="true">
  &lt;ion-toolbar>
    &lt;ion-title>
      Blank
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content [fullscreen]="true">
  &lt;ion-header collapse="condense">
    &lt;ion-toolbar>
      &lt;ion-title size="large">Blank&lt;/ion-title>
    &lt;/ion-toolbar>
  &lt;/ion-header>

  &lt;div id="container">
     
     &lt;ion-button #stripeButton color="primary">Pay with Stripe&lt;/ion-button>
    
  &lt;/div>
&lt;/ion-content>
</pre>



<p>In the <strong>home.page.ts</strong> file we will initialise Stripe with the publishable_key that we can get from our Stripe dashboard :</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component, ElementRef, ViewChild } from '@angular/core';
import { LoadingController } from '@ionic/angular';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Plugins } from '@capacitor/core';
const { Storage } = Plugins;

declare var Stripe;

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  stripe = Stripe("pk_test_XwRn88Fmqzh3966EVK92hpJY00jGOfGKPt");
  
  @ViewChild('stripeButton',{read:ElementRef}) stripeButton : ElementRef;
   constructor(public loadingController:LoadingController,
    private http: HttpClient) {
   

  }</pre>



<p>Then once the view will be initiated, we will grab a reference to our Stripe button and will listen for <strong>click</strong> event </p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> ngAfterViewInit(){
    const stripeButton = this.stripeButton.nativeElement;
    stripeButton.addEventListener('click', event => {
      console.log("=== CLICK BUTTON ")
   });
  }</pre>



<p>On click we can show a loader and then call our Django API (that we will create soon) to create and obtain a stripe checkout session. Then if everything goes fine we can redirect the user to the Stripe checkout page. Here is the full method:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ngAfterViewInit(){
    const stripeButton = this.stripeButton.nativeElement;
    stripeButton.addEventListener('click', event => {
      console.log("=== ON CLIQUE LE BUTTON ")
      const options = {
        headers: new HttpHeaders({
            'content-type': 'application/json',
        })
    };
    
      this.showLoader()
      // call API
      let params = {
        "currency" : "eur",
        "quantity": 1,
        "price": 1000,
        "productName" : "My product"
      }
      console.log(params)
      this.http.post("https://app-e06ec9e7-b121-44fa-a383-54bbda3f9706.cleverapps.io/create-checkout-session/", params,options)
      .subscribe(async res => {
        console.log(res)
       
         
          let status = res["status"]
          if (status=="OK"){
            let sessionID = res["session"]
            // save session ID  
            await Storage.set({key: "paymentSessionID", value: sessionID});

            //Redirect sur checkout
            this.stripe.redirectToCheckout({ sessionId: sessionID}).then((result) => {
              console.log(result)
            })
          }
          else{
            console.log("===ERROR")
            this.hideLoader()
          }

      }, error => {
           this.hideLoader()
          console.log(error);// Error getting the data
        });
      });
  }</pre>



<p>For demo purpose i have hardcoded the payment information (price, quantity, product name).</p>



<p>If the API call is successfull (status equals OK), then we store the received session id using capacitor storage (we will discuss the reason why later) and then we redirect the user to Stripe checkout passing the session id that we get from the API</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">//Redirect sur checkout
            this.stripe.redirectToCheckout({ sessionId: sessionID}).then((result) => {
              console.log(result)
            })</pre>



<p>At this point, the application will go on background and the user will be redirected on his device browser, on which the stripe payment form should appeared.</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="473" height="1024" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox-473x1024.jpg" alt="" class="wp-image-627" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox-473x1024.jpg 473w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox-600x1300.jpg 600w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox-138x300.jpg 138w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox-768x1664.jpg 768w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox-709x1536.jpg 709w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox-945x2048.jpg 945w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox-400x867.jpg 400w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/11/Screenshot_2020-11-01-15-58-50-174_org.mozilla.firefox.jpg 1080w" sizes="(max-width: 473px) 100vw, 473px" /></figure>



<p>Now let&#8217;s focus on the Django part</p>






<h2>Add Stripe checkout to Django project</h2>



<p>In our Django project, we will define 3 endpoints to manage Stripe checkout:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">url(r'^create-checkout-session/', createCheckoutSession, name='create-checkout-session'),
url(r'^checkout-session-success/', checkoutSessionSuccess, name='checkout-session-success'),
url(r'^checkout-session-failure/', checkoutSessionFailure, name='checkout-session-failure'),</pre>



<p>The method <strong>createCheckoutSession</strong> is the endpoint used previously in the ionic page, to send payment information (price, currency) and to receive the stripe checkout session id. So let&#8217;s add a view that will manage this:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
@csrf_exempt
@api_view(['POST'])
def createCheckoutSession(request):
    import stripe
    stripe.api_key = settings.STRIPE_API_KEY
    if request.method == 'POST':
        currency = request.data["currency"]
        quantity = request.data["quantity"]
        price = request.data["price"]
        productName = request.data["productName"]
        session = stripe.checkout.Session.create(
            payment_method_types=['card'],
            line_items=[{
                'price_data': {
                    'currency': currency,
                    'product_data': {
                        'name': productName,
                    },
                    'unit_amount': price,
                },
                'quantity': quantity,
            }],
            mode='payment',
            success_url='https://app-e06ec9e7-b121-44fa-a383-54bbda3f9706.cleverapps.io/checkout-session-success/?session_id={CHECKOUT_SESSION_ID}',
            cancel_url='https://app-e06ec9e7-b121-44fa-a383-54bbda3f9706.cleverapps.io/checkout-session-failure/?session_id={CHECKOUT_SESSION_ID}',
        )
        newdict = {'status': 'OK', 'session': session.id}
        return JsonResponse(newdict)
    else:
        return HttpResponse(status=404)</pre>



<p>Nothing really complicated. We use Django rest framework to define our endpoint and get the parameters. Then we create a Stripe checkout session with the parameters and our success / cancel urls (the two other endpoints that we defined). Finally if everything goes fine, we send our response with a JSON containing the status and the session identifier. For success and cancel endpoint, we need to create these methods:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
def checkoutSessionSuccess(request):
    session_id = request.GET['session_id']
    stripe.api_key = settings.STRIPE_API_KEY
    if session_id:
        try:
            session = stripe.checkout.Session.retrieve(session_id)
            customer = stripe.Customer.retrieve(session.customer)
            payment = Payment()
            payment.sessionId = session.id
            payment.customerId = customer.id
            payment.success=True
            payment.save()
            return render(request,'bo/templates/bo/success.html', {})
        except Exception as e:
            logger.error(e)
            return render(request,'bo/templates/bo/error.html', {})


def checkoutSessionFailure(request):
    session_id = request.GET['session_id']
    stripe.api_key = settings.STRIPE_API_KEY
    if session_id:
        try:
            session = stripe.checkout.Session.retrieve(session_id)
            customer = stripe.Customer.retrieve(session.customer)
            payment = Payment()
            payment.sessionId = session.id
            payment.customerId = customer.id
            payment.success = False
            payment.save()
            return render(request,'bo/templates/bo/error.html', {})
        except Exception as e:
            logger.error(e)
            return render(request,'bo/templates/bo/error.html', {})
</pre>



<p>On success we can retrieve the session and the customer and save the results in our model call Payment with a success status.</p>



<p>On cancel, we do the same but with a payment.success to False.</p>



<p>On success.html page we can display a message to the user that the payment has been successfull and that he can go back to the application.</p>



<p>Same for error.html but with a payment error message.</p>



<h2>How to notify Ionic application about Stripe checkout payment status ?</h2>



<p>At this point, the user is still redirected to it&#8217;s device browser and outside our Ionic application. On the success and error pages, we display a message asking him to go back to the application. So we can expect that he will do it. If so, when application launching again (remember application was suspended in background), we can check if we have a payment in progress and check the status of this payment by asking our Django backend. </p>



<p>Remember the :</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">   await Storage.set({key: "paymentSessionID", value: sessionID});</pre>



<p>This is how we can know a payment is in progress. So when application resume, we can check the status and determine actions (go to a payment successfull page or a payment error page)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> //Subscribe on resume
    this.platform.resume.subscribe(async () => {
      let paymentInProgress = await Storage.get({key: "paymentSessionID"});
      if (paymentInProgress){
        let sessionId = paymentInProgress.value;
        // Remove key for future launch
        Storage.remove({key: "paymentSessionID"});
        console.log("========== NEED TO CHECK STATUS FOR PAYMENT")
        const options = {
          headers: new HttpHeaders({
              'content-type': 'application/json',
          })
      };
        let params = {
          "sessionId" : sessionId
        }
        console.log(params)
        this.http.post("https://app-e06ec9e7-b121-44fa-a383-54bbda3f9706.cleverapps.io/paymentCheckStatus/", params,options)
        .subscribe(async res => {
            console.log(res)
            let status = res["status"]
            if (status=="OK"){
              this.router.navigateByUrl("/payment-ok")
            }
            else{
              this.router.navigateByUrl("/payment-ko")
            }
        })
      }
    });</pre>



<p>It&#8217;s important to remove the key <strong>paymentSessionID</strong> from our capacitor storage once read, to avoid checking at next launch.</p>



<p>In our Django backend, we will add this new url paymentCheckStatus</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> url(r"^paymentCheckStatus/",paymentCheckStatus,name="paymentCheckStatus"),</pre>



<p> and the corresponding view:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
@csrf_exempt
@api_view(['POST'])
def paymentCheckStatus(request):
    if request.method == 'POST':
        status ="KO"
        sessionId = request.data["sessionId"]
        try:
            payment = Payment.objects.get(sessionId=sessionId)
            if payment.success:
                status="OK"
            else:
                status = "KO"
        except Exception as e:
            logger.error(e)
            status = "KO"
        newdict = {'status': status}
        return JsonResponse(newdict)
    else:
        return HttpResponse(status=404)</pre>



<p>And voila based on the status our Ionic application can go to a successful payment page or to an error page</p>



<h2>Conclusion</h2>



<p>We have seen in this tutorial one method to use Stripe checkout within a Ionic application. We have implemented a simple system with a little compromise. Once redirected outside our Ionic application, the user will need to go back manually to the application. If not, the Ionic application will know nothing about the payment.</p>



<p>Another better solution, could have been to implement deeplinks in our ionic application (one for success and one for error payments). Then in our Django views (success or cancel) we could have redirected automatically the user to our app using the deeplinks.</p>



<p>We could also have used push notifications to send a custom message to the application to notify about the payment status.</p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/stripe-checkout-and-ionic/">How to use Stripe checkout with Ionic ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to automatically display loading spinner using Http Interceptor in Ionic 5 ?</title>
		<link>https://www.ionicanddjangotutorial.com/how-to-automatically-display-loading-spinner-using-http-interceptor-in-ionic-5/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Fri, 02 Oct 2020 08:39:38 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=576</guid>

					<description><![CDATA[<p>In this tutorial, i will show you how to user angular http interceptor to show a loading spinner alert to&#8230; <a href="https://www.ionicanddjangotutorial.com/how-to-automatically-display-loading-spinner-using-http-interceptor-in-ionic-5/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-automatically-display-loading-spinner-using-http-interceptor-in-ionic-5/">How to automatically display loading spinner using Http Interceptor in Ionic 5 ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this tutorial, i will show you how to user angular http interceptor to show a loading spinner alert to the user each time a request is made, without having to start or stop manually the spinner.</p>



<p>The  <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/paypal" target="_blank" rel="noreferrer noopener">source code </a><a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/spinner" target="_blank" rel="noreferrer noopener">repository</a></p>



<span id="more-576"></span>



<p>Each mobile application needs to warn users when a request is made to a server, to let them know that they should wait a little bit. To manage it, most common use case is to display an alert or a <a href="https://ionicframework.com/docs/api/spinner" target="_blank" rel="noreferrer noopener">loading spinner</a> before the request, and then stopping the alert once the response has been received. And usually we need to manage this behaviour manually, by starting or stopping the alert or spinner from code.</p>



<p>&nbsp;Angular http interceptor does is allow you to&nbsp;<em>intercept&nbsp;</em>an HttpRequest before it’s sent to the server and after the server responds.&nbsp;So it look the perfect place to automatically start or stop a loading spinner or an alert.</p>



<p>Let&#8217;s create our Ionic 5 application with command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start spinner blank --capacitor --project-id=spinner --package-id=com.idevotion.spinner</pre>



<p>Then let&#8217;s add a service with methods to display or stop alerts and to get data from an api.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic g service ApiService</pre>



<p> Edit the <strong>api-service.service.ts</strong> file to add the following code</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { LoadingController, AlertController, Platform } from '@ionic/angular';
import { Observable } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class ApiServiceService {

  isShowingLoader = false;
  loader: any;

  constructor(public http: HttpClient,
    public loadingController: LoadingController,
    public alertCtrl: AlertController,
    public platform: Platform) {
  }

  getJsonData() {
    let urlToCall = "https://jsonplaceholder.typicode.com/comments/"
    return Observable.create(observer => {
      this.http.get(urlToCall)
        .subscribe(res => {
          observer.next(res);
          observer.complete();
        }, error => {
          observer.next(null);
          observer.complete();
        });
    });
  }

  async showLoader() {
    if (!this.isShowingLoader) {
      this.isShowingLoader = true
      this.loader = await this.loadingController.create({
        message: 'Please wait',
        duration: 4000
      });
      return await this.loader.present();
    }
  }

  async stopLoader() {
    if (this.loader) {
      this.loader.dismiss()
      this.loader = null
      this.isShowingLoader = false
    }
  }
}
</pre>






<p>The API Service contains a <strong>getJsonData()</strong> which will call an API and return json data from it.</p>



<p>And two methods<strong> showLoader() </strong>and <strong>stopLoader()</strong> to display a Loadingcontroller showing a waiting message. Nothing really complicated here.</p>



<p>Now let&#8217;s edit our <strong>home.page.html</strong> file </p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header [translucent]="true">
  &lt;ion-toolbar>
    &lt;ion-title>
      Loading Spinner
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content [fullscreen]="true">
  &lt;ion-header collapse="condense">
    &lt;ion-toolbar>
      &lt;ion-title size="large">Loading Spinner&lt;/ion-title>
    &lt;/ion-toolbar>
  &lt;/ion-header>

  &lt;div id="container">
    &lt;strong>Ready to test?&lt;/strong>
    &lt;ion-button shape="round" (click)="clickButton()">Get JSON Data&lt;/ion-button>
  &lt;/div>
&lt;/ion-content>
</pre>



<p>And in our home.page.ts file we create a method <strong>clickButton()</strong> to :</p>



<ol><li>show the alert</li><li>call the API</li><li>stop the alert</li></ol>



<p>So let&#8217;s write our <strong>home.page.ts</strong> </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { ApiServiceService } from './../api-service.service';
import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  constructor(public apiService:ApiServiceService) {
  }

  clickButton(){
    this.apiService.showLoader().then(()=>{
      this.apiService.getJsonData().subscribe((data)=>{
        console.log(data)
        this.apiService.stopLoader()
      })
    })
  }
}
</pre>



<p>You can now run <strong>ionic serve</strong> and have a look to the result. When clicking on the button, an alert message should be displayed, then the alert disappeared and data received from the API should be displayed in the console of your browser.</p>



<p>Ok this is the usual method. Now let&#8217;s use Angular Http Interceptor to automatically display the alert when a request is initiated and stop the alert once the response is received.</p>



<h2>Using angular http interceptor with Ionic 5</h2>



<p>We will add the interceptor in our <strong>app.module.ts</strong> file after the imports and just before the <strong>@NgModule</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Injectable()
export class AngularInterceptor implements HttpInterceptor {

  constructor(){
  }

  intercept(req: HttpRequest&lt;any>, next: HttpHandler): Observable&lt;HttpEvent&lt;any>> {
    let defaultTimeout = 10000;
      
      return next.handle(req).pipe(timeout(defaultTimeout),
        retryWhen(err=>{
          let retries = 1;
          return err.pipe(
            delay(500),
            map(error=>{
              if (retries++ ===3){
                throw error
              }
              return error;
            })
          )
        }),catchError(err=>{
          console.log(err)
          return EMPTY
        }), finalize(()=>{
          
        })
      )
    };    
}</pre>



<p>The method <strong>intercept</strong> will be called at the start of every http request which is perfect. This is not the study of your tutorial, but i have added some pipes to the interceptor to add a default timeout of 10 secondes for a request, and if the request failed it will try again 3 times with 500ms between each request. Then after 3 times it will failed.</p>



<p>We will add an observer to our API service that we will use to know when a request starts and when it stops.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> loadingObserver: BehaviorSubject&lt;boolean> = new BehaviorSubject&lt;boolean>(false);</pre>



<p>Then we will declare a map that will contain request made</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">loadingRequestMap: Map&lt;string, boolean> = new Map&lt;string, boolean>();</pre>



<p>Now, we will  use these two variables in a new method <strong>setLoading</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> setLoading(loading: boolean, url: string): void {
    if (!url) {
      throw new Error('The request URL must be provided');
    }
    if (loading === true) {
      this.loadingRequestMap.set(url, loading);
      this.loadingObserver.next(true);
    }else if (loading === false &amp;&amp; this.loadingRequestMap.has(url)) {
      this.loadingRequestMap.delete(url);
    }
    if (this.loadingRequestMap.size === 0) {
      this.loadingObserver.next(false);
    }
  }</pre>



<p>When loading we set the url in our map and we notify our observer that a request has started. And when not loading, we can remove the url and still notify our observers.</p>



<p>With that in place we can use it in our intercept method. Here is the new code </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Injectable()
export class AngularInterceptor implements HttpInterceptor {
  constructor(private _loading: ApiServiceService){

  }

  intercept(req: HttpRequest&lt;any>, next: HttpHandler): Observable&lt;HttpEvent&lt;any>> {
    let defaultTimeout = 10000;
      this._loading.setLoading(true, req.url);
   
      return next.handle(req).pipe(timeout(defaultTimeout),
        retryWhen(err=>{
          let retries = 1;
          return err.pipe(
            delay(1000),
            map(error=>{
              if (retries++ ===3){
                this._loading.setLoading(false, req.url);
                throw error
              }
              return error;
            })
          )
        }),catchError(err=>{
          console.log(err)
            this._loading.setLoading(false, req.url);
          return EMPTY
        }), finalize(()=>{
            this._loading.setLoading(false, req.url);
        })
      )
    };
}</pre>



<p>Ok. Now we just need to subscribe to the <strong>loadingObserver</strong> variable and show or stop the alert to the user. And because we want this behaviour to be global to our application, we will add the code in our <strong>app.component.ts </strong>file</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">export class AppComponent implements OnInit{
  constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar,
    private apiService:ApiServiceService
  ) {
    this.initializeApp();
  }
  ngOnInit() {
    this.listenToLoading();
  }

  listenToLoading(): void {
    this.apiService.loadingObserver
      .pipe(delay(0)) // This prevents a ExpressionChangedAfterItHasBeenCheckedError for subsequent requests
      .subscribe((loading) => {
        if (loading){
          this.apiService.showLoader()
        }
        else{
          this.apiService.stopLoader()
        }
      });
  }
</pre>






<p>On the <strong>ngOnInit()</strong> method we start listening to our variable and then based on loading value we can hide or show an alert to the user.</p>



<p>Finally we can modify our <strong>clickButton</strong>() method in our home.page.ts file and remove our manual call to start and stop loader.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">  clickButton(){
      this.apiService.getJsonData().subscribe((data)=>{
        console.log(data)
      })
  }</pre>



<p>If you test again, you still should be able to see the alert message ! </p>



<p>Now each time a request will be made in our Ionic application, an alert message will be displayed automatically !</p>



<p>But if for any reasons you don&#8217;t want to display this alert message when calling some specific urls, you can do it by improving the intercept method and check if the called url should display an alert or not. Something like this:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> if (req.url.indexOf("checkAPI")&lt;0){
      this._loading.setLoading(true, req.url);
    }</pre>



<p>It is up to you ! </p>



<h2>Display a loading spinner with http interceptor with Ionic 5</h2>



<p>Now let&#8217;s modify the code to display a loading spinner instead. Let&#8217;s modify our home.page.html file</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header [translucent]="true">
  &lt;ion-toolbar>
    &lt;ion-title>
      Loading Spinner
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content [fullscreen]="true">
  &lt;ion-header collapse="condense">
    &lt;ion-toolbar>
      &lt;ion-title size="large">Loading Spinner&lt;/ion-title>
    &lt;/ion-toolbar>
  &lt;/ion-header>

  &lt;div id="container">
    &lt;strong>Ready to test?&lt;/strong>
    &lt;ion-spinner name="bubbles" *ngIf="loading">&lt;/ion-spinner>
    &lt;ion-button shape="round" (click)="clickButton()">Get JSON Data&lt;/ion-button>
  &lt;/div>
&lt;/ion-content>
</pre>



<p>If the variable <strong>loading</strong> is true we will display a<strong> ion-spinner</strong></p>



<p>Let&#8217;s change the code in home.page.ts. Now we will subscribe to our observer variable and set the loading variable with the value of our observer</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { ApiServiceService } from './../api-service.service';
import { Component } from '@angular/core';
import { delay } from 'rxjs/operators';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  loading=false;
  constructor(public apiService:ApiServiceService) {

    this.apiService.loadingObserver
    .pipe(delay(0)) // This prevents a ExpressionChangedAfterItHasBeenCheckedError for subsequent requests
    .subscribe((loading) => {
      this.loading = loading
    });
  }

  clickButton(){
      this.apiService.getJsonData().subscribe((data)=>{
        console.log(data)
      })
  }
}
</pre>



<p>And we can comment the <strong>listenToLoading()</strong> method in our <strong>app.component.ts</strong> because we don&#8217;t want to display the alert message.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">  ngOnInit() {
   // this.listenToLoading();
  }

  listenToLoading(): void {
    this.apiService.loadingObserver
      .pipe(delay(0)) // This prevents a ExpressionChangedAfterItHasBeenCheckedError for subsequent requests
      .subscribe((loading) => {
        if (loading){
          this.apiService.showLoader()
        }
        else{
          this.apiService.stopLoader()
        }
      });
  }
</pre>



<p>Now you can test again and you will be able to see the bubble spinner showing up.</p>



<p>Voilà. Enjoy and don&#8217;t hesitate to improve the code by yourself !</p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-automatically-display-loading-spinner-using-http-interceptor-in-ionic-5/">How to automatically display loading spinner using Http Interceptor in Ionic 5 ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to use paypal on PWA with Ionic 5 ?</title>
		<link>https://www.ionicanddjangotutorial.com/how-to-use-paypal-on-pwa-with-ionic-5/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Tue, 22 Sep 2020 17:09:48 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=562</guid>

					<description><![CDATA[<p>In this tutorial, i will show you how to integrate Paypal with Ionic 5, to enable payments on your PWA&#8230; <a href="https://www.ionicanddjangotutorial.com/how-to-use-paypal-on-pwa-with-ionic-5/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-use-paypal-on-pwa-with-ionic-5/">How to use paypal on PWA with Ionic 5 ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this tutorial, i will show you how to integrate Paypal with Ionic 5, to enable payments on your PWA version.</p>



<p>The  <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/paypal" target="_blank" rel="noreferrer noopener">source code repository</a></p>



<span id="more-562"></span>



<p>To integrate paypal in a Ionic 5 application will allow users to make payments using their paypal account or by entering credit card detail information.</p>



<p><strong>Reminder</strong> this tutorial is not about using paypal mobile SDK in an iOS or Android Ionic application, but using Paypal javascript SDK on your Ionic PWA version (running on web).</p>



<p>Let&#8217;s create our Ionic 5 application with command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start paypal blank --capacitor --project-id=paypal --package-id=com.ionicanddjangotutorials.paypal</pre>



<p>Then we need to edit the <strong>index.html</strong> file of our ionic application, to load the Paypal javascript sdk</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;script src="https://www.paypal.com/sdk/js?client-id=YOUR-CLIENT-ID&amp;currency=EUR">&lt;/script></pre>



<p> Just replace the <strong>YOUR-CLIENT-ID</strong> with your sandbox/live environment client id.</p>



<p>Now we will modify our <strong>home.page.html </strong>file to integrate a Paypal button</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header>
  &lt;ion-toolbar>
    &lt;ion-title>Paypal Demo&lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content>
  &lt;div class="page-flex-align" padding>
    &lt;div class="top-content">
      &lt;h1>Would you like to pay {{price}} ? &lt;/h1>
      &lt;p>
      &lt;/p>
      &lt;div id="paypal-button">&lt;/div>
   &lt;/div>
  &lt;/div>
&lt;/ion-content>
</pre>






<p>Nothing really complicated. We display the amount that the user will pay, then to integrate a Paypal button, the important line is :</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;div id="paypal-button">&lt;/div></pre>



<p>The paypal code will be injected automatically from our <strong>home.page.ts</strong> using the Paypal javascript library.</p>



<p>So let&#8217;s write our <strong>home.page.ts</strong> </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component } from '@angular/core';
declare var paypal: any;
@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  price;
  priceToPay = 10;
  payPalConfig: any;

  PAYPAL_CLIENT_ID_TEST = "YOURDEVKEY"
  PAYPAL_CLIENT_ID_LIVE = "YOURLIVEKEY"

  PAYPAL_CLIENT_ID = this.PAYPAL_CLIENT_ID_TEST

  constructor() {

    this.price = this.priceToPay + " €"
    let enviroment = ""
    if (this.PAYPAL_CLIENT_ID == this.PAYPAL_CLIENT_ID_TEST) {
      enviroment = "sandbox"
    }
    else {
      enviroment = "live"
    }

    this.payPalConfig = {
      env: enviroment,
      client: {
        sandbox: this.PAYPAL_CLIENT_ID,
      },
      commit: false,
       createOrder: (data, actions)=> {
        return actions.order.create({
            purchase_units: [{
                amount: {
                    value: this.module.prix,
                    currency: 'EUR' 
                }
            }]
        });
    },
      // Finalize the transaction
      onApprove: (data, actions) => {
        //console.log(data)
        //console.log(actions)
        return actions.order.capture()
          .then((details) => {
            // Show a success message to the buyer
            console.log(details)
            let status = details["status"]
            let id = details["id"]
            if (status == "COMPLETED") {
              this.validPurchase(id)
            }
            else {
              //Status not completed...
            }
            console.log('Transaction completed by ' + details.payer.name.given_name + '!');
          })
          .catch(err => {
            console.log(err);
            // deal with error
          })
      }
      ,
      onError: (err) => {
        // Show an error page here, when an error occurs
        console.log(err)
        // deal with error
      }
    }
  }

  validPurchase(id) {
    // Purchase confirm 
    //Do whatever you want to do
  }

  ionViewDidEnter() {
    paypal.Buttons(this.payPalConfig).render('#paypal-button');
  }


  ngOnInit() {
  }

}
</pre>



<h2>Use paypal javscript sdk with Ionic 5</h2>



<p>First we need to declare the paypal sdk</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">declare var paypal: any;</pre>



<p>Then we can configure our paypal api keys</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">PAYPAL_CLIENT_ID_TEST = "YOURDEVKEY"
PAYPAL_CLIENT_ID_LIVE = "YOURLIVEKEY"

PAYPAL_CLIENT_ID = this.PAYPAL_CLIENT_ID_TEST</pre>



<p>And set the paypal environment to use (sandbox or live) based on the key</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> let enviroment = ""
    if (this.PAYPAL_CLIENT_ID == this.PAYPAL_CLIENT_ID_TEST) {
      enviroment = "sandbox"
    }
    else {
      enviroment = "live"
    }</pre>



<p>Now, we will configure the most important piece of the sdk, a paypal configuration object :</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> this.payPalConfig = {
      env: enviroment,
      client: {
        sandbox: this.PAYPAL_CLIENT_ID,
      },
      commit: false,
       createOrder: (data, actions)=> {
        return actions.order.create({
            purchase_units: [{
                amount: {
                    value: this.module.prix,
                    currency: 'EUR' 
                }
            }]
        });
    },
      // Finalize the transaction
      onApprove: (data, actions) => {
        //console.log(data)
        //console.log(actions)
        return actions.order.capture()
          .then((details) => {
            // Show a success message to the buyer
            console.log(details)
            let status = details["status"]
            let id = details["id"]
            if (status == "COMPLETED") {
              this.validPurchase(id)
            }
            else {
              //Status not completed...
            }
            console.log('Transaction completed by ' + details.payer.name.given_name + '!');
          })
          .catch(err => {
            console.log(err);
            // deal with error
          })
      }
      ,
      onError: (err) => {
        // Show an error page here, when an error occurs
        console.log(err)
        // deal with error
      }
    }</pre>



<p>The <strong>createOrder</strong> method will set the amout and the currency that the user needs to pay</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">  createOrder: (data, actions)=> {
        return actions.order.create({
            purchase_units: [{
                amount: {
                    value: this.module.prix,
                    currency: 'EUR' 
                }
            }]
        });
    },</pre>



<p>The <strong>onApprove</strong> method will be fired once the payment has been made. </p>



<p>We just need to check the value of <strong>status</strong> variable to know if the payment has been successfull or not. If status is not COMPLETED or an error occurs, we just need to deal with the error.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> onApprove: (data, actions) => {
        //console.log(data)
        //console.log(actions)
        return actions.order.capture()
          .then((details) => {
            // Show a success message to the buyer
            console.log(details)
            let status = details["status"]
            let id = details["id"]
            if (status == "COMPLETED") {
              this.validPurchase(id)
            }
            else {
              //Status not completed...
            }
            console.log('Transaction completed by ' + details.payer.name.given_name + '!');
          })
          .catch(err => {
            console.log(err);
            // deal with error
          })
      }</pre>






<p>We can also configure <a href="https://developer.paypal.com/docs/checkout/integration-features/customize-button/#layout" target="_blank" rel="noreferrer noopener">the style of our paypal button</a>. We just need to pass a style configuration to our <strong>paypalConfig</strong> dictionnary.</p>



<p>Et voila. We are now able to use paypal to pay on the web in our Ionic 5 application.</p>



<p></p>



<p></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-use-paypal-on-pwa-with-ionic-5/">How to use paypal on PWA with Ionic 5 ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to use videogular2 with Ionic 5 ?</title>
		<link>https://www.ionicanddjangotutorial.com/how-to-use-videogular2-with-ionic5/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Thu, 17 Sep 2020 13:03:10 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=534</guid>

					<description><![CDATA[<p>In this tutorial, i will show you how to use videogular2 with Ionic 5, for playing videos from local source&#8230; <a href="https://www.ionicanddjangotutorial.com/how-to-use-videogular2-with-ionic5/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-use-videogular2-with-ionic5/">How to use videogular2 with Ionic 5 ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this tutorial, i will show you how to use videogular2 with Ionic 5, for playing videos from local source (assets folder) or external source (from internet).</p>



<p>The  <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/videoplayer" target="_blank" rel="noreferrer noopener">source code repository</a></p>



<span id="more-534"></span>



<p>If you want to embed a video player in your Ionic 5 page working in mobile and desktop, there is no solution other than using an external package. Ionic or Capacitor doesn&#8217;t provide a ready component/plugin yet !</p>



<p><a href="https://github.com/videogular/videogular2" target="_blank" rel="noreferrer noopener">Videogular2</a> is an HTML5 video player for Angular2+, which is working on mobile and browser. It is a perfect candidate to use with Ionic 5.</p>



<h2>Installing videogular2 to use with Ionic 5</h2>



<p> If you try to install the official npm for videogular2, your Ionic 5 compilation source code will failed, raising some import issues. To avoid these issues, we need to use the <strong><a href="https://github.com/videogular/ngx-videogular" target="_blank" rel="noreferrer noopener">ngx-videogular</a> </strong>package.</p>



<p>But before that let&#8217;s create our Ionic 5 project with command</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start videoplayer blank --capacitor --project-id=videoplayer --package-id=com.ionicanddjangotutorials.videoplayer</pre>



<p>Then we can go into our new created ionic 5 directory <strong>videoplayer</strong> and install a Ionic 5 videogular2 compatible plugin with command</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">npm i --save @videogular/ngx-videogular</pre>



<h2>Using videogular2 with Ionic 5</h2>



<p>Now we need to import the videogular css and library modules into our Ionic 5 project. Let&#8217;s start with the css file. We can edit our <strong>global.scss</strong> file and add the line</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@import "~@videogular/ngx-videogular/fonts/videogular.css";</pre>



<p>Then we will modify our <strong>home.module.ts</strong> file to import videogular2 main modules.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
import { VgCoreModule } from '@videogular/ngx-videogular/core';
import { VgControlsModule } from '@videogular/ngx-videogular/controls';
import { VgOverlayPlayModule } from '@videogular/ngx-videogular/overlay-play';
import { VgBufferingModule } from '@videogular/ngx-videogular/buffering';</pre>



<p>Ok the final file should be</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { HomePage } from './home.page';

import { HomePageRoutingModule } from './home-routing.module';

import { VgCoreModule } from '@videogular/ngx-videogular/core';
import { VgControlsModule } from '@videogular/ngx-videogular/controls';
import { VgOverlayPlayModule } from '@videogular/ngx-videogular/overlay-play';
import { VgBufferingModule } from '@videogular/ngx-videogular/buffering';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    HomePageRoutingModule,
    VgCoreModule,
    VgControlsModule,
    VgOverlayPlayModule,
    VgBufferingModule
  ],
  declarations: [HomePage]
})
export class HomePageModule {}
</pre>






<p>In the <strong>home.page.ts</strong>  file script, we will declare an array of our videos. One will come from our <strong>ionic assets </strong>directory and the other one would be loaded from internet. To display videos, we will modify the <strong>home.page.html</strong> file and write</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header>
  &lt;ion-toolbar>
    &lt;ion-title>Demo VideoPlayer    
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>
&lt;ion-content padding>
  &lt;div class="videoplayer">
    &lt;vg-player (onPlayerReady)="onPlayerReady($event)">
      &lt;video class="videoplayer" [vgMedia]="media" #media id="singleVideo" preload="auto" controls crossorigin>
        &lt;source [src]="urlVideo" type="video/mp4">
      &lt;/video>
    &lt;/vg-player>
  &lt;/div>
  &lt;ion-grid class="ion-no-padding">
    &lt;ion-row class="ion-padding">
      &lt;ion-col *ngFor="let item of items" size="4" class="ion-padding">
        &lt;div class="module">
        &lt;ion-item fill="clear" class="ion-no-padding" (click)="playVideo(item)">
          &lt;ion-img src="{{item.imagePreview}}" alt="Image not found">&lt;/ion-img>
         
        &lt;/ion-item>
        &lt;div  class="ion-no-padding">
          &lt;h4 class="overlay-text">{{item.title}}&lt;/h4>
        &lt;/div>
      &lt;/div>
      &lt;/ion-col>
    &lt;/ion-row>
  &lt;/ion-grid>
&lt;/ion-content></pre>



<p>First we create a <strong>&lt;div></strong> containing our videogular2 player</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> &lt;div class="videoplayer">
    &lt;vg-player (onPlayerReady)="onPlayerReady($event)">
      &lt;video class="videoplayer" [vgMedia]="media" #media id="singleVideo" preload="auto" controls crossorigin>
        &lt;source [src]="urlVideo" type="video/mp4">
      &lt;/video>
    &lt;/vg-player>
  &lt;/div></pre>



<p>The <strong>onPlayerReady</strong> method will be fired once the player will be ready. </p>



<p>The <strong>urlVideo</strong>  will contains our video url to play. To learn mode about the player and available option, you can consult the <a href="https://videogular.github.io/videogular2/docs/getting-started/how-videogular-works.html" target="_blank" rel="noreferrer noopener">documentation</a>.</p>



<p>After that, we display a simple Ionic grid showing available videos with their title and image preview. Each item/video in the grid is clickable and will call the method <strong>playVideo(item)</strong>.</p>



<p>Ok final step, we can modify the <strong>home.page.ts</strong> file to write our code</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component, ViewChild } from '@angular/core';
import { VgApiService, VgMediaDirective } from '@videogular/ngx-videogular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  @ViewChild(VgMediaDirective, { static: true }) media: VgMediaDirective;
  api:VgApiService;
  urlVideo:string=""
  items = [
    {
      "title":"External file",
      "url":"http://static.videogular.com/assets/videos/videogular.mp4",
      "imagePreview":"assets/earth.png"
    },
    {
      "title":"Local video file",
      "url":"assets/videogular.mp4",
      "imagePreview":"assets/earth.png"
    },
  ]
  constructor() {}

  playVideo(item){
   
    // Play video
    this.urlVideo=item.url
    if (this.media){
       this.media.vgMedia.src=this.urlVideo
       this.media.subscriptions.canPlay.subscribe((value)=>{
        //this.api.fsAPI.toggleFullscreen()
        this.media.play()
       })
    }
  }

  onPlayerReady(api:VgApiService){
    this.api = api
    this.urlVideo = this.items[0].url
   // this.api.fsAPI.toggleFullscreen()
  }
}
</pre>



<p>To grab a reference to the media video we can use a ViewChild instruction</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@ViewChild(VgMediaDirective, { static: true }) media: VgMediaDirective;</pre>



<p>Then in our method<strong> playVideo(item)</strong> , we can use that reference to change the video url to play. </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> this.media.vgMedia.src=this.urlVideo</pre>



<p>We also subscribe to the event that the media is ready to play and if ready we play it. If you try to play the video without waiting for that <strong>canPlay event </strong>it will not work correctly.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> this.media.subscriptions.canPlay.subscribe((value)=>{
        //this.api.fsAPI.toggleFullscreen()
        this.media.play()
       })</pre>






<p>In the method <strong>onPlayerReady(api:VgApiService)</strong> we get a reference to the <a href="https://videogular.github.io/videogular2/docs/modules/core/services/vg-api/" target="_blank" rel="noreferrer noopener">VgApi</a> service. We can use this service to toggle the video in fullscreen if required</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">this.api.fsAPI.toggleFullscreen()</pre>



<p>Et voila. We are now able to play local or external video files in a Ionic 5 project, on mobile and on web ! Nice job.</p>



<p></p>



<figure class="wp-block-image size-large"><img loading="lazy" width="517" height="869" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/09/Selection_001.png" alt="" class="wp-image-554" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/09/Selection_001.png 517w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/09/Selection_001-178x300.png 178w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/09/Selection_001-400x672.png 400w" sizes="(max-width: 517px) 100vw, 517px" /></figure>



<p></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-use-videogular2-with-ionic5/">How to use videogular2 with Ionic 5 ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		<enclosure url="http://static.videogular.com/assets/videos/videogular.mp4" length="24406814" type="video/mp4" />

			</item>
		<item>
		<title>How to capture multiple photo with Ionic and Capacitor ?</title>
		<link>https://www.ionicanddjangotutorial.com/how-to-capture-multiple-photo-with-ionic-and-capacitor/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Fri, 21 Aug 2020 08:29:00 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=465</guid>

					<description><![CDATA[<p>Recently, i had to develop a ionic application in which users should be able to take multiple photo with their&#8230; <a href="https://www.ionicanddjangotutorial.com/how-to-capture-multiple-photo-with-ionic-and-capacitor/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-capture-multiple-photo-with-ionic-and-capacitor/">How to capture multiple photo with Ionic and Capacitor ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Recently, i had to develop a ionic application in which users should be able to take multiple photo with their camera. This will be the purpose of this tutorial.</p>



<p>The&nbsp;<a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/multiplephoto" target="_blank" rel="noreferrer noopener">source code repository</a></p>



<span id="more-465"></span>



<p> First le&#8217;ts generate a new Ionic with capacitor project. Launch a terminal and enter:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start multiplephoto blank --capacitor --project-id=multiplephoto --package-id=com.ionicanddjangotutorials.multiplephoto</pre>



<h2>Installing and using Media capture plugin with Ionic and Capacitor</h2>



<p>To be able to access the mobile camera and implement our multiple photo functionnality, we will use the <a href="https://ionicframework.com/docs/native/media-capture" target="_blank" rel="noreferrer noopener">Media Capture plugin</a> so let&#8217;s install it</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">npm install cordova-plugin-media-capture
npm install @ionic-native/media-capture</pre>



<p>Now let&#8217;s modify the generated <strong>home.page.html</strong> file to add a button which will open the Camera preview on click</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header [translucent]="true">
  &lt;ion-toolbar>
    &lt;ion-title>
      Multiple photo capture
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content [fullscreen]="true">
  &lt;div id="container">
    &lt;strong>Ready to start?&lt;/strong>
    &lt;ion-button color="primary" (click)="openCamera()">Take photos&lt;/ion-button>
  &lt;/div>
&lt;/ion-content>
</pre>



<p>and let&#8217;s adjust the <strong>home.page.scss</strong> for a better design</p>



<pre class="EnlighterJSRAW" data-enlighter-language="css" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#container {
  display: flex;
  flex-direction: column;
  text-align: center;
  position: absolute;
  left: 0;
  right: 0;
  top: 10%;
}

#container strong {
  font-size: 20px;
  line-height: 26px;
}

#container p {
  font-size: 16px;
  line-height: 22px;
  color: #8c8c8c;
  margin: 0;
}

#container a {
  text-decoration: none;
}</pre>



<p>Our screen should look like this:</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="494" height="873" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/08/Selection_003-2.png" alt="" class="wp-image-473" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/08/Selection_003-2.png 494w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/08/Selection_003-2-170x300.png 170w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/08/Selection_003-2-400x707.png 400w" sizes="(max-width: 494px) 100vw, 494px" /></figure>






<p>Ok now we can implement the <strong>openCamera()</strong> method in the <strong>home.page.ts</strong> file</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component } from '@angular/core';
import { MediaCapture, MediaFile, CaptureError, CaptureImageOptions } from '@ionic-native/media-capture/ngx';


@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

 
  constructor(private mediaCapture: MediaCapture) { 

  }

  openCamera(){
    let options: CaptureImageOptions = { limit: 3 }
    this.mediaCapture.captureImage(options).then((data: MediaFile[]) => {
      console.log(data)
    },
    (err: CaptureError) => {
      console.error(err)
    });
  }

}
</pre>



<p>and we need to modify the<strong> home.module.ts</strong> file to declare the MediaCapture provider plugin: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { HomePage } from './home.page';

import { HomePageRoutingModule } from './home-routing.module';
import { MediaCapture } from '@ionic-native/media-capture/ngx';


@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    HomePageRoutingModule
  ],
  providers:[MediaCapture],
  declarations: [HomePage]
})
export class HomePageModule {}
</pre>



<p>Ok lot of code but the most important part is the <strong>openCamera()</strong> method and more specifically the following instructions:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> let options: CaptureImageOptions = { limit: 3 }
    </pre>



<p>We indicate that the user would be able to take up to 3 photos. If you don&#8217;t specify a <strong>limit</strong> value the default will be only one photo. Once photos taken, we should receive an array with the images information or an error if something bad occured</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">this.mediaCapture.captureImage(options).then((data: MediaFile[]) => {
      console.log(data)
    },
    (err: CaptureError) => {
      console.error(err)
    });</pre>



<h2>Testing the photo capture on real device with capacitor</h2>



<p>At this stage, we would like to run our app in a real mobile device to have a look of what&#8217;s happening and if everything is working fine. With capacitor, it is super easy to do so. First we build our app</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic build</pre>



<p>Then since i will test with an android device, i need to add the android platform to capacitor</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">npx cap add android
npx cap sync</pre>



<p>Now i plug in my android device to usb port and launch the command</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic capacitor run android --livereload --external</pre>



<p>This command will open <strong>Android Studio</strong> and i just need to hit the run button. The Ionic application will be deployed on my android device with live mode, meaning that if i made a change in the code, the application will update automatically and reload to reflect the changes.</p>



<p><em>Please notice that your android device must be in debug mode if you want to run applications on it from Android Studio </em></p>



<p><em>If you are using an iPhone just do the same with <strong>npx cap add ios</strong> and <strong>ionic capacitor run ios &#8211;livereload &#8211;external</strong></em></p>



<p>The application running on my android device, i can click the Take photo button and the camera preview appears and let me take one photo. Then the view briefly disappears and appears again and i can take another photo. Same process happens once more. Finally after my 3 photos capture, the camera preview closes and i can see in the logs:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> http://192.168.1.51:8100/home-home-module.js - Line 200 - Msg: [object Object],[object Object],[object Object]</pre>



<p>I have 3 objects in my array list which is a good news since i took 3 photos. For better debugging just change the console.log output to show json data</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">console.log(JSON.stringify(data))
</pre>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[
{"name":"1597995590266.jpg",
"localURL":"cdvfile://localhost/sdcard/Pictures/1597995590266.jpg",
"type":"image/jpeg","lastModified":null,"lastModifiedDate":1597995598000,"size":5735308,"start":0,"end":0,
"fullPath":"file:///storage/emulated/0/Pictures/1597995590266.jpg"},{"name":"1597995598705.jpg",
"localURL":"cdvfile://localhost/sdcard/Pictures/1597995598705.jpg",
"type":"image/jpeg","lastModified":null,"lastModifiedDate":1597995603000,"size":5915682,"start":0,"end":0,
"fullPath":"file:///storage/emulated/0/Pictures/1597995598705.jpg"},{"name":"1597995603724.jpg","localURL":"cdvfile://localhost/sdcard/Pictures/1597995603724.jpg","type":"image/jpeg","lastModified":null,"lastModifiedDate":1597995609000,"size":5962056,"start":0,"end":0,"fullPath":"file:///storage/emulated/0/Pictures/1597995603724.jpg"}]</pre>



<p>As expected the array contains the information about the 3 photos taken and where the files are stored.</p>



<p><em>The plugin behaviour/user experience is not so great because user needs to take 3 photos (not 2 or one) to exit the camera mode </em>(or close the camera application). </p>



<p>Now if you want to read the files, i will suggest to use the <a href="https://capacitorjs.com/docs/apis/filesystem" target="_blank" rel="noreferrer noopener">Capacitor Filesystem plugin</a> and more particulary the <strong>readFile</strong> methods</p>



<p>Then with the files, you can display them or send them to a server like we learned in this <a href="https://www.ionicanddjangotutorial.com/how-to-upload-image-from-ionic-application-to-django-using-django-rest-framework/" target="_blank" rel="noreferrer noopener">tutorial</a>.</p>



<h2>Read and display images taken with the media capture plugin into our Ionic application</h2>



<p>Let&#8217;s modify our html page to display pictures in a grid:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header [translucent]="true">
  &lt;ion-toolbar>
    &lt;ion-title>
      Multiple photo capture
    &lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>

&lt;ion-content [fullscreen]="true">
  &lt;div id="container">
    &lt;strong>Ready to start?&lt;/strong>
    &lt;ion-button color="primary" (click)="openCamera()">Take photos&lt;/ion-button>
  
  &lt;div class="section-body">
    &lt;ion-row>
      &lt;ion-col size="4" *ngFor="let item of pictures">
        &lt;div class="image-card">
          &lt;img [src]="item" alt="">
        &lt;/div>
      &lt;/ion-col>
    &lt;/ion-row>
  &lt;/div>
  
&lt;/div>
&lt;/ion-content>
</pre>



<p>Now we modify our code to use the Capacitor filesystem plugin , browse each photo of our array and add them to a the new array pictures which is displayed in our html page</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component, NgZone } from '@angular/core';
import { MediaCapture, MediaFile, CaptureError, CaptureImageOptions } from '@ionic-native/media-capture/ngx';
import { Plugins, FilesystemDirectory, FilesystemEncoding } from '@capacitor/core';
const { Filesystem } = Plugins;

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  pictures=[]
  constructor(private mediaCapture: MediaCapture) { 

  }

  openCamera(){
    let options: CaptureImageOptions = { limit: 1 }
    this.mediaCapture.captureImage(options).then(async (data: MediaFile[]) => {
      console.log(JSON.stringify(data))
      for (let photo of data){
        let fullPath = photo.fullPath
        try{
          let photoBinary = await Filesystem.readFile({path:fullPath})
          if (photoBinary.data){
            let fullData = "data:" + photo.type + ";base64," + photoBinary.data
            this.pictures.push(fullData)
          }
        
        }
        catch(error){
          console.log(error)
        }
       
      }
    },
    (err: CaptureError) => {
      console.error(err)
    });
  }

}
</pre>



<p>Capacitor readFile returns the binary data in a based64 encoded so we will use this value with the type value of the photo (image/jpeg) to be able to display it correctly in our img tag:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">let photoBinary = await Filesystem.readFile({path:fullPath})
          if (photoBinary.data){
            let fullData = "data:" + photo.type + ";base64," + photoBinary.data
            this.pictures.push(fullData)
          }</pre>



<p><em>Please notice that with Android 10 a change occurs and we need to add: <strong>android:requestLegacyExternalStorage=&#8221;true&#8221;</strong> into our AndroidManifest.xml file otherwise the application will crash</em></p>



<pre class="EnlighterJSRAW" data-enlighter-language="xml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:requestLegacyExternalStorage="true"
        android:theme="@style/AppTheme"></pre>



<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-capture-multiple-photo-with-ionic-and-capacitor/">How to capture multiple photo with Ionic and Capacitor ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to search google places with Ionic ?</title>
		<link>https://www.ionicanddjangotutorial.com/how-to-search-google-places-with-ionic-and-display-results/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Wed, 05 Aug 2020 08:51:46 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=245</guid>

					<description><![CDATA[<p>In this tutorial, i will show you how to use google places sdk to search places in a ionic application&#8230; <a href="https://www.ionicanddjangotutorial.com/how-to-search-google-places-with-ionic-and-display-results/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-search-google-places-with-ionic-and-display-results/">How to search google places with Ionic ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this tutorial, i will show you how to use google places sdk to search places in a ionic application and display results to our users.</p>



<p>The <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/googleplaces/googleplaces" target="_blank" aria-label="undefined (opens in a new tab)" rel="noreferrer noopener">source code repository</a></p>



<span id="more-245"></span>



<h2>Generate a Google places api key and add it to the project</h2>



<p>To use the Google places API, you need to have an API Key and you can get one <a aria-label="undefined (opens in a new tab)" href="https://developers.google.com/places/web-service/get-api-key" target="_blank" rel="noreferrer noopener">here</a></p>



<p>For this tutorial, we will create a new ionic application. Launch a terminal and enter:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ionic start googleplaces blank --capacitor --project-id=googleplaces --package-id=com.ionicandjdangotutorials.googleplaces
</pre>



<p>With the <strong>&#8211;project-id</strong> argument we can specify our application name and with the <strong>&#8211;package-id</strong> argument the bundle identifier of our application.</p>



<p>If you are not familiar with these values you can learn more <a href="https://cocoacasts.com/what-are-app-ids-and-bundle-identifiers/" target="_blank" aria-label="undefined (opens in a new tab)" rel="noreferrer noopener">here</a>.</p>



<p>Now we can add our Google Places API key in the <strong>index.html</strong> file of our ionic project:</p>



<pre class="wp-block-code"><code> &lt;script src="https://maps.googleapis.com/maps/api/js?v=3&amp;key=YOURAPIKEY&amp;libraries=places">&lt;/script></code></pre>



<p>Replace the <strong>YOURAPIKEY</strong> with the value you obtain from google. This line tells Ionic to load the Google places javascript sdk.</p>



<h2>Add Google places search with Ionic</h2>



<p>We will modify the <strong>home.page.html </strong>file to add a search box and add a list of search results.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;ion-header no-border mode="ios">
  &lt;ion-toolbar mode="ios">
    &lt;ion-title>Search google places&lt;/ion-title>
  &lt;/ion-toolbar>
&lt;/ion-header>
&lt;ion-content>
  &lt;div class="page-flex-align" padding>
    &lt;div class="top-content">
      &lt;h1>Destination address&lt;/h1>
      &lt;p>Please type the address you would like to find&lt;/p>
      &lt;ion-searchbar class="location-search" mode="ios" [(ngModel)]="autocomplete.query" [showCancelButton]="false" (ionInput)="updateSearch()"
      (ionCancel)="dismiss()" (ionClear)="dismiss()" placeholder="">
    &lt;/ion-searchbar>
    &lt;p class="small" *ngIf="selectedItem">{{autocomplete.query}}&lt;/p>

      
      &lt;ion-list lines="full">
     
        &lt;ion-item  *ngFor="let item of items" (click)="chooseItem(item)">
          &lt;ion-icon slot="end" color="success" size="small" class="wg-arrow-line-left">
          &lt;/ion-icon>
          &lt;ion-label text-wrap>
            &lt;h2>{{item.structured_formatting.main_text}}&lt;/h2>
            &lt;p>{{item.structured_formatting.secondary_text}}&lt;/p>
          &lt;/ion-label>
        &lt;/ion-item>
      &lt;/ion-list>
      &lt;ion-button [disabled]="buttonDisabled" mode="ios" expand="block" shape="round" (click)="validDestination()">
        Validate&lt;/ion-button>
    &lt;/div>
  &lt;/div>
&lt;/ion-content></pre>






<p>We store the value entered in the search box in a variable called <strong>autocomplete.query</strong> </p>



<pre class="wp-block-code"><code>&#091;(ngModel)]="autocomplete.query" &#091;showCancelButton]="false" (ionInput)="updateSearch()"</code></pre>



<p>And each time an input is typed, we call the method <strong>updateSearch()</strong>.</p>



<p>Now let&#8217;s move on to the implementation. First we will geolocate the user using <strong>Capacitor</strong>. To do this edit the <strong>home.page.ts</strong> and add the method </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">  geoloc() {
    return new Promise(async resolve => {
      if (this.platform.is('capacitor')) {
        const position = await Geolocation.getCurrentPosition();
        if (position){
          resolve(position);
        }
        else{
        }
        console.log("------  PLATFORM capacitor");
      }
      else {
        // webgeoloc
      
        if (navigator.geolocation) {
          navigator.geolocation.getCurrentPosition(
            position => {
              console.log("============= POSITION  ================");
              console.log(position)
              resolve(position);
            },
            error => {
              resolve(false);
            }
          );
        }
      }
    })
  }</pre>



<p>We check if the user is on mobile an use capacitor native geoloc plugin to get the coordinates of his position, otherwise we use the browser geolocation. This method is called when our view is entered</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> async ionViewWillEnter() {
    this.items=[]
    this.autocomplete.query=""
    const position = await Geolocation.getCurrentPosition();
     if (position) {
      console.log(position)
       this.currentLat = position.coords.latitude
       this.currentLon = position.coords.longitude
     }
  } </pre>



<p>We also initialize an empty <strong>items</strong> array which will contain the google places results and our <strong>autocomplete.query</strong> variable that will contains the input of the user. </p>



<p>We also need to initialize the Google places API as soon as our page is initialized</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> constructor(
    public platform: Platform) {
    this.initPage()
  }

  initPage() {
    // Create a new session token.
    this.sessionToken = new google.maps.places.AutocompleteSessionToken();
    this.acService = new google.maps.places.AutocompleteService();
    this.items = [];
    this.autocomplete = {
      query: ''
    };
  }
</pre>



<p>The <strong>initPage() </strong>method creates a new session token and initialise the AutocompleteService using the <a aria-label="undefined (opens in a new tab)" href="https://developers.google.com/maps/documentation/javascript/places" target="_blank" rel="noreferrer noopener">google places sdk </a></p>



<p>Now the most important method is to get places each time our user enters a value</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
  updateSearch() {
    console.log('modal > updateSearch '+this.autocomplete.query);
    if (this.autocomplete.query == '') {
      this.items = [];
      this.buttonDisabled = true
      return;
    }
    let self = this;
    let config: any;
    if (this.currentLat) {
      let myLatLng = new google.maps.LatLng({lat: this.currentLat, lng: this.currentLon}); 
      config = {
        types: ['geocode'], // other types available in the API: 'establishment', 'regions', and 'cities'
        input: this.autocomplete.query,
        sessionToken: this.sessionToken,
        language: "EN",
        location: myLatLng,
        radius: 500 * 100 //50Km
        //, 
        //componentRestrictions: { country: 'FR,ES,BE' } 
      }

    }
    else {
      config = {
        types: ['geocode'], // other types available in the API: 'establishment', 'regions', and 'cities'
        input: this.autocomplete.query,
        sessionToken: this.sessionToken,
        language:"EN"
        //location: {lat: -34, lng: 151},
        //radius: 1000 * 100 //100Km
        //, 
        //componentRestrictions: { country: 'FR,ES,BE' } 
      }

    }

    console.log(config)
    this.acService.getPlacePredictions(config, function (predictions, status) {
      //console.log('modal > getPlacePredictions > status > ', status);
      self.items = [];
      //console.log("predictions "+JSON .stringify(predictions)) 
      if (predictions) {
        predictions.forEach(function (prediction) {
          self.items.push(prediction);
        });
      }
    });

  }</pre>



<p>To get places we use the <strong>getPlacePredictions</strong> method which will return a list of predictions (the places themselves)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> this.acService.getPlacePredictions(config, function (predictions, status) {
      //console.log('modal > getPlacePredictions > status > ', status);
      self.items = [];
      //console.log("predictions "+JSON .stringify(predictions)) 
      if (predictions) {
        predictions.forEach(function (prediction) {
          self.items.push(prediction);
        });
      }
    });</pre>



<p>We need to pass to this method a <strong>config </strong>object</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> config = {
        types: ['geocode'], // other types available in the API: 'establishment', 'regions', and 'cities'
        input: this.autocomplete.query,
        sessionToken: this.sessionToken,
        language: "EN",
        location: myLatLng,
        radius: 500 * 100 //50Km
        //, 
        //componentRestrictions: { country: 'FR,ES,BE' } 
      }</pre>



<p>Many <a aria-label="undefined (opens in a new tab)" href="https://developers.google.com/maps/documentation/javascript/places-autocomplete#add-autocomplete" target="_blank" rel="noreferrer noopener">parameters</a> can be set such as the type of results that we want, the langage in which results should be, the location of the user, the radius of the search,&#8230;</p>



<p>Once our results are displayed on our screen if the user selects a particular value, we can assign it </p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
 validDestination() {
    if (this.selectedItem == undefined) {
      // should display a message to the user
      console.log("Enter a destination")
    }
    else {
      let latitude = this.selectedItem.latitude;
      let longitude = this.selectedItem.longitude;
      console.log("Ok selected item "+JSON.stringify(this.selectedItem))
    }
  }

  chooseItem(item: any) {
    console.log('modal > chooseItem > item > ', item);
    console.log(item)
    this.selectedItem = item;
    this.items = [];
    this.autocomplete.query = item.structured_formatting.main_text + " - " + item.structured_formatting.secondary_text;
    this.buttonDisabled = false;
    if (item.structured_formatting.secondary_text.indexOf(",")>0){
      let lieuSplitted = item.structured_formatting.secondary_text.split(",",1); 
      this.destinationCity  = lieuSplitted[0]
    }
    else{
      this.destinationCity  = item.structured_formatting.main_text
    }
  }</pre>






<p>And voila. You can do whatever you want, once the user selects a google places value.</p>



<figure class="wp-block-image size-large"><img loading="lazy" width="551" height="910" src="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/08/Selection_008.png" alt="Ionic Google places" class="wp-image-253" srcset="https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/08/Selection_008.png 551w, https://www.ionicanddjangotutorial.com/wp-content/uploads/2020/08/Selection_008-182x300.png 182w" sizes="(max-width: 551px) 100vw, 551px" /></figure>



<p>Here is the full <strong>home.page.ts </strong>code with import and variable declarations and get the full project code here : <a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/googleplaces/googleplaces" target="_blank" rel="noreferrer noopener">source code repository</a></p>



<pre class="wp-block-code"><code>import { Component, OnInit } from '@angular/core';
import { Platform } from '@ionic/angular';
const { Geolocation } = Plugins;
import { Plugins } from '@capacitor/core';
declare var google;

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: &#091;'home.page.scss'],
})
export class HomePage {
   
  items: any;
  autocomplete: any;
  acService: any;
  placesService: any;
  selectedItem: any;
  buttonDisabled = true;
  sessionToken: any;
  currentLon: any;
  currentLat: any;
  destinationCity : string;
  zipCode : string="";
  constructor(
    public platform: Platform) {
    this.initPage()
  }



  geoloc() {
    return new Promise(async resolve => {
      if (this.platform.is('capacitor')) {
         
        const position = await Geolocation.getCurrentPosition();
        if (position){
          resolve(position);
        }
        else{
           
        }
        
        console.log("------  PLATFORM capacitor");
       
      }
      else {
        // webgeoloc
      
        if (navigator.geolocation) {
          navigator.geolocation.getCurrentPosition(
            position => {
              console.log("============= POSITION  ================");
              console.log(position)
              //Hardcoded 

              resolve(position);
            },
            error => {

              resolve(false);
            }
          );
        }
      }
    })
  }

  goBack() {
   
  }

  dismiss() {
    console.log("Clear search")
    this.items = &#091;];
    this.autocomplete = {
      query: ''
    };
   
  }
 
  initPage() {
    // Create a new session token.
    this.sessionToken = new google.maps.places.AutocompleteSessionToken();
    this.acService = new google.maps.places.AutocompleteService();
    this.items = &#091;];
    this.autocomplete = {
      query: ''
    };
  }

  async ionViewWillEnter() {
    this.items=&#091;]
    this.autocomplete.query=""
    
    const position = await Geolocation.getCurrentPosition();
     
     if (position) {
      console.log(position)
       this.currentLat = position.coords.latitude
       this.currentLon = position.coords.longitude
     }
    
  } 

  ngOnInit() {

  } 
   
  validDestination() {
    if (this.selectedItem == undefined) {
      // should display a message to the user
      console.log("Enter a destination")
    }
    else {
      let latitude = this.selectedItem.latitude;
      let longitude = this.selectedItem.longitude;
      console.log("Ok selected item "+JSON.stringify(this.selectedItem))
    }
  }

  chooseItem(item: any) {
    console.log('modal > chooseItem > item > ', item);
    console.log(item)
    this.selectedItem = item;
    this.items = &#091;];
    this.autocomplete.query = item.structured_formatting.main_text + " - " + item.structured_formatting.secondary_text;
    this.buttonDisabled = false;
    if (item.structured_formatting.secondary_text.indexOf(",")>0){
      let lieuSplitted = item.structured_formatting.secondary_text.split(",",1); 
      this.destinationCity  = lieuSplitted&#091;0]
    }
    else{
      this.destinationCity  = item.structured_formatting.main_text
    }
  }

  updateSearch() {
    console.log('modal > updateSearch '+this.autocomplete.query);
    if (this.autocomplete.query == '') {
      this.items = &#091;];
      this.buttonDisabled = true
      return;
    }
    let self = this;
    let config: any;
    if (this.currentLat) {
      let myLatLng = new google.maps.LatLng({lat: this.currentLat, lng: this.currentLon}); 
      config = {
        types: &#091;'geocode'], // other types available in the API: 'establishment', 'regions', and 'cities'
        input: this.autocomplete.query,
        sessionToken: this.sessionToken,
        language: "EN",
        location: myLatLng,
        radius: 500 * 100 //50Km
        //, 
        //componentRestrictions: { country: 'FR,ES,BE' } 
      }

    }
    else {
      config = {
        types: &#091;'geocode'], // other types available in the API: 'establishment', 'regions', and 'cities'
        input: this.autocomplete.query,
        sessionToken: this.sessionToken,
        language:"EN"
        //location: {lat: -34, lng: 151},
        //radius: 1000 * 100 //100Km
        //, 
        //componentRestrictions: { country: 'FR,ES,BE' } 
      }

    }

    console.log(config)
    this.acService.getPlacePredictions(config, function (predictions, status) {
      //console.log('modal > getPlacePredictions > status > ', status);
      self.items = &#091;];
      //console.log("predictions "+JSON .stringify(predictions)) 
      if (predictions) {
        predictions.forEach(function (prediction) {
          self.items.push(prediction);
        });
      }
    });

  }
}
</code></pre>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-search-google-places-with-ionic-and-display-results/">How to search google places with Ionic ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to upload image from Ionic to Django using Django Rest Framework ?</title>
		<link>https://www.ionicanddjangotutorial.com/how-to-upload-image-from-ionic-application-to-django-using-django-rest-framework/</link>
		
		<dc:creator><![CDATA[Christophe Surbier]]></dc:creator>
		<pubDate>Mon, 27 Jul 2020 15:18:15 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://www.ionicanddjangotutorial.com/?p=168</guid>

					<description><![CDATA[<p>In this tutorial, i will show you how to take an image using Capacitor Camera plugin and upload it to&#8230; <a href="https://www.ionicanddjangotutorial.com/how-to-upload-image-from-ionic-application-to-django-using-django-rest-framework/" class="more-link">Continue Reading <span class="meta-nav">&#8594;</span></a></p>
<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-upload-image-from-ionic-application-to-django-using-django-rest-framework/">How to upload image from Ionic to Django using Django Rest Framework ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this tutorial, i will show you how to take an image using Capacitor Camera plugin and upload it to our Django backend from our ionic application.<br>You will find the repository source code&nbsp;<a href="https://github.com/csurbier/ionicanddjangotutorial/tree/master/uploadimage" target="_blank" rel="noreferrer noopener">here.</a></p>



<span id="more-168"></span>



<h2>Take or choose an image using Capacitor Camera plugin</h2>



<p>Let&#8217;s imagine we would allow our user to set or change his profile picture. First we will add a new page</p>



<pre class="wp-block-code"><code>ionic g page UploadPicture</code></pre>



<p>Then as always, i will move the new created page in the&nbsp;<strong>pages</strong>&nbsp;folder and modiy the app-routing.module.ts to reflect this change</p>



<pre class="wp-block-code"><code> { path: 'upload-picture', loadChildren: './pages/upload-picture/upload-picture.module#UploadPicturePageModule' }</code></pre>



<p>Ok now we can edit the&nbsp;<strong>upload-picture.page.ts</strong>&nbsp;file to include the capacitor&nbsp;<strong>Camera</strong>&nbsp;plugin which will be used to take or select a photo</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { Component, OnInit } from '@angular/core';
import { Plugins, CameraResultType,CameraSource } from '@capacitor/core';
const { Camera } = Plugins;
@Component({
  selector: 'app-upload-picture',
  templateUrl: './upload-picture.page.html',
  styleUrls: ['./upload-picture.page.scss'],
})
export class UploadPicturePage implements OnInit {
  constructor() { }
  ngOnInit() {
  }
}</pre>



<p>And then implement a method which will ask the user if he wants to choose or take a photo (<strong>default behaviour of the capacitor Camera plugin</strong>)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">async chooseOrTakePicture() {
    const image = await Plugins.Camera.getPhoto({
      quality: 90,
      allowEditing: false,
      resultType: CameraResultType.Base64,
      //source: CameraSource.Camera
    }).catch((error)=>{
      console.log(error)
    })
    // variable image should contain our base64 image
  }</pre>



<p>You can find all the options available for the&nbsp;<strong>Camerag plugin</strong>&nbsp;on&nbsp;<a href="https://capacitorjs.com/docs/apis/camera#type-162680" target="_blank" rel="noreferrer noopener">the Capacitor documentation page</a></p>



<h2>Convert a base64 image into a blob in our Ionic application</h2>



<p>The capacitor Camera plugin should return the image in base64 format because we ask for it using the line</p>



<pre class="wp-block-code"><code>resultType: CameraResultType.Base64,</code></pre>



<p>It is better to transform the image into a binary file because sometimes some weird conversion errors can occured between javascript base64 and python base64 format. </p>



<p>To transform a base64 image to a blob we can use this code</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
  public b64toBlob(b64Data, contentType = '', sliceSize = 512) {
    const byteCharacters = atob(b64Data);
    const byteArrays = [];
 
    for (let offset = 0; offset &lt; byteCharacters.length; offset += sliceSize) {
      const slice = byteCharacters.slice(offset, offset + sliceSize);
 
      const byteNumbers = new Array(slice.length);
      for (let i = 0; i &lt; slice.length; i++) {
        byteNumbers[i] = slice.charCodeAt(i);
      }
 
      const byteArray = new Uint8Array(byteNumbers);
      byteArrays.push(byteArray);
    }
 
    const blob = new Blob(byteArrays, { type: contentType });
    return blob;
  }</pre>



<p>So now our method will look like</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
  async chooseOrTakePicture() {
    const image = await Plugins.Camera.getPhoto({
      quality: 90,
      allowEditing: false,
      resultType: CameraResultType.Base64,
      //source: CameraSource.Camera
    }).catch((error)=>{
      console.log(error)
    })
    // variable image should contain our base64 image
    if (image){
      // convert base64 image to blob
      let blob = this.b64toBlob(image.base64String)
      
    }
   
  }</pre>






<p></p>



<h2>Upload image by using a form from our Ionic application to Django</h2>



<p>Now that we have our binary image we will use a standard angular form to send data to our Django backend. We will first need to check if network is available and if so, we can send the form</p>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> if (this.apiService.networkConnected){
        //Create a form to send the file
        const formData = new FormData();
        //Generate a fake filename
        let name =Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 10);
        formData.append('file', blob, name+<code data-enlighter-language="generic" class="EnlighterJSRAW">.${image.format}</code>);
        formData.append('name', name);
        this.apiService.uploadPhoto(formData).subscribe((value)=>{
          //server return value
        })
      }
      else{
        this.apiService.showNoNetwork()
      }</pre>



<p>The form will be composed of the file and a fake name that we randomly generate.</p>



<p>We need to create the <strong>uploadPhoto</strong> method into our usual django-api service and will use the <strong>fetch</strong> method to send the form. We also need to define our new api endpoint which will be called <strong>uploadphotobinary</strong></p>



<pre class="wp-block-code"><code> getUploadPhotoUrlBinary= this.virtualHostName + this.apiPrefix + "/uploadphotobinary/"</code></pre>



<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">uploadPhoto(formData) {
 
  var myHeaders = new Headers();
  myHeaders.append("Authorization", "Bearer "+ this.tokenSSO);

  var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: formData
  };
  return Observable.create(observer => {
  fetch(this.getUploadPhotoUrlBinary, requestOptions)
  .then(response => response.json())
  .then(result => {
      
      observer.next(result)
  })
  .catch(error => {console.log('error', error);
      observer.next()});
  })
}
</pre>



<h2>Receive and save an image using Django Rest Framework</h2>



<p>To receive and save the image in our Django backend, we first need to declare the model that will be used to save photo. So we can add the following models to our <strong>models.py </strong>file</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class Photo(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4)
    file = models.ImageField(upload_to='dossiers/(%Y_%m)/', null=True, blank=True)
    createdAt = models.DateTimeField(auto_now_add=True)
    updatedAt = models.DateTimeField(auto_now=True)</pre>



<p>Now we declare our new endpoint in the <strong>urls.py </strong>file</p>



<pre class="wp-block-code"><code> url(r'^uploadphotobinary/$', PhotoUploadView.as_view()),</code></pre>



<p>Then we declare the <strong>PhotoUploadView</strong> in our views.py file</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">//Adding some imports
from rest_framework.views import APIView
from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response
from rest_framework import status
class PhotoUploadView(APIView):
    parser_class = (FileUploadParser,)

    def post(self, request, *args, **kwargs):
      print("=== DANS POST METHOD")
      print(request.data)
      file_serializer = PhotoUploadSerializer(data=request.data)

      if file_serializer.is_valid():
          file_serializer.save()
          return Response(file_serializer.data, status=status.HTTP_201_CREATED)
      else:
          print(file_serializer.errors)
          return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
</pre>



<p>and we declare the new <strong>PhotoUploadSerializer</strong> in our <strong>serializers</strong>.py file</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">
class PhotoUploadSerializer(ModelSerializer):
    class Meta:
        model = Photo
        fields = '__all__'
</pre>



<p>The post <a rel="nofollow" href="https://www.ionicanddjangotutorial.com/how-to-upload-image-from-ionic-application-to-django-using-django-rest-framework/">How to upload image from Ionic to Django using Django Rest Framework ?</a> appeared first on <a rel="nofollow" href="https://www.ionicanddjangotutorial.com">Ionic and Django Tutorial</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
