How to disable Browser back button in Angular 2 - javascript

I'm developing a web site using Angular 2.
Is there any way to disable or trigger Browser back button using Angular 2?
Thanks

Not sure if this is already sorted, but posting the answer nonetheless, for future references.
To tackle this, you basically need to add a listener in your app-component and setup a canDeactivate guard on your angular-router.
// in app.component.ts
import { LocationStrategy } from '#angular/common';
#Component({
selector: 'app-root'
})
export class AppComponent {
constructor(
private location: LocationStrategy
) {
// check if back or forward button is pressed.
this.location.onPopState(() => {
// set isBackButtonClicked to true.
this.someNavigationService.setBackClicked(true);
return false;
});
}
}
// in navigation guard
#Injectable()
export class NavigationGuard implements CanDeactivate<any> {
constructor(private someNavigationService: SomeNavigationService) {}
canDeactivate(component: any) {
// will prevent user from going back
if (this.someNavigationService.getBackClicked()) {
this.someNavigationService.setBackClicked(false);
// push current state again to prevent further attempts.
history.pushState(null, null, location.href);
return false;
}
return true;
}
}

import { LocationStrategy } from '#angular/common';
constructor( private location: LocationStrategy){
// preventing back button in browser implemented by "Samba Siva"
history.pushState(null, null, window.location.href);
this.location.onPopState(() => {
history.pushState(null, null, window.location.href);
});
}
its working fine to me 100% in angular2/4/5

This Very simple, use the following code, This example code is from plain javascript i have converted this into angular and using in my 2-3 projects
// Inject LocationStrategy Service into your component
constructor(
private locationStrategy: LocationStrategy
) { }
// Define a function to handle back button and use anywhere
preventBackButton() {
history.pushState(null, null, location.href);
this.locationStrategy.onPopState(() => {
history.pushState(null, null, location.href);
})
}
You can define preventBackButton in any service as well and call it from there

Snippet that I use and works across all major browsers!
ngOnInit() {
history.pushState(null, null, location.href);
this.subscription = fromEvent(window, 'popstate').subscribe(_ => {
history.pushState(null, null, location.href);
this.openModal(`You can't make changes or go back at this time.`, 'Okay');
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}

I've tried all the solutions mentioned above but none of them worked perfectly for me. Finally I've found this npm module that worked immediately and perfectly, after two days of failed attempts.
Github: https://github.com/Zatikyan/angular-disable-browser-back-button#readme

A bit late perhaps but maybe somebody can use it.
This is a solution I use for a page with tabs (Bootstrap 4 style) where each tab is a component.
#Injectable()
export class CanNavigateService {
private static _isPermissionGranted = true
public navigationAttempt = new Subject<boolean>()
//-------------------------------------------------------------//
/**Will the next navigation attempt be permitted? */
updatePermission(isPermissionGranted: boolean) {
CanNavigateService._isPermissionGranted = isPermissionGranted
}//updatePermission
//-------------------------------------------------------------//
/**Broadcast the last attempt and whether it was permitted */
updateNavigationAttempt(wasPermissionGranted: boolean) {
this.navigationAttempt.next(wasPermissionGranted)
}//updatePermission
//-------------------------------------------------------------//
/**Can we navigate? */
public isPermissionGranted(): boolean {
return CanNavigateService._isPermissionGranted
}//isPermissionGranted
}//Cls
NavigationGuard like #Jithin Nair above but also broadcasts when an attempt to navigate was made and whether it was permitted. Subscribers of CanNavigateService can use it to decide what to do instead of back navigation.
#Injectable()
export class NavigationGuard implements CanDeactivate<any> {
constructor(private canNavigateService: CanNavigateService) { }
//--------------------------------------------------------------------//
// will prevent user from going back if permission has not been granted
canDeactivate(component: any) {
let permitted = this.canNavigateService.isPermissionGranted()
this.canNavigateService.updateNavigationAttempt(permitted)
if (!permitted) {
// push current state again to prevent further attempts.
history.pushState(null, null, location.href)
return false
}
return true
}//canDeactivate
}//Cls
Usage:
constructor(private _navigateService: CanNavigateService) {
super()
_navigateService.navigationAttempt.subscribe(wasPermitted => {
//If navigation was prevented then just go to first tab
if (!wasPermitted)
this.onTabSelected( this._firstTab)
})
}//ctor
//----------------------------------------------------------------------------//
onTabSelected(tab) {
this._selectedTab = tab
//If it's not the first tab you can't back navigate
this._navigateService.updatePermission(this._selectedTab == this._firstTab)
}//onTabSelected

try to use this
window.onpopstate = function (e) { window.history.forward(1); }

Try this
<script type = "text/javascript" >
history.pushState(null, null, 'pagename');
window.addEventListener('popstate', function(event) {
history.pushState(null, null, 'pagename');
});
</script>
where change 'pagename' to your page name and put this into head section of page.

If you want to prevent a route to be reached you can add the #CanActivate() decorator to your routing component
#Component({selector: 'control-panel-cmp', template: `<div>Settings: ...</div>`})
#CanActivate(checkIfWeHavePermission)
class ControlPanelCmp {
}
See also
- Angular 2: Inject a dependency into #CanActivate? for access to global services.
- Angular2 Router - Anyone know how to use canActivate in app.ts so that I can redirect to home page if not logged in

Why not use just this. Should avoid browser insert automatically things in the history. Just insert in some main.ts (or elsewhere executed at startup)
history.pushState = () => {};
Object.freeze(history);

This issue occurs on IE browser. Use below mentioned code it will resolve your issue.
#HostListener('document:keydown', ['$event'])
onKeyDown(evt: KeyboardEvent) {
if (
evt.keyCode === 8 || evt.which === 8
) {
let doPrevent = true;
const types =['text','password','file','search','email','number','date','color','datetime','datetime-local','month','range','search','tel','time','url','week'];
const target = (<HTMLInputElement>evt.target);
const disabled = target.disabled || (<HTMLInputElement>event.target).readOnly;
if (!disabled) {
if (target.isContentEditable) {
doPrevent = false;
} else if (target.nodeName === 'INPUT') {
let type = target.type;
if (type) {
type = type.toLowerCase();
}
if (types.indexOf(type) > -1) {
doPrevent = false;
}
} else if (target.nodeName === 'TEXTAREA') {
doPrevent = false;
}
}
if (doPrevent) {
evt.preventDefault();
return false;
}
}
}

If you are looking to disable browser back button in angular(7/8/9/10)... Try this link and install package using npm.
1) npm install --save angular-disable-browser-back-button
2) import { NgModule } from '#angular/core';
import { BackButtonDisableModule } from 'angular-disable-browser-back-button';
#NgModule({
...
imports: [
...
BackButtonDisableModule.forRoot()
],
...
})
export class AppModule {}
3) BackButtonDisableModule.forRoot({
preserveScrollPosition: true
})
Please use this link given below.. reference taken from.
[https://www.npmjs.com/package/angular-disable-browser-back-button][1]

This isn't Angular2 related problem. You can send the user back in history. See Manipulating the browser history, history.go() method particular:
window.history.go(-1);
However, I don't think there's a way to cancel or disable default browser action on pressing back button in the browser window because that could be very easily abused.
As an alternative you can show a dialog window when user tries to leave the page: javascript before leaving the page

Add following code in TS file of the component, where you don't want to go back.
#HostListener('window:hashchange', ['$event'])
hashChangeHandler(e) {
window.location.hash = "dontgoback";
}

step 1: Import Locatoion from angular commmon
import {Location} from "#angular/common";
step 2: Initialise in constructor
private location: Location
step 3: Add function in ngOnInit of the respective coponent,
this.location.subscribe(currentLocation => {
if (currentLocation.url === '*/basic-info*') {
window.onpopstate = function (event) {
history.go(1);
}
}
});
Note: Here /basic-info will be replaced by your path.
If first time it is not working, try adding outside subscribe,
let currentUrl = window.location.href;
let tmpVar = currentUrl.includes('/basic-info');
if (currentUrl.includes('/basic-info')) {
window.onpopstate = function (event) {
history.go(1);
}
}

Related

Angular 10 - html template not showing correct info depending on variables

I am practically new to Angular (Angular 10 used) and maybe it's a pretty easy thing but I can't figure it out. In the info component I am getting the current user details and the current book details from the tables on the server and depending on 3 variables I want to switch the buttons in the html template.
If the user is publisher (publisherMenu) -> show the publisher menu, if not (!publisherMenu) -> show regular user menu. This one is working. The publisherMenu variable is true or false in the response and the template loads the correct pair of buttons streight away.
But the problem is with the Buy Now and Add to Wishlist buttons. When you click one of them they change to You own it and In your Wishlist. That's ok but if I revisit the same book after that, the buttons are back to Buy Now and Add to Wishlist. If I refresh the page they get fixed, but only when I refresh it. If I navigate to another page and get back to this page, again, the buttons are not ok.
These two buttons depend on bookBought and inWishlist variables. For both of them I get the info in a string, split it to get an array and search for the user Id, and put true/false respectively.
If I debug in the browser everything works ok and the buttons show the correct texts... Now the function is in the constructor, I tried putting it in ngOnInit() but no difference.
Any help will be highly appreciated!
Thank you!
info.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import Backendless from 'backendless';
import { BookService } from '../book.service';
import { UserService } from 'src/app/user/user.service';
#Component({
selector: 'app-info',
templateUrl: './info.component.html',
styleUrls: ['./info.component.css']
})
export class DetailComponent implements OnInit {
currentBookData;
currentUserData;
publisherMenu: Boolean = false;
bookBought: Boolean = false;
inWishlist: Boolean = false;
constructor(private router: Router, private bookService: BookService, private userService: UserService) {
//get book data
let getBookData = Backendless.Data.of('books').findById({objectId})
.then(currentBook => {
//console.log('Current Data: ' + JSON.stringify(currentBook));
return currentBook;
})
.catch(error => {
console.log(error);
});
getBookData.then(result => {
console.log(JSON.stringify(current book));
this.currentBookData = result;
})
// get user data
let getUserData = Backendless.UserService.getCurrentUser()
.then(function(currentUser) {
return currentUser;
})
.then(result => {
this.currentUserData = result;
console.log('currentUser :' + JSON.stringify(result));
if (this.currentUserData.publisher) {
this.publisherMenu = true;
} else {
this.publisherMenu = false;
}
if(!this.currentUserData.wishlist) {
this.inWishlist = false;
} else {
let currentWishlist = this.currentUserData.wishlist.split(',');
if(currentWishlist.indexOf(this.currentGameData.objectId) !== -1) {
this.inWishlist = true;
} else {
this.inWishlist = false;
}
}
if(!this.currentUserData.orders) {
this.bookBought = false;
} else {
let currentOrders = this.currentUserData.orders.split(',');
if(currentOrders.indexOf(this.currentBookData.objectId) !== -1) {
this.bookBought= true;
} else {
this.bookBought= false;
}
}
})
.catch(function (error) {
console.error(error)
})
//current book id: 88443C15-9CEB-4FF2-B174-D88D7F3324D3
console.logs
current book :{"title":"Demo Book","ownerId":"7B8EF09F-9DF3-4CED-A6B7-
39C3CD90089D","sales":1,"price":"31.99","___class":"books","orders":"B63744A3-
C3C3-4FC5-8CF4-3FCD131A0929","updated":1607770188000,"objectId":"88443C15-
9CEB-4FF2-B174-D88D7F3324D3"}
currentUser :{"___class":"Users","wishlist":"D8005359-AFD5-487F-B130-
3012AF3A7F1E,63DD9C7D-36D5-4CCA-96A3-81BFCBCE92CD,88443C15-9CEB-4FF2-B174-
D88D7F3324D3,100D4187-5B37-4FD4-ADA3-77AE1392F905,96BD531E-B717-4AEB-8DA5-
51805143EC07","ownerId":"B63744A3-C3C3-4FC5-8CF4-3FCD131A0929","name":"Demo
User","publisher":false,"orders":"0BF4737D-F1C4-49C0-AD7A-
40279CF0E3CD,D8005359-AFD5-487F-B130-3012AF3A7F1E,88443C15-9CEB-4FF2-B174-
D88D7F3324D3,100D4187-5B37-4FD4-ADA3-77AE1392F905,96BD531E-B717-4AEB-8DA5-
51805143EC07","email":"demo#demo.demo","objectId":"B63744A3-C3C3-4FC5-8CF4-
3FCD131A0929"}
Results from the IFs on first page load or after navigating through the pages and return back:
publisherMenu :false
bookBought :false
inWishlist :false
Results from the IFs if I refresh the page:
publisherMenu: false
bookBought: true
inWishlist: true
info.component.html
<div *ngIf="!publisherMenu" class="book-buttons">
<a *ngIf="!bookBought" (click)="buyBook(currentBookData.objectId)">Buy Now</a>
<a *ngIf="bookBought">You own it</a>
<br><br>
<a *ngIf="!inWishlist" (click)="addBookToWishlist(currentBookData.objectId)">Add To Wishlist</a>
<a *ngIf="inWishlist">In your Wishlist</a>
</div>
<div *ngIf="publisherMenu" class="book-buttons">
<a [routerLink]="['../../add']">Add Book</a>
<br><br>
<a [routerLink]="['../../my-books']">My Books</a>
</div>
EDIT: I see now that if I don't manually refresh the page I only get the current book details and no current user details, I guess this is the reason, but why the second functions does not work like the first one, as they are practicaly the same..!?

How to load and unload Azure App Insights for web pages in JavaScript

I have followed this guide and this guide to add Azure App Insights to our Angular application. It works great but the problem I am having is how can we start/load and stop/unload tracking of the insights conditionally?
Basically, we have a toggle in our application that allows the user to turn on collection of data and Application Insights should be analyzing and collecting the data. Once the user turns this toggle off, it should stop analyzing and tracking.
It seems once we call this.appInsights.loadAppInsights(), there is no way to unlatch/unload/stop listening. If there is a way to unlatch/unload/stop listening, please let me know.
Thanks.
You can refer to the doc Disabling telemetry.
For example, if you want to unload app insights in javascript, use the code below:
telemetry.config.disableAppInsights = true;
I ended up creating a common service related to everything with AppInsights inspired from here.
import { AngularPlugin, AngularPluginService } from '#microsoft/applicationinsights-angularplugin-js';
import {
ApplicationInsights,
IEventTelemetry,
IExceptionTelemetry
} from '#microsoft/applicationinsights-web';
....
export class ApplicationInsightService {
private appInsights: ApplicationInsights;
constructor(private router: Router, private angularPluginService: AngularPluginService) {
const angularPlugin = new AngularPlugin();
this.angularPluginService.init(angularPlugin, this.router);
this.appInsights = new ApplicationInsights({ config: {
instrumentationKey: 'Your key here',
extensions: [angularPlugin],
extensionConfig: {
[angularPlugin.identifier]: { router: this.router },
}
}});
this.appInsights.loadAppInsights();
this.appInsights.trackPageView();
}
setUserId(userId: string) {
this.appInsights.setAuthenticatedUserContext(userId);
}
clearUserId() {
this.appInsights.clearAuthenticatedUserContext();
}
logPageView(name?: string, uri?: string) {
this.appInsights.trackPageView({ name, uri });
}
logEvent(event: IEventTelemetry, customProperties: { [key: string]: any }) {
this.appInsights.trackEvent(event, customProperties);
}
trackError(exception: Error) {
let telemetry = {
exception,
} as IExceptionTelemetry;
this.appInsights.trackException(telemetry);
}
stopTelemetry() { // !! this is how you stop tracking
this.appInsights.config.disableTelemetry = true;
}
startTelemetry() { // !! this is how you start/re-start it
this.appInsights.config.disableTelemetry = false;
}
}
The JavaScript part of the documentation was not helpful because those methods/properties didn't exist but the C# documentation helped and it seems to be similar to that.

Instascan QR Code scanner 'scan' listener won't update Angular 5 component view

I've implemented Instascan (https://github.com/schmich/instascan) to allow users to read QR Codes from within my Angular 5 app.
I have to trigger some actions after a successful scan and update my component's view accordingly.
I'm using the following code inside my component to detect my cameras and start scanning:
cameras: Array<any> = []
selectedCamera: any
scanner: any
content: string
ngOnInit () {
let vm = this
Instascan.Camera.getCameras().then(function (cameras) {
if (cameras.length > 0) {
vm.cameras.push(...cameras)
} else {
console.error('No cameras found.')
}
}).catch(function (e) {
console.error(e)
})
}
startScan () {
let vm = this
this.scanner = new Instascan.Scanner({
video: this.videoContainer.nativeElement,
backgroundScan: false,
mirror: false
})
this.scanner.addListener('scan', function (content) {
vm.content = content
})
this.selectedCamera = this.cameras[0]
this.scanner.start(this.selectedCamera)
}
And in my template I have an element that exists only if 'content' exists, and on click emit the scanned content to the parent component through an EventEmitter:
<div *ngIf="content" class="btn" (click)="emitContent()">
PROCEED
</div>
The problem is that in 'scan' event callback the changes in 'content' seems to don't be applied to my view, and my 'PROCEED' button don't become visible. An even stranger behavior happens: after I click anywhere in the screen, those changes are applied to my view.
I've also tried using ChangeDetectorRef.detectChanges() method inside the callback and binding 'this' into the callback, but both are not working.
How can I overcome this issue?
Thanks!
I've managed to solve this problem by using NgZone like this:
import { NgZone } from '#angular/core'
constructor (
private ngZone: NgZone
) {}
startScan () {
this.scanner = new Instascan.Scanner({
video: this.videoContainer.nativeElement,
backgroundScan: false,
mirror: false
})
this.scanner.addListener('scan', function (content) {
this.ngZone.run(() => {
this.content = content
})
}.bind(this))
this.selectedCamera = this.cameras[0]
this.scanner.start(this.selectedCamera)
}
I don't know if this is the best solution, and actually I don't know how NgZone usage affects the application performance/state at all.
If someone know a better solution, it will be welcome!
Thanks!

Executing code after router.navigate in Angular2

I wonder if there is a way to execute something after i navigate to a different "view" using angular router.
this.router.navigate(["/search", "1", ""]);
// Everything after navigate does not not get executed.
this.sideFiltersService.discoverFilter(category);
this.router.navigate returns a promise so you can simply use:
this.router.navigate(["/search", "1", ""]).then(()=>{
// do whatever you need after navigation succeeds
});
// In javascript
this.router.navigate(["/search", "1", ""])
.then(succeeded => {
if(succeeded)
{
// Do your stuff
}
else
{
// Do some other stuff
}
})
.catch(error => {
// Handle the error
});
// In typescript you can use the javascript example as well.
// But you can also do:
try
{
let succeeded = await this.router.navigate(["/search", "1", ""]);
if(succeeded)
{
// Do your stuff
}
else
{
// Do some other stuff
}
}
catch(error)
{
// Handle the error
}
Not entirely sure of the context but an option would be to subscribe to a change in the URL using ActivatedRoute
https://angular.io/docs/ts/latest/guide/router.html#!#activated-route
Here's an example:
...
import { ActivatedRoute } from '#angular/router';
...
private _routerSubscription: any;
// Some class or service
constructor(private _route: ActivatedRoute){
this._routerSubscription = this._route.url.subscribe(url => {
// Your action/function will go here
});
}
There are many other observables you can subscribe to in ActivatedRoute which are listed in that link if url isn't quite what you need.
The subscription can be done in the constructor() or in an ngOnInit() depending on what suits you best, just remember to clean up after yourself and unsubscribe in an ngOnDestroy() :)
this._routerSubscription.unsubscribe();
If you are navigated from ComponentA to ComponentB then after navigating you can do any actions in ngOnInit() function of ComponentB, depending upon the parameters passed in the route.
You also have to ensure that there are no ongoing subscriptions... I faced the same problem and in my case there was a subscription which changed route. So the route has been changed twice. But practically you can use promises, thats right

Refresh a page in ionic2

Is there a way to refresh only a page i.e. only one screen in ionic2.
I tried :
window.location.reload();
and
location.reload();
but it rebuilds the app .. is there a way to refresh only that page (particular screen).
Also tried:
<ion-input *ngIf="no_internet === 1" (click)="refresh($event)"></ion-input>
in TypeScript:
refresh(refresher) {
console.log('Begin async operation', refresher);
setTimeout(() => {
console.log('Async operation has ended');
refresher.complete();
}, 2000);
}
Try this code :
this.navCtrl.setRoot(this.navCtrl.getActive().component);
You could also use the ionic refresher, to create a pull to refresh action on the page
http://ionicframework.com/docs/v2/api/components/refresher/Refresher/
I would do that : (based on #Ahmad Aghazadeh answer)
this.navCtrl.push(this.navCtrl.getActive().component).then(() => {
let index = this.viewCtrl.index;
this.navCtrl.remove(index);
})
=> Push this page once more (loading it again)
=> Remove the page we were on (using index)
Example of using ion-refresher in an async function in ionic 3:
in your .html file:
<ion-content no-padding >
<ion-refresher (ionRefresh)="doRefresh($event)">
<ion-refresher-content></ion-refresher-content>
</ion-refresher>
and in your .ts file:
constructor(...) {
...
samplefuncion(null){
asyncFunction().then(()=>{
...//after success call
...
if (event)
event.complete();
},(error)=>{
....
if (event)
event.complete();
})
}
doRefresh(event) {
samplefuncion(event);
}
If you are willing to follow a common convention, I have found a very easy way to reload the current view (including all of its parameters). I tested this using Ionic3, but it should still apply in Ionic 2
Move all of your initialization code for every page into ionViewDidLoad(), which is run ONCE on the first time the view is loaded
import { Component } from '#angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { Api } from '../../providers/api';
import { Movie } from '../../interfaces/movie';
#Component({
selector: 'page-movie-info',
templateUrl: 'movie-info.html'
})
export class MovieInfoPage {
constructor(
public navCtrl: NavController,
public navParams: NavParams,
public api: Api
) {
}
/**
* Run all page initialization in this method so that this page
* can be refreshed simply by re-calling this function
*/
ionViewDidLoad() {
//access any parameters provided to the page through navParams.
var movieId = this.navParams.data.movieId;
this.api.movies.getById(movieId).then((movie) => {
this.movie = movie;
});
}
public movie: Movie;
}
From anywhere else in the app, you can reload the current view with this code
//get the currently active page component
var component = this.navController.getActive().instance;
//re-run the view load function if the page has one declared
if (component.ionViewDidLoad) {
component.ionViewDidLoad();
}
Html:
<ion-refresher (ionRefresh)="doRefresh($event)">
<ion-refresher-content></ion-refresher-content>
</ion-refresher>
</ion-content>
TypeScript :
#Component({...})
export class NewsFeedPage {
doRefresh(refresher) {
console.log('Begin async operation', refresher);
setTimeout(() => {
console.log('Async operation has ended');
refresher.complete();
}, 2000);
}
}
source : Ionic doc
Try this, just pop one page and then push that page again.
this.navCtrl.pop();
this.navCtrl.push(NewPage);
Hope this will help.
Try this: $window.location.reload(); $route.reload() use to reload route.
if you are using $stateProvider : $state.go($state.current, {}, {reload: true});
or
var currentPageTemplate = $route.current.templateUrl;
$templateCache.remove(currentPageTemplate);
$route.reload();

Categories