How to change the app route path if a page (walkthrough) has been view once - ionic-framework

I have app walkthrough / intro built using ion-slides which is loaded as the default page in app.routing.module.ts .
{
path: '',
redirectTo: 'walkthrough',
pathMatch: 'full'
},{
path: 'walkthrough',
loadChildren: () => import('./walkthrough/walkthrough.module').then(m => m.WalkthroughPageModule)
}
I only want to show this the first time the app is launched, so my question is how do I configure the app-route in the app-routing module to set the opening page just once?
I read the documentation and could not find a reference.

For anyone in a similar situation you can add conditionals / user logic using angular route gaurds. In the Walkthrough.ts module I set the value to storage:
ngOnInit(): void {
// save key to mark the walkthrough as visited so the next time the user vistis the app, he would be redirected to log in
Storage.set({
key: 'visitedWalkthrough',
value: 'true'
});
}
In a walkthrough.gaurd.ts I check for same value and change the route based on same:
const { Storage } = Plugins;
#Injectable()
export class WalkthroughGuard implements CanActivate {
constructor(private router: Router) {}
async canActivate(): Promise<boolean> {
const { value } = await Storage.get({ key: 'visitedWalkthrough' });
if (value === 'true') {
// this is a returning user, don't show him the walkthrough
this.router.navigate(['auth']);
return false;
} else return true;
}
}
Good tutorial here :

Related

Ionic 3 The external links open only once in system browser outside in appbrowser

i wrapped a website and there are external links which i made to open in System browser, but when I return to in-app browser using hardware back button and again try to open the external links it doesn’t open. It looks like the 'loadstart" occurs only once.Or am i doing anything wrong here?
home.ts
export class HomePage {
constructor(public navCtrl: NavController, private iab: InAppBrowser, public
platform: Platform,private fileOpener: FileOpener, private
transfer:FileTransfer, private file: File, private diagnostic:Diagnostic) {
platform.ready().then(() => {
const browser =
this.iab.create('https://www.tutorialspoint.com/ionic/index.htm','_blank',
{zoom:'yes',location:'no', clearcache: 'yes', clearsessioncache: 'yes'});
browser.show();
browser.on('loadstart').subscribe(
(data) => {
console.log("URL IS", data.url);
this.downloadfile(data.url)
},
err => {
console.log("InAppBrowser Loadstop Event Error: " + err);
}
);
});
}
downloadfile(url) {
var externalCheck = (!url.includes("tutorial.points"));
var pdfCheck = (url.substr(url.length - 4) == '.pdf');
if (externalCheck || pdfCheck) {
window.open(url, "_system", 'location=no');
}
}
}
Is there any solution for this ? any help is really appreciated.Thanks in advance

Ionic 2: push notification on click

A notification appears, but upon clicking them, they only open the application again. What I want is upon clicking the notification, it opens a specific item.
In Laravel, I am using the brozot/Laravel-FCM package for Firebase Cloud Messaging (FCM) to send notifications, and on the other end, I'm using Ionic push notifications to receive and display notifications in the notification tray.
If I don't use setClickAction() on Laravel, the Ionic application opens upon clicking the notification, but if I set setClickAction(), then nothing happens. The notification merely disappears.
Laravel-code:
$notificationBuilder = new PayloadNotificationBuilder('my title');
$notificationBuilder->setBody('Hello world')
->setSound('default')
->setClickAction('window.doSomething');
$notification = $notificationBuilder->build();
Ionic 2 framework sample:
import { Component, ViewChild } from '#angular/core';
import { Platform, Nav, MenuController, ModalController, Events, AlertController } from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
import { Push, PushObject, PushOptions } from '#ionic-native/push';
import { Storage } from '#ionic/storage';
import {
SearchPage
} from '../pages/pages';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
rootPage: any = SearchPage;
constructor(
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen,
private menu: MenuController,
private modalCtrl: ModalController,
private events: Events,
private push: Push,
private alertCtrl: AlertController,
private storage: Storage
) {
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.pushSetup();
}
pushSetup() {
const options: PushOptions = {
android: {
senderID: 'xxxxxxxxxxx',
forceShow: true
},
ios: {
senderID: 'xxxxxxxxxxx',
alert: 'true',
badge: true,
sound: 'true'
},
windows: {},
browser: {
pushServiceURL: 'http://push.api.phonegap.com/v1/push'
}
};
const pushObject: PushObject = this.push.init(options);
pushObject.on('notification').subscribe((notification: any) => {
});
pushObject.on('registration').subscribe((registration: any) => {
alert(registration.id);
});
pushObject.on('error').subscribe(error => alert('Error with Push plugin' + error));
}
}
(<any>window).doSomething = function () {
alert('doSomething called');
}
What am I missing?
There are these steps that need to be done for general One-Signal push notification to be implemented
Create a OneSignal Account
Add a New APP in the One Signal , configure for Android first (you can target for any platform but i'm focussing on Android as of now) .you need to get the Google Server Key and Google Project Id.
You can get the Above keys from the Firebase using this Steps
Now we are done with Configuring the OneSignal Account, now integrate with the ionic using the cordova plugin
In Ionic2 :
OneSignal.startInit(//google Server Key, //Google ProjectId);
OneSignal.inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification);
OneSignal.setSubscription(true);
OneSignal.handleNotificationReceived().subscribe(() => {
// handle received here how you wish.
// this.goToReleatedPage(data.Key, data.Value);
});
OneSignal.handleNotificationOpened().subscribe((data: any) => {
//console.log('MyData'+ JSON.stringify(data.additionalData));
this.parseObject(data);
});
OneSignal.endInit();
ParsingObject in Ionic
public parseObject(obj) {
for (var key in obj) {
this.goToReleatedPage(key, obj[key]);
if (obj[key] instanceof Object) {
this.parseObject(obj[key]);
}
}
}
goToReleatedPage Method
public goToReleatedPage(Key, Value) {
//console.log("Pagename"+" " + Key + "ID" +" " + Value);
if (Key === 'xxxx') {
this.navCtrl.push(xxxPage, {
id: Value
});
} else if (Key === 'Foo') {
this.navCtrl.push(foosPage, {
id: Value,
});
} else if (Key === 'bar') {
this.navCtrl.push(barPage, {
id: Value
});
}
}
While sending the Message from OneSignal , you need to specify which page you need to open and you want to pass Id as follows

Redirect to requested page after login using vue-router

In my application some routes are just accessible for authenticated users.When a unauthenticated user clicks on a link, for which he has to be signed in, he will be redirected to the login component.
If the user logs in successfully, I would like to redirect him to the URL he requested before he had to log in. However, there also should be a default route, in case the user did not request another URL before he logged in.
How can I achieve this using vue-router?
My code without redirect after login
router.beforeEach(
(to, from, next) => {
if(to.matched.some(record => record.meta.forVisitors)) {
next()
} else if(to.matched.some(record => record.meta.forAuth)) {
if(!Vue.auth.isAuthenticated()) {
next({
path: '/login'
// Redirect to original path if specified
})
} else {
next()
}
} else {
next()
}
}
)
My login function in my login component
login() {
var data = {
client_id: 2,
client_secret: '**************',
grant_type: 'password',
username: this.email,
password: this.password
}
// send data
this.$http.post('oauth/token', data)
.then(response => {
// authenticate the user
this.$auth.setToken(response.body.access_token,
response.body.expires_in + Date.now())
// redirect to route after successful login
this.$router.push('/')
})
}
This can be achieved by adding the redirect path in the route as a query parameter.
Then when you login, you have to check if the redirect parameter is set:
if IS set redirect to the path found in param
if is NOT set you can fallback on root.
Put an action to your link for example:
onLinkClicked() {
if(!isAuthenticated) {
// If not authenticated, add a path where to redirect after login.
this.$router.push({ name: 'login', query: { redirect: '/path' } });
}
}
The login submit action:
submitForm() {
AuthService.login(this.credentials)
.then(() => this.$router.push(this.$route.query.redirect || '/'))
.catch(error => { /*handle errors*/ })
}
I know this is old but it's the first result in google and for those of you that just want it given to you this is what you add to your two files. In my case I am using firebase for auth.
Router
The key line here is const loginpath = window.location.pathname; where I get the relative path of their first visit and then the next line next({ name: 'Login', query: { from: loginpath } }); I pass as a query in the redirect.
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser;
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
if (requiresAuth && !currentUser) {
const loginpath = window.location.pathname;
next({ name: 'Login', query: { from: loginpath } });
} else if (!requiresAuth && currentUser) next('menu');
else next();
});
Login Page
No magic here you'll just notice my action upon the user being authenticated this.$router.replace(this.$route.query.from); it sends them to the query url we generated earlier.
signIn() {
firebase.auth().signInWithEmailAndPassword(this.email, this.password).then(
(user) => {
this.$router.replace(this.$route.query.from);
},
(err) => {
this.loginerr = err.message;
},
);
},
I am going to be fleshing out this logic in more detail but it works as is. I hope this helps those that come across this page.
Following on from Matt C's answer, this is probably the simplest solution but there were a few issues with that post, so I thought it best to write a complete solution.
The destination route can be stored in the browser's session storage and retrieved after authentication. The benefit of using session storage over using local storage in this case is that the data doesn't linger after a broswer session is ended.
In the router's beforeEach hook set the destination path in session storage so that it can be retrieved after authentication. This works also if you are redirected via a third party auth provider (Google, Facebook etc).
router.js
// If user is not authenticated, before redirecting to login in beforeEach
sessionStorage.setItem('redirectPath', to.path)
So a fuller example might look something like this. I'm using Firebase here but if you're not you can modify it for your purposes:
router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(x => x.meta.requiresAuth);
const currentUser = firebase.auth().currentUser;
if (requiresAuth && !currentUser) {
sessionStorage.setItem('redirectPath', to.path);
next('/login');
} else if (requiresAuth && currentUser) {
next();
} else {
next();
}
});
login.vue
In your login method, after authetication you will have a line of code that will send the user to a different route. This line will now read the value from session storage. Afterwards we will delete the item from session storage so that it is not accidently used in future (if you the user went directly to the login page on next auth for instance).
this.$router.replace(sessionStorage.getItem('redirectPath') || '/defaultpath');
sessionStorage.removeItem('redirectPath');
A fuller example might look like this:
export default Vue.extend({
name: 'Login',
data() {
return {
loginForm: {
email: '',
password: ''
}
}
},
methods: {
login() {
auth.signInWithEmailAndPassword(this.loginForm.email, this.loginForm.password).then(user => {
//Go to '/defaultpath' if no redirectPath value is set
this.$router.replace(sessionStorage.getItem('redirectPath') || '/defaultpath');
//Cleanup redirectPath
sessionStorage.removeItem('redirectPath');
}).catch(err => {
console.log(err);
});
},
},
});
If route guard is setup as below
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!loggedIn) {
next({
path: "/login",
query: { redirect: to.fullPath }
});
} else {
next();
}
} else {
next();
}
});
The redirect query can be extracted and used upon successful login
let searchParams = new URLSearchParams(window.location.search);
if (searchParams.has("redirect")) {
this.$router.push({ path: `${searchParams.get("redirect")}` });
} else this.$router.push({ path: "/dashboard" });
Another quick and dirty option would be to use local storage like the following:
In your beforeEach, before you redirect to login place the following line of code to save the initial requested path to local storage:
router.js
// If user is not authenticated, before redirecting to login
localStorage.setItem('pathToLoadAfterLogin', to.path)
Then in your login component, upon succesful login, you can redirect to the localStorage variable that you previously created:
login.vue
// If user login is successful, route them to what they previously requested or some default route this.$router.push(localStorage.getItem('pathToLoadAfterLogin') || 'somedefaultroute');
Much easier with this library
and login function is
let redirect = this.$auth.redirect();
this.$auth
.login({
data: this.model,
rememberMe: true,
redirect: { name: redirect ? redirect.from.name : "homepage", query: redirect.from.query },
fetchUser: true
})
This will help you #Schwesi .
Router.beforeEach(
(to, from, next) => {
if (to.matched.some(record => record.meta.forVisitors)) {
if (Vue.auth.isAuthenticated()) {
next({
path: '/feed'
})
} else
next()
}
else if (to.matched.some(record => record.meta.forAuth)) {
if (!Vue.auth.isAuthenticated()) {
next({
path: '/login'
})
} else
next()
} else
next()
}
);
This worked for me.
this.axios.post('your api link', {
token: this.token,
})
.then(() => this.$router.push(this.$route.query.redirect || '/dashboard'))
In Vue2 if someone has a routing and guarded some groups of routes. I solved this way.
function webGuard(to, from, next) {
if (!store.getters["auth/authenticated"]) {
sessionStorage.setItem("redirect", to); // hear I save the to
next("/login");
} else {
next();
}
}
Vue.use(VueRouter);
export default new VueRouter({
mode: "history",
hash: false,
routes: [
{
path: "/",
component: Home,
children: [
{ path: "", redirect: "home" },
...
...
],
beforeEnter: webGuard
},]
when you login
this.signIn({ email: test#gmail.com, password: 123 })
.then((res) => {
var redirectPath = sessionStorage.getItem('redirect');
sessionStorage.removeItem('redirect');
this.$router.push(redirectPath?redirectPath:"/dashboard");
})

one signal additional data in ionic 2/3

I'm trying to work with one signal plugin in my ionic 2 app
I've installed Onesignal and it was working fine,but i don't know how to work with handleNotificationOpened function
there is no document at all (nothing was found)
this is my code:
this.oneSignal.handleNotificationReceived().subscribe((msg) => {
// o something when notification is received
});
but I have no idea how to use msg for getting data.
any help? link?
tank you
Here is how i redirect user to related page when app launch from notification.
app.component.ts
this.oneSignal.handleNotificationOpened().subscribe((data) => {
let payload = data; // getting id and action in additionalData.
this.redirectToPage(payload);
});
redirectToPage(data) {
let type
try {
type = data.notification.payload.additionalData.type;
} catch (e) {
console.warn(e);
}
switch (type) {
case 'Followers': {
this.navController.push(UserProfilePage, { userId: data.notification.payload.additionalData.uid });
break;
} case 'comment': {
this.navController.push(CommentsPage, { id: data.notification.payload.additionalData.pid })
break;
}
}
}
A better solution would be to reset the current nav stack and recreate it. Why?
Lets see this scenario:
TodosPage (rootPage) -> TodoPage (push) -> CommentsPage (push)
If you go directly to CommentsPage the "go back" button won't work as expected (its gone or redirect you to... who knows where :D).
So this is my proposal:
this.oneSignal.handleNotificationOpened().subscribe((data) => {
// Service to create new navigation stack
this.navigationService.createNav(data);
});
navigation.service.ts
import {Injectable} from '#angular/core';
import {App} from 'ionic-angular';
import {TodosPage} from '../pages/todos/todos';
import {TodoPage} from '../pages/todo/todo';
import {CommentsPage} from '../pages/comments/comments';
#Injectable()
export class NavigationService {
pagesToPush: Array<any>;
constructor(public app: App) {
}
// Function to create nav stack
createNav(data: any) {
this.pagesToPush = [];
// Customize for different push notifications
// Setting up navigation for new comments on TodoPage
if (data.notification.payload.additionalData.type === 'NEW_TODO_COMMENT') {
this.pagesToPush.push({
page: TodoPage,
params: {
todoId: data.notification.payload.additionalData.todoId
}
});
this.pagesToPush.push({
page: CommentsPage,
params: {
todoId: data.notification.payload.additionalData.todoId,
}
});
}
// We need to reset current stack
this.app.getRootNav().setRoot(TodosPage).then(() => {
// Inserts an array of components into the nav stack at the specified index
this.app.getRootNav().insertPages(this.app.getRootNav().length(), this.pagesToPush);
});
}
}
I hope it helps :)

RouterConfiguration and Router undefined in aurelia

I am very new to Aurelia and just trying to apply navigation to my project.Though i import aurelia-router still it says RouterConfiguration and Router are undefined in constructor
import {Todo} from './ToDo/todo';
import {RouterConfiguration, Router} from 'aurelia-router';
export class App {
heading = "Todos";
todos: Todo[] = [];
todoDescription = '';
router :any;
list: any[];
constructor(RouterConfiguration: RouterConfiguration, Router: Router) {
this.todos = [];
this.configureRouter(RouterConfiguration, Router);
//console.log("klist", this.list);
}
//config.map() adds route(s) to the router. Although only route, name,
//moduleId, href and nav are shown above there are other properties that can be included in a route.
//The class name for each route is
configureRouter(config: RouterConfiguration, router: Router): void {
this.router = router;
config.title = 'Aurelia';
config.map([
{ route: '', name: 'home', moduleId: 'home/home', nav: true, title: 'Home' },
{ route: 'users', name: 'users', moduleId: './Friends/Friends', nav: true },
//{ route: 'users/:id/detail', name: 'userDetail', moduleId: 'users/detail' },
//{ route: 'files/*path', name: 'files', moduleId: 'files/index', href: '#files', nav: 0 }
]);
}
addTodo() {
if (this.todoDescription) {
this.todos.push(new Todo(this.todoDescription));
// this.todoDescription = '';
}
}
}
By convention, Aurelia looks in the initial class that loads (App) for the configureRouter() function and executes it. This means, you do not have to inject anything in the constructor.
It looks like you've simply added too much. I think fixing your sample seems to be as easy as removing some stuff, like so:
import { Todo } from './ToDo/todo';
import { RouterConfiguration, Router } from 'aurelia-router';
export class App {
heading = "Todos";
todos: Todo[] = [];
todoDescription = '';
list: any[];
constructor() {
// note: removed routing here entirely (you don't need it)
// also, you've already declared this.todos above, so no need to do it here again
}
configureRouter(config : RouterConfiguration, router : Router): void {
this.router = router;
config.title = 'Aurelia';
config.map([
{ route: '', name: 'home', moduleId: 'home/home', nav: true, title: 'Home' },
{ route: 'users', name: 'users', moduleId: './Friends/Friends', nav: true }
]);
}
addTodo() {
// removed this for brevity
}
}
This should resolve your 'undefined' errors on Router and RouteConfiguration. As an additional note, don't forget to add the <router-view> to your html template as well. Otherwise, you'll get no errors but the views won't show up either:
<template>
<div class="content">
<router-view></router-view>
</div>
</template>
Great documentation on this can be found at the Aurelia Docs - Routing.