dancingbush
dancingbush

Reputation: 2281

How to navigate to an external web url form ionic 5 app

I want to navigate to a webpage url from the ionic html page. I have tried following :

  1. This example

    {{test.refWebsite}}

  2. This example:

    <ion-router-link href="{{test.refWebsite}}">{{test.refWebsite}}</ion-router-link>
    
  3. And finally this:

    <a [routerLink]="{{test.refWebsite}}">{{test.refWebsite}}</a>
    

Ever example navigates to my 'page-not-found' route defined in the app.routng.module:

  {
    path: '**',
    redirectTo: 'page-not-found'
  },

So my question is how do I navigate to an external website form my ionic app html page?

Update: Following the Capacitor documentation I have implemented as follows:

import { Plugins } from '@capacitor/core';

const { Browser  } = Plugins; 

async  openBrowser(webpage){
    console.log("home.page.ts: Opneing Web browser: " + webpage);
    await Browser.open({url: webpage});
  }

//HTML implementation

 <p (click)="openBrowser(test.refWebsite)">Referral Center: <a> {{test.refWebsite}}</a></p>

Console output shows a valid URL is been passed tot he function:

home.page.ts: Opneing Web browser: www.sheffieldchildrens.nhs.uk/sdgs/oncology-genetics

However I am still getting the 'page-not-found' opening, although the URL (webpage) is valid, any input appreciated as I can't see why this is happening.

ADDING COMPLETE CLASS FILE:

home.page.ts

mport { Component, ɵChangeDetectorStatus } from '@angular/core';
import { AlertController, ModalController } from '@ionic/angular';
import { Student, Test, StudentService } from '../services/student.service';
import { StudentModalPage } from '../student-modal/student-modal.page';
import {FormControl, ReactiveFormsModule } from '@angular/forms'; 
import { debounceTime} from "rxjs/operators";
import { serializeNodes } from '@angular/compiler/src/i18n/digest';
import { IonSlides, MenuController } from '@ionic/angular';
import { Storage} from '@ionic/storage';
import { CallNumber } from '@ionic-native/call-number/ngx';
import { Plugins } from '@capacitor/core';
import { stringify } from '@angular/compiler/src/util';

const { Browser  } = Plugins; 

// import { Plugins } from '@capacitor/core';
// const { Storage } = Plugins;


@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  //Firelds
  public students : Student[];
  public tests : Test[];
  public searchTerm : string;
  public searchControl : FormControl;
  public searching : any = false;
  public adminUser : any = false;
  public cacheData : Test[];
  public referralPhoneNumber : string;

  constructor(
    private service : StudentService,
    private alertCtrl : AlertController,
    private modalCtrl : ModalController,
    private menu : MenuController,
    private storage : Storage,
    private callNumber : CallNumber
    ) {
      this.searchControl = new FormControl();
    }

    
    ionViewDidEnter(){
      this.menu.enable(true);
      //See if admin user
     //this.adminUser = Storage.get({key: 'adminUser'});
     this.storage.get('adminUser').then((val) =>{
      console.log("home.pg: value of admin user = ", val);
      this.adminUser = val;
      console.log("home.page: Is user admin user: " + this.adminUser);

    });

    //this.storage.set('adminUser', true);
    
    }

  ngOnInit() {
    /**
     * Get data from the SQL DB and store in local array Test
     * This calls student.serce which in turn calls the GET API
     * Set up serach Form, debounce tme is time to wait 
     * before before triggerng serah / observable
     */

     


     this.searching = true;
     console.log("Home.page: ngOnOt- Gettnig all data...");
    this.service.getAll().subscribe(response => {
      console.log("Home.ts: getAll call = " + response);
      //this.students = response;
      this.tests = response;
      this.searching = false; //Turn spinner off
      //Cache data 
     // Storage.set
      
    });

    // this.setFilteredTests("");
     this.searchControl.valueChanges
     .pipe(debounceTime(700))
     .subscribe(search=> {
       this.searching = false;
       this.setFilteredTests(search);
       
     });

    
  }

  setFilteredTests(searchTerm : string){
    /**
     * USer teh seah term form user input to filter
     * the test array
     * https://www.joshmorony.com/high-performance-list-filtering-in-ionic-2/
     * Assign a lcoal var tp current tests array which we will use
     * to reasign tests if searh is null or empty. 
     * This saves us making another tme wasting PAI get call
     */
    console.log("Home.page.ts: 44 Ste filter serach bar called, serach = " + searchTerm);
   
    if (searchTerm != ""){
     this.tests =  this.tests.filter(test => {
       
      if (test.investigation.toLowerCase().indexOf(searchTerm.toLowerCase()) == -1){
        console.log("Home.page.ts 73= Serach not found for " + searchTerm + " so searching alais");
       return  test.Alias.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
      }
       console.log("home.oage: 78: Found " + searchTerm + " in test.investigation.");
      return test.investigation.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;

    })
  }else{
    console.log("Home.page.ts: Seathetrm is blank -  " + searchTerm + "; so getting dtta with GET API");
   
    this.service.getAll().subscribe(data => {
      this.tests = data;
      
    })
  }
   
  }

  onSearchInput(){
    /**
     * Turn on spinner when seraching
     * Set to false when loaing page and when serach observable triggers in onIt
     */
    this.searching = true;
  }

  
 async  openBrowser(webpage){
    console.log("home.page.ts: Opneing Web browser: " + webpage);
    await Browser.open({url: webpage});
  }


  removeTest(id : number){
    console.log("Home.page.ts: Remove test id : " + id);

    if (this.adminUser){
    this.alertCtrl
    .create({
      header: 'Delete',
      message: 'are you sure you want to delete this test?',
      buttons : [
        {
          text: 'Yes',
          handler: () => {
            this.service.remove(id).subscribe(() => {
              // Filter the tst array to all test.id's excpet one removed
              this.tests = this.tests.filter(std => std.id !== id);
            });            
          }
        },
        { text: 'No'}
      ]
    })
.then(alertE1 => alertE1.present());
  }//if this admin user
  else{
    this.alertCtrl.create({
      header: 'Delete',
      message: 'You must be an administrator  to make these changes',
      buttons : [
        {
          text: 'OK'
        }
      ]
    })
    .then(alertE1 => alertE1.present());
  }
    }


    callPhoneNumber(phoneNumber) : void{
      //Usre clicked call number, https://ionicframework.com/docs/native/call-number
      let correctNumber = phoneNumber.replace(/\s/g,"");//remove whitespace
      console.log("home.page: call Nimber() " + correctNumber);

      this.callNumber.callNumber(phoneNumber, true)
      .then(res => console.log("Launched Dialer", res))
      .catch(err => console.log("Error launching handler", err));



  //     this.callNumber.callNumber("18001010101", true)
  // .then(res => console.log('Launched dialer!', res))
  // .catch(err => console.log('Error launching dialer', err));

    }
    addStudent(){
      /**
       * Call studentmodal page
       * Also when Add Student page is closed 
       * we will capture what teh page sends back  when navCtlr.dismiss(params)
       * is called, so we can upadte any new test additions to this page
       * The modal presnet teh Add Student page, .then() return inof sent back 
       * from this page.
       */
      console.log("Home.ts: Add test");
      if (this.adminUser){
      this.modalCtrl.create({
        component: StudentModalPage
      })
      .then(modal => {
        modal.present();
        return modal.onDidDismiss();
      })
      .then(({ data, role}) => {
        if ( role === 'created'){
          //add new test from Add Test page to test arrauy
          console.log("Home.page: 148- New test created : " + data + "- Adding to Tests Array view.")
          //this.students.push(data);
          this.tests.push(data);
        }
      });
    }//if this admin user
  else{
    this.alertCtrl.create({
      header: 'Add',
      message: 'You must be an administrator  to make these changes',
      buttons : [
        {
          text: 'OK'
        }
      ]
    })
    .then(alertE1 => alertE1.present());
  }

    }

    updateStudent(test){
      /* We need to pas the student object ot the 
      * StidentModalPage with modalCtrl
      * And we also handle the retuned data from Student Modal Page
      */
       
      console.log("Home.page.ts: upadte, passing to StudentModalPage student - " + test.investigation + " : id - " + test.id);
      if (this.adminUser){
      this.modalCtrl.create({
        component : StudentModalPage,
        //componentProps : {student : student}
        componentProps : {test : test}

      })
      .then(modal => {
        modal.present();
        //get returned moddel form Stdent Model page
        console.log("home.page.ts: 92: Getting return data form Stduent Modal page Uodate / PUT");
        return modal.onDidDismiss();
      })
      .then(({ data, role}) => {
        //this.students = this.students.filter(std => {
          this.tests = this.tests.filter(std=> {
          console.log("Home.page.ts: 97: Retrining to home frm Student modal update / PUT with data studnet IFD to update list: " + data.id);
          if (data.id === std.id){
            //return uodated tests array
            return data;
          }
          return std;
        });

      });
    }
    //if this admin user
  else{
    this.alertCtrl.create({
      header: 'Delete',
      message: 'You must be an administrator  to make these changes',
      buttons : [
        {
          text: 'OK'
        }
      ]
    })
    .then(alertE1 => alertE1.present());
  }
    }//upadtes test
    
}

HTML

<ion-header [translucent]="true">
  <ion-toolbar color="primary">
    <ion-buttons slot="start">
      <ion-back-button defaultHref="app/categories">Menu</ion-back-button>
    </ion-buttons>
    <ion-buttons slot="end">
      <ion-button color="primary" (click)="addStudent()">
        Add New
      </ion-button>
    </ion-buttons>
    <ion-title>
      All tests
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content ion-padding color="primary">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">User Manual Tests</ion-title>
    </ion-toolbar>
  </ion-header>

  <!--Serachbar-->
  <ion-searchbar [formControl]="searchControl" (ionChange)="onSearchInput()">
</ion-searchbar> 

<!--Spinner for serach box-->
 <div *ngIf="searching" class="spinner-container">
   <ion-spinner></ion-spinner>
 </div>

  <!--<ion-list *ngFor="let student of students">-->
    <ion-list *ngFor="let test of tests" color="primary"> 
    <ion-item-sliding>

    
    <ion-item >
      <ion-avatar slot="start">
        <div class="avatar">
          <!--{{student.name.substring(0,1).toUpperCase()}}-->
          {{test.investigation.substring(0,1).toUpperCase()}}
        </div>
      </ion-avatar>
      <ion-label>
        <!--
        <h3 class="title">{{student.name}}</h3>
        <p class="subtext">
          {{'From ' + student.address + ' Tel: ' + student.phone}}
        </p>
      -->
      <h3 class="title">{{test.investigation}}</h3>
       <p class="subtext">Speciman Type:
        {{test.SpecimanType}}
        <br> {{test.Comments}}
        <br>Container: 
        {{test.container}}
        <br>Phone:
        {{test.phone}}
        <br>TAT: {{test.TAT}}
        <br>Phoning Criteria: {{test.phoneCriteria}}
        <br>Referred Tests (Y/N): {{test.referred}}
      </p>
      <div class = "subtext" *ngIf="test.referred == 'Y'">

        <a (click)="openBrowser(test.refWebsite)">{{test.refWebsite}}</a>
        <a href="{{test.refWebsite}}">OPEN {{test.refWebsite}}</a>

        <p (click) = "callPhoneNumber(test.refNumber)">
        Refferal Number: <a>{{test.refNumber}}</a></p>
    </div>
      </ion-label>
    </ion-item>
    <ion-item-options slide="end">
      <ion-item-option color="danger" (click) = "removeTest(test.id)">
        <ion-icon name="trash"></ion-icon>

      </ion-item-option>
      <ion-item-option color="success" (click)="updateStudent(test)">
        Edit

      </ion-item-option>
    </ion-item-options>
  </ion-item-sliding>
  </ion-list>
  <!-- <div id="container">
    <strong>Ready to create an app?</strong>
    <p>Start with Ionic <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
  </div> -->

</ion-content>

Upvotes: 1

Views: 4049

Answers (2)

user796446
user796446

Reputation:

The examples you reference are not for opening an external link in a browser. You can know this from the error you are getting since the link is not found within your application. You need to implement an external url solution such as what is found here.

UPDATE AFTER QUESTION UPDATE: Neither of your HTML implementations is correct for the plugin mentioned. You are not trying to open routerLinks, which are internal project pages defined in router modules. You are trying to open an external link, completely separate from your project.

Something like the following will work.

<a (click)="openBrowser(test.refWebsite)">{{test.refWebsite}}</a>

NOTE: Without the href attribute you may see some inconsistent styling. The way I personally do this is to use an ion-text tag and style accordingly.

p.s. I know Capacitor is the "the new shiny" from Ionic, but the documents are still quite poor. In our enterprise application we still use Cordova because even though it is now in Version 3, IMO Capacitor is not ready for prime time.

Upvotes: 1

dancingbush
dancingbush

Reputation: 2281

The url required a ‘https://‘ prefix to work.

OPEN {{test.refWebsite}}

Upvotes: 1

Related Questions