I make one application with ionic 2. I am trying to get a confirmation alert before close the application.
How can I do it ?
export class MyApp{
constructor(public alert: AlertController,public platform: Platform){}
exit(){
let alert = this.alert.create({
title: 'Confirm',
message: 'Do you want to exit?',
buttons: [{
text: "exit?",
handler: () => { this.exitApp() }
}, {
text: "Cancel",
role: 'cancel'
}]
})
alert.present();
}
exitApp(){
this.platform.exitApp();
}
}
If you would like to enable back button exit, add event listener for it and call exit function.
You can use this.platform.registerBackButtonAction(this.exit) for it.
I could find by myself the right solution:
https://forum.ionicframework.com/t/show-a-confirmation-alert-before-app-close-ionic/63313
showedAlert: boolean;
constructor(..., public alertCtrl: AlertController) {
}
initializeApp() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
Splashscreen.hide();
this.showedAlert = false;
// Confirm exit
this.platform.registerBackButtonAction(() => {
if (this.nav.length() == 1) {
if (!this.showedAlert) {
this.confirmExitApp();
} else {
this.showedAlert = false;
this.confirmAlert.dismiss();
}
}
this.nav.pop();
});
});
}
confirmExitApp() {
this.showedAlert = true;
this.confirmAlert = this.alertCtrl.create({
title: "Salir",
message: "¿ Esta seguro que desea salir de la aplicación ?",
buttons: [
{
text: 'Cancelar',
handler: () => {
this.showedAlert = false;
return;
}
},
{
text: 'Aceptar',
handler: () => {
this.platform.exitApp();
}
}
]
});
this.confirmAlert.present();
}
Ionic 2+ quick solution: In your app.component.ts try
ngOnInit() {
this.platform.registerBackButtonAction(() => {
if (this.nav.canGoBack()) {
this.nav.pop();
} else {
// Currently on root page
this.appClosePromt();
}
}, 1);
}
appClosePromt() {
let alert = this.alertCtrl.create({
title: '',
message: 'Do you want to exit?',
buttons: [
{
text: 'No',
role: 'cancel',
handler: () => {
// Dismiss
}
},
{
text: 'Exit',
handler: () => {
this.platform.exitApp();
}
}
]
});
alert.present();
}
Related
TS code :
getTab(param){
const tab = [
{
name: 'View',
action: () => {
this.tabService.setSelectedTabValue(this.filterValue);
this.router.navigate([Constants.View_Url], {
queryParams:
{ Id: param?.node.data.limitId, Date: param?.node.data.startDate }
});
}
}
];
if (this.CommonUtil.isUserEdit(param?.node.data.status)) {
tab.push(
{
name: 'Edit',
action: () => {
this.tabService.setSelectedTabValue('edit');
this.router.navigate([Constants.Edit_Url], {
queryParams:
{ Id: param?.node.data.limitId, Date: param?.node.data.startDate }
});
}
}
I have tried to write unit test case for if condition, but it is not the covering "action" property and could achieve for negative case also.
Testing lifecycle methods when a VueJS component renders on the transition group.
I've been writing tests for lifecycle methods when the component renders on the transition group of the following VueJS component I've made little progress on getting it to work and would appreciate advice regarding this. I also tried switching between shallow mounting and mounting the component though that seemed to make no difference.
import { shallowMount } from '#vue/test-utils';
import StaggeredTransition from '../src/index';
const staggeredTransitionWrapper = componentData =>
shallowMount(StaggeredTransition, {
...componentData,
});
const staggeredTransition = staggeredTransitionWrapper();
describe('StaggeredTransition.vue', () => {
it('should render a staggered transition component', () => {
expect(staggeredTransition.element.tagName).toBe('SPAN');
expect(staggeredTransition.html()).toMatchSnapshot();
});
it('should mock calling the enter method', () => {
const enterMock = jest.fn();
StaggeredTransition.methods.enter = enterMock;
const staggeredTransitionWrapper2 = componentData =>
shallowMount(StaggeredTransition, { ...componentData });
const staggeredTransition2 = staggeredTransitionWrapper2({
slots: {
default: '<h1 :key="1">Staggered transition test</h1>',
},
});
expect(enterMock).toBeCalled();
});
});
Code for the StaggeredTransition component
<template>
<transition-group
:tag="tag"
:name="'staggered-' + type"
:css="false"
appear
#before-enter="beforeEnter"
#enter="enter"
#leave="leave"
>
<slot />
</transition-group>
</template>
<script>
const { log } = console;
export default {
name: 'StaggeredTransition',
props: {
type: {
type: String,
options: ['fade', 'slide'],
required: false,
default: 'fade',
},
tag: {
type: String,
required: false,
default: 'div',
},
delay: {
type: Number,
required: false,
default: 100,
},
},
methods: {
beforeEnter(el) {
console.log('beforeEnter');
el.classList.add(`staggered-${this.type}-item`);
},
enter(el, done) {
console.log('enter');
setTimeout(() => {
el.classList.add(`staggered-${this.type}-item--visible`);
done();
}, this.getCalculatedDelay(el));
},
leave(el, done) {
console.log('leave');
setTimeout(() => {
el.classList.remove(`staggered-${this.type}-item--visible`);
done();
}, this.getCalculatedDelay(el));
},
getCalculatedDelay(el) {
console.log('getCalculatedDelay');
if (typeof el.dataset.index === 'undefined') {
log(
'data-index attribute is not set. Please set it in order to
make the staggered transition working.',
);
}
return el.dataset.index * this.delay;
},
},
};
</script>
My current stack is:
Django using FCM to send push notifications to an Ionic app. The app uses the phonegap-plugin-push.
I have the problem, that the on notification handler doesn't get called.
Here is the data that I'm sending:
'message': {
'token': '<my-device-token>',
'data': {
'yup': 'okay'
},
'apns': {
'payload': {
'aps': {
'data': 'here is my data',
'badge': 1,
'content-available': 1
},
'notId': 2
}
}
}
The app gets the data, but somehow the on notificatoin handler doesn't get called.
Also here is my code in the app:
import { Injectable } from '#angular/core';
import { Push, PushObject, PushOptions } from '#ionic-native/push';
import { AlertController, Platform } from 'ionic-angular';
import { FcmDataProvider } from './fcm.data';
#Injectable()
export class FcmProvider {
/*
* FIREBASE CLOUD MESSAGING
*/
constructor(private push: Push,
private alertCtrl: AlertController,
private platform: Platform,
private fcmDataProv: FcmDataProvider) {
}
getPermission(): Promise<{isEnabled: boolean}> {
// Listen for res.isEnabled.
return this.push.hasPermission();
}
initPush() {
console.log("Init push!");
const options: PushOptions = this.initPushOptions();
const pushObject: PushObject = this.push.init(options);
pushObject.on('notification').subscribe((notification: any) => {
console.log('Received a notification', notification);
if(this.platform.is('ios')) {
this.handleIOSNotification(notification, pushObject);
} else if(this.platform.is('android')) {
this.handleAndroidNotification(notification);
}
this.presentSuccessAlert(notification.message);
});
pushObject.on('registration').subscribe(
(registration: any) => {
console.log('Device registered', registration);
// TODO: Send registration.registrationId to server and update it.
}
);
pushObject.on('error').subscribe(
error => console.error('Error with Push plugin', error)
);
}
private initPushOptions(): PushOptions {
return {
android: {
sound: true,
vibrate: true,
clearBadge: true
},
ios: {
alert: true,
badge: true,
sound: true,
clearBadge: true
},
windows: {}, // Lol
browser: {
pushServiceURL: 'http://push.api.phonegap.com/v1/push'
}
};
}
private handleIOSNotification(data, push: PushObject) {
push.finish().then(
() => console.log("Finished processing push data")
).catch(() => console.error(
"Something went wrong with push.finish for ID=", data.additionalData.notId
));
}
private handleAndroidNotification(data) {
console.log(data.data);
}
private presentSuccessAlert(message: string): void {
let alert = this.alertCtrl.create({
title: "Neue Benachrichtigung",
message: message,
buttons: ["Ok"]
});
alert.present();
}
}
I'm testing on iOS but I would love to know how to handle it on android as well.
Edit:
Here is the console.log I receive from XCode:
Push Plugin notId 1
Warning: Application delegate received call to -application:didReceiveRemoteNotification:fetchCompletionHandler: but the completion handler was never called.
Notification received
Push Plugin key: content-available
Push Plugin key: data
Push Plugin key: badge
Wow this issue is super silly.
But here is what was wrong: You got to put notId first!
Like this:
"notId": 1, # notId HAS TO BE FIRST!!!
'aps': {
'data': 'here is my data',
'content-available': 1,
}
unit testing code in spec.ts
it('Should test LoadMenus method', () => {
let arrays = [{ "Id": 1, "MenuType": "A00", "MenuName": "Search", "UserDetail": { "UserName": "rajendranra" }
alert(11);// executed
this.modalNota.LoadMenus(arrays, 'A00', 'A', '0');
alert(12);// does not execute
expect(this.modalNota.cocmenuitems[0].MenuName).toBe('Search');
});
Component method which am testing
LoadMenus(lstMenu, menustring, submenustartstring, submenuendstring) {
alert(menustring);
this.subitems = [];
let menu = lstMenu.find(myObj => myObj.MenuType === menustring);
if (menu !== null) {
this.menufilter = lstMenu.filter(myObj => myObj.MenuType.startsWith(submenustartstring))
.filter(x => x.MenuType.endsWith(submenuendstring));
if (this.menufilter.length > 1) {
for (let menuType = 0; menuType < this.menufilter.length; menuType++) {
if (this.menufilter[menuType].MenuType !== menustring) {
this.subitems.push({
label: this.menufilter[menuType].MenuName,
routerLink: [this.GetLink(this.menufilter[menuType].MenuName)]
});
}
}
alert(menu.MenuName);
this.cocmenuitems.push({ label: menu.MenuName, routerLink: [this.GetLink(menu.MenuName)], items: this.subitems });
} else {
alert(menu.MenuName);
this.cocmenuitems.push({ label: menu.MenuName, routerLink: [this.GetLink(menu.MenuName)] });
}
}
}
This unit test showing the result is failed. the main problem is Alert alert(12); does not fire. Why my alert(12) does not hit after this this.modalNota.LoadMenus(arrays, 'A00', 'A', '0'); component code is executed??
Am struggling a whole day for this. kindly give me the solution.
I am in the process of upgrading from Ionic 2 beta to Ionic 2 rc3.
I have my app.component.ts file, that worked fine, when it was just displaying a root page. But as soon as I have tried to add menu items from my old working Ionic 2 beta version, I get the error below.
If anyone can advise how I can resolve this, I would appreciate the help.
Compile Error in CLI
[13:14:56] template error, "E:\Development\IDE\ionic-apps\WhatsAppClone\src\app\build\app.html": Error: ENOENT: no such
file or directory, open 'E:\Development\IDE\ionic-apps\WhatsAppClone\src\app\build\app.html'
Runtime Error in browser console
Unhandled Promise rejection: Failed to load build/app.html ; Zone: meteor-rxjs-zone ; Task: Promise.then ; Value: Failed to load build/app.html undefined polyfills.js:3:7730
Error: Uncaught (in promise): Failed to load build/app.html
Stack trace:
s#http://localhost:8100/build/polyfills.js:3:8568
s#http://localhost:8100/build/polyfills.js:3:8391
h/<#http://localhost:8100/build/polyfills.js:3:8902
sg</d</t.prototype.invokeTask#http://localhost:8100/build/polyfills.js:3:14040
sg</v</e.prototype.runTask#http://localhost:8100/build/polyfills.js:3:11392
i#http://localhost:8100/build/polyfills.js:3:8021
t/this.invoke#http://localhost:8100/build/polyfills.js:3:15204
app.component.ts
import { Component, ViewChild } from '#angular/core';
import { Storage } from "#ionic/storage";
import { Platform, Events, AlertController, Nav } from 'ionic-angular';
import { StatusBar, Push, Splashscreen } from 'ionic-native';
import { SearchJobsPage } from "../pages/searchjobs/searchjobs";
import { LoginPage } from '../pages/login/login';
import { LogoutPage } from '../pages/logout/logout';
import { PersonModel } from '../pages/model/personModel';
import { ChatsPage } from '../pages/chats/chats';
import { PersonPage } from '../pages/person/person';
import { SearchFavouriteJobsPage } from '../pages/searchfavouritejobs/searchfavouritejobs';
import { SearchPostingsPage } from '../pages/searchpostings/searchpostings';
import { SearchFavouritePostingsPage } from '../pages/searchfavouritepostings/searchfavouritepostings';
import { UtilityService } from '../pages/utils/utilityService';
import { NotificationService } from '../pages/service/notificationService';
import { JobService } from '../pages/service/jobService';
import { JobModel } from '../pages/model/jobModel';
import { MapLocationsPage } from '../pages/maplocations/maplocations';
import { MapRangePage } from '../pages/maprange/maprange';
//import { METEOR_PROVIDERS } from 'angular2-meteor';
// import * as Check from 'meteor/check';
// import * as EJSON from 'meteor/ejson';
//declare let Meteor;
#Component({
templateUrl: 'build/app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
rootPage: any;
public storage: Storage = null;
public pages: Array<{ title: string, component: any }>;
public pages_person: Array<{ title: string, component: any }>;
public pages_person_admin: Array<{ title: string, component: any }>;
public menuTitle: string = 'Menu';
public personModel: PersonModel = null;
public utilityService: UtilityService = null;
public notificationService: NotificationService = null;
public personModelLoggedIn: PersonModel;
public jobService: JobService = null;
public events: Events = null;
public ios: boolean = false;
constructor(private platform: Platform, utilityService: UtilityService, notificationService: NotificationService, jobService: JobService, events: Events, private alertCtrl: AlertController, storage: Storage) {
this.storage = storage;
this.utilityService = utilityService;
this.jobService = jobService;
this.notificationService = notificationService;
this.events = events;
this.initializeApp();
if (this.platform.is('ios')) {
this.ios = true;
}
// this.rootPage = Meteor.user() ? TabsPage : LoginComponent;
this.rootPage = SearchJobsPage;
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.initializeApp();
StatusBar.styleDefault();
Splashscreen.hide();
});
// used for an example of ngFor and navigation
this.pages = [
{ title: 'Market', component: SearchJobsPage },
{ title: 'Postings', component: SearchPostingsPage },
{ title: 'Login', component: LoginPage }
];
this.pages_person = [
{ title: 'Market', component: SearchJobsPage },
{ title: 'Market Favourites', component: SearchFavouriteJobsPage },
{ title: 'Postings', component: SearchPostingsPage },
{ title: 'Favourite Postings', component: SearchFavouritePostingsPage },
{ title: 'Messages', component: ChatsPage },
{ title: 'Profile', component: PersonPage },
{ title: 'Logout', component: LogoutPage }
];
this.pages_person_admin = [
{ title: 'Market', component: SearchJobsPage },
{ title: 'Market Favourites', component: SearchFavouriteJobsPage },
{ title: 'Postings', component: SearchPostingsPage },
{ title: 'Favourite Postings', component: SearchFavouritePostingsPage },
{ title: 'Messages', component: ChatsPage },
{ title: 'Profile', component: PersonPage },
{ title: 'Logout', component: LogoutPage },
{ title: 'Map Locations', component: MapLocationsPage },
{ title: 'Map Range', component: MapRangePage }
];
}
initializeApp() {
StatusBar.styleDefault();
this.checkLogin();
this.utilityService.startUpChecks();
if (window['cordova']) {
this.utilityService.setLocalStrorage('this.chats.observe', 'false');
this.utilityService.setLocalStrorage('this.messages.observe', 'false');
this.utilityService.setLocalStrorage('this.messages.subscribe', 'false');
this.utilityService.setLocalStrorage('push:notifications.subscribe', 'false');
}
this.subscribeEvents();
}
// openPage(page) {
// // Reset the content nav to have just this page
// // we wouldn't want the back button to show in this scenario
// this.nav.setRoot(page.component);
// }
public subscribeEvents(): void {
this.events.subscribe('push:notifications', (data) => {
this.checkLogin();
});
}
public pushNotifications(): void {
let observedPromise: Promise<string> = this.utilityService.getLocalStrorage('push:notifications.subscribe');
observedPromise.then((observed: string) => {
if (!observed || observed != 'true') {
this.utilityService.setLocalStrorage('push:notifications.subscribe', 'true');
try {
if (window['cordova']) {
if (this.personModelLoggedIn) {
let promiseJobsForPerson: Promise<JobModel[]> = this.jobService.getJobsByPerson(this.personModelLoggedIn.id);
promiseJobsForPerson.then((data) => {
let jobModelsForPerson: JobModel[] = data;
let topics: string[] = [];
topics.push('P' + this.personModelLoggedIn.id);
for (let i = 0; i < jobModelsForPerson.length; i++) {
let jobModel: JobModel = jobModelsForPerson[i];
topics.push('J' + jobModel.id);
}
//topics.push('J65'); // deleteme
//topics.push('P9'); // deleteme
let push = Push.init({
android: {
senderID: "893141127008",
topics: topics
},
ios: {
alert: "true",
badge: false,
sound: "true",
topics: topics
},
windows: {}
});
push.on('registration', (data1) => {
this.events.subscribe('messages:notify', (data) => {
let promise: Promise<string> = this.notificationService.push('null', data[0].topic, data[0].message, data[0].title);
promise.then((data2) => {
// console.log('app.ts messages2:notify', data2);
});
});
});
push.on('notification', (data) => {
this.events.publish('messages:update');
if (this.nav.getActive().name != 'ChatsPage' && this.nav.getActive().name != 'MessagesPage') {
//if user using app and push notification comes
if (data.additionalData.foreground) {
//if application open, show popup
let confirmAlert = this.alertCtrl.create({
title: data.title,
message: data.message,
buttons: [{
text: 'Ignore',
role: 'cancel'
}, {
text: 'View',
handler: () => {
this.rootPage = ChatsPage;
}
}]
});
confirmAlert.present(confirmAlert);
} else {
this.rootPage = ChatsPage;
}
}
});
push.on('error', (e) => {
alert('Error: ' + e.message);
console.log(e);
});
});
}
}
} catch (e) {
alert('Push Notification: ' + e.message());
console.log('Push Notification: ' + e.message());
}
}
});
}
public checkLogin(): void {
let promise: Promise<string> = this.utilityService.getLoggedInPerson();
promise.then((data) => {
this.personModelLoggedIn = JSON.parse(data);
if (this.personModelLoggedIn) {
this.utilityService.setUpMenuItems();
this.pushNotifications();
}
});
}
}
app.html
<ion-menu [content]="content" id="unauthenticated">
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-menu [content]="content" id="authenticated-person">
<ion-toolbar [class]="ios ? 'menu-toolbar' : ''">
<ion-title [class]="ios ? 'menu-title' : ''">
<div class="item-avatar-img" id="menu-item-avatar-img-person"></div>
<div class="item-avatar-name" id="menu-item-avatar-name-person"></div>
</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages_person" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-menu [content]="content" id="authenticated-person-admin">
<ion-toolbar [class]="ios ? 'menu-toolbar' : ''">
<ion-title [class]="ios ? 'menu-title' : ''">
<div class="item-avatar-img" id="menu-item-avatar-img-person-admin"></div>
<div class="item-avatar-name" id="menu-item-avatar-name-person-admin"></div>
</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages_person_admin" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<!-- Disable swipe-to-go-back because it's poor UX to combine STGB with side menus -->
<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>
Directory
You have set your templateUrl as build/app.html.
You shouldnt check it in build folder.
Try templateUrl: 'app.html' in app.component.ts