external redirection in AngularJS erasing navigation stack - angularjs

We have set up different applications something like micro frontends running in different versions of Angular, one of them which is in AngularJS is required to redirect to a url which is in another project. Everything works until you try to go back using the browsers back button, and it takes you to the previous domain the user has navigated to. It can't go back to the immediately previous URL. It seems the navigation has been replaced but I can't see anything on the project that uses location.replace.
for example:
I am navigating
www.anyurl.com
then i go to my projects dns like
myproject.com/employer/dashboard
after I can click on to desired button which will take me to
myproject.com/employer/job-flow#location
Up to there everything is fine, but If I click on the browsers back button instead of taking me to:
myproject.com/employer/dashboard
It will go to the previous domain:
www.anyurl.com
I hope this is a clear explanation
from here: /employer/search to /employer/job-flow#location
The Process:
Declare a new item in the menu:
..., {
title: 'Post a job',
mainPath: 'job-flow',
fallbackUrl: 'employer/job-flow',
image: '/assets/images/new-job.svg',
notification: false,
subitems: [],
enabled: true
}
In the HTML there is a:
ng-href="{{ item.fallbackUrl }}"
Then there is an angular module which stores an array with all the routes:
var employerWebRoutes = [
...
'employer/job-flow'
];
Afterwards the array is iterated (I think it works like an interceptor for the given routes):
employerWebRoutes.forEach( function ( route ) {
$stateRegistryProvider.register( {
url: '/' + route + '{path:.*}',
name: route + 'ExternalState',
component: 'merlExternalRoutesComponent',
resolve: {
reloadFromServer: reloadFromServerResolver
},
data: {
authAccess: 'private',
isExternalRoute: true
}
} );
} );
And lastly the reloadFromServerResolver method
var reloadFromServerResolver = [
'$transition$',
'$q',
function (
$transition$,
/** angular.IQService */ $q
) {
var /** #type module:angular.ui.IStateService */ stateService = $transition$.router.stateService;
var /** #type string */ toUrl = stateService.href( $transition$.to().name, $transition$.params() );
setTimeout( function () {
window.location.assign( toUrl );
}, 10 );
return $q.reject();
}
];
I am guessing this is how is done, I am new to this project and can't figure very well how to describe this problem, if anyone can point in the right direction I would be highly grateful. Thanks

Related

Firefox WebExtensions: How to open a background page as a unique tab?

The question: In Firefox WebExtensions, from arbitrary background origins, how can I open an arbitrary page in a tab, uniquely?
Requirements:
1) Arbitrary background origins. My initial use case is from browser_action contextMenus. However, the technique ought to work from popup, options, or any custom background script. For the moment, not concerned with content pages. Although if the technique works for them too, great.
2) Open an arbitrary page in a tab. My initial use is for the options page, but the technique should work for arbitrary background pages (options, popup, or custom pages).
3) Open uniquely. If it's the first time, open in a new tab. If the tab was previously opened, focus to the existing tab, don't create duplicate tabs.
4) If I close a tab, I need to make sure I remove my reference to the previously opened tab ID.
I've answered my own question with one possible solution.
If the solution could be improved upon, let me know. Thank you.
If the question is a duplicate, I'll remove and post solution elsewhere if appropriate.
This is an attempt to answer my own question, with comments inline.
This simple example assumes all pagers live in the top level directory, but this is arbitrary and easily changed.
Essentially it consists of four parts:
1) A global object tabIDs to hold the page names (without '.html'). You could change this to be full path including extension, or keep the page name as a short name and modify technique to use another option like path for the full path name, etc.
2) An async function (to make use of the new await feature) named tabCreate to determine if one is already open and switch to it or create a new tab.
3) An onRemoved event handler, tabRemove to handle cleanup of tabIDs after a tab is closed (if it was one of interest).
4) A usage example, from a context menu item, passing some a page and a panel option, which have no use in this question, but are part of another question, and just here for demonstration purposes.
background.js:
// tabID contexts
// global var to keep track of different tabs,
// i.e. options.html, popup.html and so on.
var tabIDs = {
'options': null,
'popup': null,
}
// Requires Firefox 52.0+ to use async/await
// opts correspond to contexts above in tabIDs
// of the form { 'page': 'options' } or { 'page': 'popup' }
// note if using Node.js, this may require v7+ and --harmony_async_await
async function tabCreate ( opts ) {
var tab;
if ( tabIDs[ opts.page ] !== null ) {
// should probably bring window to front first... oops
// ..
// switch to tab
tab = await browser.tabs.update( tabIDs[ opts.page ], { active: true } );
} else {
tab = await browser.tabs.create( {
'url': opts.page + '.html'
} );
tabIDs[ opts.page ] = tab.id;
}
console.log( '**** opts.page = ' + opts.page + ', opts.tab = ' + opts.tab + ', tab.id = ' + tab.id );
}
// When tabs are closed, see if the tabID is in tabIDs,
// and if so, set it to null
function tabRemove ( tabID, removeInfo ) {
console.log( 'Closed TAB ' + tabID );
Object.keys( tabIDs ).forEach( function( key, index ) {
if ( tabIDs[ key ] === tabID ) {
tabIDs[ key ] = null;
return;
}
} );
}
browser.tabs.onRemoved.addListener( tabRemove );
/*
* Context Menus
*/
browser.contextMenus.removeAll( );
browser.contextMenus.create( {
title: 'My Web Extension',
contexts: [ 'browser_action' ],
} );
browser.contextMenus.create( {
title: 'Options',
contexts: [ 'browser_action' ],
onclick: myWebExt_Options
} );
function myWebExt_Options ( ) {
tabCreate( {
'page': 'options',
'panel': 1,
} );
}
Another approach might be to add an event listener to each page, and when closed, send a message back to background.js, but that seems much more complicated with little or no benefit.

Polymer: Step through array on button click

I am trying to update the URL bar on a button press, and cycle through JSON objects that are declared in the tag array.
The iterator works fine when the window.location.href = "http://localhost:777/tag/" + this.tag[this.counter]; line is commented out, but gets stuck on the first array item when this line is active.
I think counter gets reset when the page is refreshed. Is there a good way to save the state of the counter so I can cycle through the array as the URL changes?
<template>
<paper-button class="menu-button" on-tap="leftArrowButton">Button Text</paper-button>
</template>
<script>
Polymer({
is: 'sub-menu',
properties: {
tag: {
type: Array,
value: function () {
return ['one', 'two', 'three', 'four'];
}
},
counter: {
type: Number,
value: 0
}
},
leftArrowButton: function (e) {
this.counter = (this.counter + 1) % this.tag.length;
console.log(this.counter);
console.log(this.tag.length);
console.log(this.tag[this.counter]);
console.log("http://localhost:777/tag/" + this.tag[this.counter])
window.location.href = "http://localhost:777/tag/" + this.tag[this.counter];
}
});
</script>
You need a router to manage your app's state between pages. Check out app-route. It was built by the Polymer team as a "web components" approach to routing.
Using your links will reload the page and reset the counter. You might either want to use hashbang in your URLs like http://localhost:777/tag#one or query params like http://localhost:777/tag?choice=one. Prior will prevent a page load, latter won't but lets you parse the link (see below).
If altering the link structure is no option you could read the currently selected option from location.pathname in leftArrowButton, calculate the index of the corresponding tag entry, choose the next and build your link with that:
leftArrowButton: function (e) {
var current = location.href.substr(location.href.lastIndexOf('/') + 1);
var index = this.tag.indexOf(current);
var counter = (index + 1) % tags.length;
...

Could we add our menu items in Gitkit Starter kit "Sign In cum User Info " ( #navbar )?

Could we add our menu items in Starter kit Gitkit NavBar ?
There are two list items in the drop down : Manage Account and Sign Out.
Is it possible to add a third option with a URL link ( say like Update Profile ) ?
The html for the #navbar gets loaded through Javascript code of Gitkit.
I use GAE Python.
Possible solutions which I could think of are :
After my webpage loads completely, I could add my own <li> items to the list of dropdown menu provided by #navbar.
Or
Write a custom "Sign In cum User Info " ( #navbar ) widget.
Is there a better and simpler approach ?
MY REQUEST TO GITKIT TEAM FOR ENHANCEMENT
It would be great if we could provide our custom menu items along with their URL links as options to below JS code which loads #navbar :
<script type=text/javascript>
window.google.identitytoolkit.signInButton(
'#navbar', // accepts any CSS selector
{
widgetUrl: "http://www.mywebsite.com/oauth2callback",
signOutUrl: "/",
// Example of possible solution ( my suggestion ):
custom_menu_item__1__name : "item_1", // My Custom Menu Item 1
custom_menu_item__1__link : "http://site/link_url_1",
::
custom_menu_item__n__name : "item_1", // My Custom Menu Item n
custom_menu_item__n__link : "http://site/link_url_1",
}
);
</script>
UPDATE
Temporary Fix = I have added the needed menu options using jquery temporarily. Code snippet provided below to help anyone with similar needs till official solution arrives :
On page load,
custom_menu_add_job_id = setInterval(function(){
add_custom_menu();
}, 5000);
function add_custom_menu(){
if ($("#navbar").find(".gitkit-user-card-menu").length){
$(".gitkit-user-card-menu").append($("<li class='gitkit-user-card-menuitem' id='smh_user_profile' tabindex='0'> <img src='/images/person_32x32.png' class='user_profile_menu_icon' > Profile </li>")
.click(function(){
window.location.href = window.location.protocol + "//" + window.location.host + "/user/";
})
);
clearInterval(custom_menu_add_job_id);
}
}
If you want, you could check it live at ShowMyHall.
Customized menu items are now supported in Google Identity Toolkit javascript widget. Examples:
window.google.identitytoolkit.signInButton(
'#navbar', // accepts any CSS selector
{
widgetUrl: "...",
dropDownMenu: [
{
'label': 'Check Configuration',
'url': '/config'
},
{
'label': 'Sign out',
'handler': function() {google.identitytoolkit.signOut();}
},
{
'label': 'Manage Account',
'handler': function() {google.identitytoolkit.manageAccount();}
},
]
};
Until this feature arrives, I also implemented a similar temporary fix that you outlined at the end of your question. I got around using a timer as follows (note that my gitkit is using the div login):
$(window).load(function() {
$("#login").hover(function() {
add_custom_menu_items();
})
});
function add_custom_menu_items(){
if ($("#login").find(".gitkit-user-card-menu").length == 1){
if ($("#my_data_link").length == 0) {
$(".gitkit-user-card-menu li:eq(0)").after($('<li class="gitkit-user-card-menuitem" id="my_data_link" tabindex="0">My data</li>'));
}
}
}
Basically when you hover over the div it adds the menu item, but only if it hasn't already been added.
The navbar drop down menu does not support images but if you really need that, here's a hacky way to do it in jquery:
var config = {...}; // your config which includes the custom drop down menu.
// Render button on load. (now supported)
window.onload = function() {
window.google.identitytoolkit.signInButton(
'#navbar', // accepts any CSS selector
config);
// This will modify the html content of the first option in drop down menu.
// Make menu dom changes.
jQuery('#navbar li:first-child').html('<img src="img.png">My Label');
}

Backbone.history.navigate with IE <= 9

Having an issue with using Backbone history / push state - but only with browsers that don't support it (old IE)
The issue is this. When I visit /en_gb/dashboard for the first time - everything works, in all browsers. However, in IE<=9, it's appending #dashboard to the address bar, forming /en_gb/dashboard#dashboard. Now, when I hit refresh, my router is not triggering.
Not all my site is under Backbone control - so the router is working off:
routes: {
'dashboard': 'showDashboard'
}
My bootstrap looks like this:
if (Backbone.history) {
var pushStateSupported = _.isFunction(history.pushState);
var urlRoot = '/en_gb/';
var enableSilent = !pushStateSupported;
Backbone.history.start({
pushState: pushStateSupported,
root: urlRoot,
silent: enableSilent
});
if (!pushStateSupported) {
Backbone.history.navigate(window.location.pathname.substring(urlRoot.length), { trigger: true });
}
}
Adding debug, I can see Backbone.history.navigate() always being called but it seems the trigger: true is not being picked up when that hash is present.
Hmm - I seem to have fixed it - while not an elegant solution, this does solve it for me:
if (!pushStateSupported) {
var route = window.location.pathname.substring(urlRoot.length);
Backbone.history.navigate('/#' + route, { trigger: true });
}
It's not elegant in that the URL in the address bar appears as /en_gb/dashboard##dashboard - but it is now getting through the Backbone.navigate() method. Previously it was failing on
if (this.fragment === fragment) return;

Reverse a route in Backbone js

Similar to Django's {{ url }}, is there a method or way to reverse a particular route by passing it a name and variables.
// example Router
var router = Backbone.Router.extend({
routes: {
'!/user/:user_id': 'editUserAction',
'!/': 'homeAction'
},
editUserAction(user_id) {
// edit user view
},
homeAction() {
// home view
}
});
Some method like
router.reverse('editUserAction', '5');
Would return the hash: !/user/5
router.reverse('homeAction');
Would return the hash: !/
A discussion about reverse routing. https://github.com/documentcloud/backbone/issues/951
a simple hack
var personRoutes = {
list: "/persons",
show: "/persons/:id",
edit: "/persons/:id/edit"
}
var getRoute = function(obj, route, routeDefinitions){
return routeDefinitions[route].replace(":id", obj.id);
}
var person = new Model({id: 1});
var route = getRoute(person, "edit", personRoutes); // => "/persons/1/edit"
Unfortunately no, there isn't anything like this built in to backbone. I've wanted something similar and there has been discussion of this on the list once or twice - maybe even a pull request (don't remember for sure at the moment). But it has not yet been done.
The best that I've come up with is to write my own methods that produce the correct route string:
function userEditPath(userId){
return "/user/" + userId + "/edit";
}

Resources