I'm very used to Ionic, and I recently started developping web apps with Angular2. I have an issue with the Router concept.
Let's say I want to make an app, with a login page, which redirects you to a dashboard with 3 tabs.
I Have a LoginPage component, which checks the login details and redirects to the app. How can I simply switch component in Angular 2 ?
I defined my route
const appRoutes: Routes = [
{ path: 'login', component: LoginPageComponent }
];
And on my app.component page, I juste have a button
<h1>
{{title}}
<!--<a routerLink="/login">Heroes</a>-->
<button (click)="login()">Change</button>
</h1>
trigerring this function :
login() {
this.router.navigate(['/login']);
}
When I click this button, my URL is changing : http://localhost:4200/login
But my view is still my view with the button, there is no "change" like in Ionic with a NavController.setRoot(NewComponent)
I don't want to display my content in a <router-outlet>, I'd like to have distincts pages with navigation between them. Is this possible in Angular2 ?
Thank in advance for your help
Related
I am trying to integrate the Google SSO using the Google Identity API's for the Angular 14 application.
The problem I am facing is, I can see the Sign In with Google button when I first come into Login screen. But if I go to other screen then do logout and when I am back to Login screen, the Sign In with google button is no more visible and I have to force refresh (Ctrl+Shift+R) to make it visible.
I have already gone through Why does the Sign In With Google button disappear after I render it the second time?
but it is unclear how to make feasible in my case.
As I can see an Iframe will be rendered during the 1st time and if I come back again to login page from other page I can not see the Iframe and the SignIn button is not visible.
Here is the code to load the sign in button from angular component
ngOnInit() {
// #ts-ignore
window.onGoogleLibraryLoad = () => {
// #ts-ignore
window.google.accounts.id.disableAutoSelect();
};
this.loadGoogleSSOScript('2.apps.googleusercontent.com');
}
loadGoogleSSOScript(clientId: string) {
// #ts-ignore
window.onGoogleLibraryLoad = () => {
// #ts-ignore
google.accounts.id.initialize({
client_id: clientId,
itp_support: true,
callback: this.handleCallback.bind(this),
});
// #ts-ignore
google.accounts.id.renderButton(
// #ts-ignore
document.getElementById('g_id_onload'),
{ theme: 'filled_blue', size: 'medium', width: '200px' }
);
// #ts-ignore
google.accounts.id.prompt(); // also display the dialog
};
}
Here is the link for Stackblitz which has full code.
How to solve this issue?
The way I solved this problem was to move the button to another place in the dom rather than allow it to be destroyed when my component is destroyed.
When I need the button again, I move it again. I use "display:none" when I'm storing the button in another location in the dom.
Here's an example of moving the button:
// to be called in onDestroy method of component that renders google button
storeButton() {
const con = this.document.getElementById(this.storageConID);
const btn = this.document.getElementById(this.googleButtonID);
con!.insertBefore(btn as any, con?.firstChild || null);
}
However, I found that trying to move the button between locations too quickly, with multiple consecutive calls to el.insertBefore would actually cause the button to disappear from the dom altogether for some reason.
In my case, I was navigating between a login and a signup page and both needed to display the button. To get around that issue, I used a MutationObserver and made sure that I didn't try to move the button out of it's "storage" location until it was actually there.
I added this to my index.html as a place to store the button when it shouldn't be displayed.
<div id="google-btn-storage-con">
<div id="google-btn" class="flex-row justify-center items-center hidden"></div>
</div>
The div with the id "google-btn" is the element I pass into the google.accounts.id.renderButton method to render the button initially on the page.
When I need to display the google button, then I move the div with the id "google-btn" into my component.
I hope that this little bit of code and explanation is enough. I would share more but the actual implementation of all this is hundreds of lines long (including using MutationObserver and dynamically loading the gsi script).
In Angular you shouldn't directly access the DOM to get the element, you can use ViewChild.
//HTML
<div #gbutton></div>
//TS
export class LoginComponent implements OnInit, AfterViewInit {
#ViewChild('gbutton') gbutton: ElementRef = new ElementRef({});
constructor() { }
ngAfterViewInit() {
google.accounts.id.initialize({
client_id: clientId,
itp_support: true,
callback: this.handleCallback.bind(this),
});
google.accounts.id.renderButton(
this.gbutton.nativeElement,
{
type: "standard", theme: "outline",
size: "medium", width: "50", shape: "pill", ux_mode: "popup",
}
)
}
I got a strange behavior from Google Identity Service signin button while implementing it with react. When I first visit the signin page the Google signin button do not appear but one tap window appear. If I refresh the page then both appears. After that if I navigate to other page and come back to signin page button disappear again but one tap window appear.
page first loading
page after browser refresh
I used the following code for signin button
renderGoogleSignInButton = () => {
return (
<>
<div
id="g_id_onload"
data-client_id="MY_CLIENT_ID"
data-auto_prompt="false"
data-auto_select="true"
data-callback="handleCredentialResponse"
></div>
<div
className="g_id_signin mt-4 flex justify-center"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="sign_in_with"
data-shape="rectangular"
data-logo_alignment="left"
></div>
</>
)
}
and following code for one tap window
componentDidMount() {
google.accounts.id.initialize({
client_id: MY_CLIENT_ID,
callback: this.handleCredentialResponse,
})
google.accounts.id.prompt()
}
I didn't find any clue using google search, not even in the docs.
Thanks in advance for your help.
For those who will have this problem in future with react.
constructor(props) {
super(props)
window.handleCredentialResponse = this.handleCredentialResponse
this.myRef = React.createRef()
}
componentDidMount() {
if (window.google) {
window.google.accounts.id.initialize({
client_id:MY_CLIENT_ID
callback: this.handleCredentialResponse,
})
window.google.accounts.id.prompt()
window.google.accounts.id.renderButton(this.myRef.current, {
theme: 'outline',
size: 'large',
})
}
}
......
}
It sounds like the One Tap prompt is being displayed as expected, but it was a little unclear. If you're having issues with One Tap, Check out the PromptMomentNotification and getNotDisplayedReason values, they should help you to understand the reason why the One Tap prompt may not be displayed. You might also consider One Tap in an iframe if there's an issue with React being hard to debug.
For the button, if you've not found anything suspicious with pre-rendering, caching or when the div tag containing g_id_signin is loaded you might try rendering the button browser side using JS and renderButton.
I had the same issue - login button would not appear without a refresh. For me, it was simply a matter of moving the below logic from App.tsx to Login.tsx.
useEffect(() => {
dispatch(setCurrentPage("login"));
google.accounts.id.initialize({
client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID!,
callback: handleCallbackResponse
});
// eslint-disable-next-line #typescript-eslint/no-non-null-assertion
const googleLoginDiv: HTMLElement = document.getElementById("googleLoginDiv")!;
google.accounts.id.renderButton(googleLoginDiv, {
type: "standard",
theme: "outline",
size: "large"
});
}, []);
I am quite new to Meteor & React. Here I would like to redirect my currect user to home page whenever the logout button is pressed. Attached you can see the protected page template with the logout button.
Please note that I am working with the latest versions (Meteor 1.6.1 and React V4).
import React from 'react';
import { Accounts } from 'meteor/accounts-base';
export default class Link extends React.Component{
onLogout(){
Accounts.logout()
};
render(){
return(
<div>
<p>Private Content goes here</p>
<button onClick={this.onLogout.bind(this)}>Logout</button>
</div>
);
}
};
any kind of support will be appreciated.
There are two main options to use here:
1. Pass a callback to Accounts.logout(func)
This is the simplest but mixes the return behavior into your component, which is not ideal.
2. Use Accounts.onLogout(func)
You could put this with your accounts initialization or with your router code, whichever keeps the logic grouped together best for your app.
In that callback, you'll want to use your router to redirect. The exact syntax will depend on your router, but will generally look like:
Router.go('/')
Another way, if you are setting things via meteor-useraccounts way...
const myLogoutFunc = function() {
FlowRouter.go('/login');
}
AccountsTemplates.configure({
// Hooks
onLogoutHook: myLogoutFunc,
onSubmitHook: mySubmitFunc,
preSignUpHook: myPreSubmitFunc,
postSignUpHook: myPostSubmitFunc,
});
Template event code is something like this..
'click .logout': () => {
AccountsTemplates.logout();
}
Read in detail here https://github.com/meteor-useraccounts/core/blob/master/Guide.md
I am developing a mini SPA, using angular, inside my ASP.Net MVC 5 application. Basically I want to use this mini SPA for onboarding/registration workflow, and use ASP.Net MVC views and routes for other functionality.
For Onboarding mini SPA I have defined an MVC route:
routes.MapRoute(
name: "Onboarding",
url: "onboarding/{*catchall}",
defaults: new { controller = "Onboarding", action = "Create" }
);
The Angular configuration looks like:
(function () {
var app = angular.module('app', ['ngRoute']);
app.config(function ($routeProvider, $locationProvider) {
$routeProvider.caseInsensitiveMatch = true;
$routeProvider.when('/onboarding', { templateUrl: '/app/Views/CreateShop.html', controller: 'onboardingController' })
.when('/onboarding/add-product', { templateUrl: '/app/Views/AddProduct.html', controller: 'onboardingController' })
$locationProvider.html5Mode(true);
});}());
The Create.chtml uses acts as a container/entry point for this mini SPA.
<div ng-controller="onboardingController">
<div ng-view></div>
</div>
#section scripts {
#Scripts.Render("~/bundles/angularOnboarding")
}
PROBLEM: When I am inside the mini SPA my navigation links/routes break e.g. navigation to contact page or about page (MVC Views) doesn't work. Angular seems to handling all routing requests. Also, navigation from an anchor tag which navigates to some MVC route doesn't work.
Putting the url in the browser works.
How do I make this work for my hybrid scenario?
Would really appreciate any suggestions or solution, Thanks.
I have a question on how to configure the inbuilt login functionality URL in ASP.net MVC Angular template. The web api url for login is "Account/Login" .
I add an href in layout.cshtml file as shown below
<data-ng-class="{active : activeViewPath==='/contact'}">
<br>
< href='#/contact'>Contact</a></li>
<br>
< data-ng-class="{active : activeViewPath==='/Login'}">
<br>
< href='~/Account/Login'>Login</a></li>
Now when I click any link(demo) , the functional URL is :
://ayz.com/#/demo
When I click Login (works fine):
://ayz.com/Account/Login#/home
When I click demo again (Account/Login gets appended):
://ayz.com/Account/Login#/demo
How to correct this ?
$routeProvider
.when('/home', { templateUrl: '/home/main', controller: 'MainController' })
.when('/contact', { templateUrl: '/home/contact', controller: 'ContactController' })
.when('/about', { templateUrl: '/home/about', controller: 'AboutController' })
.when('/demo', { templateUrl: '/home/demo', controller: 'DemoController' })
.when('/product', { templateUrl: '/home/product', controller: 'ProductController' })
Make sure that ng-view /ng-route just changes the view that is a part of the angular only.!!
As you were already in: //ayz.com/Account/Login
//ayz.com/Account/Login#/home
When you click on demo, the view is just getting updated:
//ayz.com/Account/Login#/demo
Make sure, you use angular route for Account/Logon too.
Try getting the usage of hashbang too.!! Angular js uses that and just updates the part after that for ng-view.!!
Firstly when you were in demo: ayz.com/#/demo with no MVC routing.
And later on when you clicked on Accountlogin the url changed to ayz.com/Account/Logon/#home
and now when you clicked on demo, the last hasbang part of url "#/home" will just be changed to "#/demo" as you are now in Account/Login. And note that Angular routing works only for one ng-View in a single page.