External authentication with Azure Active Directory B2C - c#

Currently we are exploring the use of AspNetBoilerPlate (.NETCore and Agular) and one of the biggest pieces we need to be able to use is Azure Active Directory B2C. After trying various methods to get the login page to redirect over to the Azure B2C instance I have had no luck. My goal it so not need to override much if any of the base ABP code in order to accomplish this. In addition, I have what is recommended in the documentation with no success.
https://aspnetboilerplate.com/Pages/Documents/Zero/User-Management?searchKey=authentication#external-authentication
Sorry here are the pieces of code that I have with certain details removed:
Within the Portal.Web.Core project - Authentication I created a new class inheriting from ExternalAuthProviderApiBase and just hard-coded some values for the user. I still need to figure out how to get these from Azure AD B2C.
public class AzureActiveDirectoryB2CAuthProvider : ExternalAuthProviderApiBase
{
public const string Name = "AzureB2C";
public AzureActiveDirectoryB2CAuthProvider()
{
}
public override Task<ExternalAuthUserInfo> GetUserInfo(string accessCode)
{
ExternalAuthUserInfo user = new ExternalAuthUserInfo();
user.EmailAddress = "admin#aol.com";
user.Name = "Administrator";
user.Provider = this.ProviderInfo.Name;
user.ProviderKey = "12345";
user.Surname = "Cool Dude";
return Task.FromResult(user);
}
}
In the Portal.Web.Host --> Startup.cs I register the ExternalAuthProvider in the Configure method just after the app.UserAuthentication
var externalAuthConfiguration = app.ApplicationServices.GetRequiredService<ExternalAuthConfiguration>();
externalAuthConfiguration.Providers.Add(
new ExternalLoginProviderInfo(
AzureActiveDirectoryB2CAuthProvider.Name, string.Empty, string.Empty,
typeof(AzureActiveDirectoryB2CAuthProvider)
)
);
Now on the Angular2 side, I have added a reference to the MSAL.js library created by Microsoft to help handle the authentication.
Within the auth-route-guard.ts file I changed the canActivate method to call the login service which will try to determine if the user is already authenticated and if not redirect them to the Microsoft login page.
if (!this._sessionService.user) {
//this._router.navigate(['/account/login']);
this._loginService.updateUser();
return false;
}
Below you will see the code from the modified login.service.ts file minus the Azure AD B2C details.
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { TokenAuthServiceProxy, AuthenticateModel, AuthenticateResultModel, ExternalLoginProviderInfoModel, ExternalAuthenticateModel, ExternalAuthenticateResultModel } fro`enter code here`m '#shared/service-proxies/service-proxies';
import { UrlHelper } from '#shared/helpers/UrlHelper';
import { AppConsts } from '#shared/AppConsts';
import { MessageService } from '#abp/message/message.service';
import { LogService } from '#abp/log/log.service';
import { TokenService } from '#abp/auth/token.service';
import { UtilsService } from '#abp/utils/utils.service';
declare var Msal: any;
#Injectable()
export class LoginService {
static readonly twoFactorRememberClientTokenName = 'TwoFactorRememberClientToken';
private clientApplication: any;
authenticateModel: AuthenticateModel;
authenticateResult: AuthenticateResultModel;
externalAuthenticateModel: ExternalAuthenticateModel;
externalAuthenticateResult: ExternalAuthenticateResultModel;
rememberMe: boolean;
B2CTodoAccessTokenKey = "msal.idtoken";
tenantConfig = {
tenant: "",
clientID: '',
signUpSignInPolicy: "",
b2cScopes: ["openid"]
};
// Configure the authority for Azure AD B2C
authority = "https://login.microsoftonline.com/tfp/" + this.tenantConfig.tenant + "/" + this.tenantConfig.signUpSignInPolicy;
/*
* B2C SignIn SignUp Policy Configuration
*/
//clientApplication = new Msal.UserAgentApplication(
// this.tenantConfig.clientID, this.authority,
// function (errorDesc: any, token: any, error: any, tokenType: any) {
// // Called after loginRedirect or acquireTokenPopup
// }
//);
constructor(
private _tokenAuthService: TokenAuthServiceProxy,
private _router: Router,
private _utilsService: UtilsService,
private _messageService: MessageService,
private _tokenService: TokenService,
private _logService: LogService,
) {
this.clear();
this.clientApplication =
new Msal.UserAgentApplication(
this.tenantConfig.clientID,
this.authority,
this.authCallback);
}
authenticate(finallyCallback?: () => void): void {
finallyCallback = finallyCallback || (() => { });
var model = new ExternalAuthenticateModel;
model.authProvider = "AzureB2C";
model.providerAccessCode = this.getAccessToken();
model.providerKey = "12345";
this._tokenAuthService
.externalAuthenticate(model)
.finally(finallyCallback)
.subscribe((result: ExternalAuthenticateResultModel) => {
this.processExternalAuthenticateResult(result);
});
}
private processAuthenticateResult(authenticateResult: AuthenticateResultModel) {
this.authenticateResult = authenticateResult;
if (authenticateResult.accessToken) {
//Successfully logged in
this.login(authenticateResult.accessToken, authenticateResult.encryptedAccessToken, authenticateResult.expireInSeconds, this.rememberMe);
} else {
//Unexpected result!
this._logService.warn('Unexpected authenticateResult!');
this._router.navigate(['account/login']);
}
}
private processExternalAuthenticateResult(externalAuthenticateResult: ExternalAuthenticateResultModel) {
this.externalAuthenticateResult = externalAuthenticateResult;
alert(externalAuthenticateResult.accessToken);
if (externalAuthenticateResult.accessToken) {
//Successfully logged in
this.login(externalAuthenticateResult.accessToken, externalAuthenticateResult.encryptedAccessToken, externalAuthenticateResult.expireInSeconds, this.rememberMe);
} else {
//Unexpected result!
this._logService.warn('Unexpected externalAuthenticateResult!');
}
}
private login(accessToken: string, encryptedAccessToken: string, expireInSeconds: number, rememberMe?: boolean): void {
var tokenExpireDate = rememberMe ? (new Date(new Date().getTime() + 1000 * expireInSeconds)) : undefined;
this._tokenService.setToken(
accessToken,
tokenExpireDate
);
this._utilsService.setCookieValue(
AppConsts.authorization.encrptedAuthTokenName,
encryptedAccessToken,
tokenExpireDate,
abp.appPath
);
var initialUrl = UrlHelper.initialUrl;
if (initialUrl.indexOf('/login') > 0) {
initialUrl = AppConsts.appBaseUrl;
}
location.href = initialUrl;
}
private clear(): void {
this.authenticateModel = new AuthenticateModel();
this.authenticateModel.rememberClient = false;
this.authenticateResult = null;
this.rememberMe = false;
}
public AzureB2Clogin(): void {
this.isOnline();
this.clientApplication.loginRedirect(this.tenantConfig.b2cScopes);
}
saveAccessTokenToCache(accessToken: string): void {
sessionStorage.setItem(this.B2CTodoAccessTokenKey, accessToken);
};
public isOnline(): boolean {
return this.clientApplication.getUser() != null;
};
public getAccessToken(): string {
alert('AccessToken: ' + sessionStorage.getItem(this.B2CTodoAccessTokenKey));
return sessionStorage.getItem(this.B2CTodoAccessTokenKey);
}
public updateUser(): void {
if (this.isOnline()) {
this.authenticate();
}
else {
this.AzureB2Clogin();
}
}
public getAuthenticationToken(): Promise<string> {
return this.clientApplication.acquireTokenSilent(this.tenantConfig.b2cScopes)
.then(token => {
alert("Got silent access token: " + token);
return token;
}).catch(error => {
alert("Could not silently retrieve token from storage." + error);
return this.clientApplication.acquireTokenPopup(this.tenantConfig.b2cScopes)
.then(token => {
alert("Got popup access token: "+ token);
return token;
}).catch(error => {
alert("Could not retrieve token from popup." + error);
this.clientApplication.acquireTokenRedirect(this.tenantConfig.b2cScopes);
return Promise.resolve("");
});
});
}
private authCallback(errorDesc: any, token: any, error: any, tokenType: any) {
if (token) {
alert("Id token: " + token);
}
else {
alert(error + ":" + errorDesc);
}
this.getAuthenticationToken();
}
}
Finally index.html has the following script references added to it-->
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/0.1.3/js/msal.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>

Related

Angular oidc-client and duende IdentityServer - Problem with authorize on client-site, user unauthorized

I'm learning angular and I find a problem that I didn't see anywhere else. To the point, when I'm trying to login to my client angular app with oidc-client via identityserver, It's working great up to a point. I'm redirecting to a identityserver, logged in, goes back to the login-callback, in server-side i'm authorized, but if I want to check if I'm authorized in client-side, I got a null user. If i want to logout, I got redirect to sign out in identityserver where I can see my token:
Server-side config.cs:
public class Config
{
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Email(),
new IdentityResources.Profile(),
new IdentityResource("RedDot.API.read", new[] { JwtClaimTypes.Name, JwtClaimTypes.Email, "location" })
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("RedDot.API.read", "Resource API")
{
ApiSecrets =
{
new Secret("secret".Sha256())
},
UserClaims =
{
JwtClaimTypes.Name,
JwtClaimTypes.Email
},
Scopes = new List<string> { "RedDot.API.read" }
}
};
}
public static IEnumerable<Client> GetClients()
{
return new[]
{
new Client {
RequireConsent = false,
ClientId = "reddot_ui",
ClientName = "RedDot",
AllowedGrantTypes = GrantTypes.Code,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"RedDot.API.read"
},
RedirectUris = {"https://localhost:4200/login-callback"},
PostLogoutRedirectUris = {"https://localhost:4200/"},
AllowedCorsOrigins = {"https://localhost:4200"},
AllowAccessTokensViaBrowser = true,
AccessTokenLifetime = 3600,
ClientSecrets =
{
new Secret("rwebv832hvegfh49--l-w".Sha256())
},
}
};
}
}
Program.cs:
builder.Services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(dataConnectionString, conf => conf.MigrationsAssembly(assembly)));
builder.Services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddIdentityServer().AddDeveloperSigningCredential()
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(dataConnectionString, conf => conf.MigrationsAssembly(assembly));
})
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<AppUser>();
Client-side angular config:
private getUserManager() {
if (!this._userManager) {
const userManagerSettings: UserManagerSettings =
new UserManagerSettings();
userManagerSettings.authority = 'https://localhost:5443';
userManagerSettings.client_id = 'reddot_ui';
userManagerSettings.response_type = 'code';
userManagerSettings.scope = 'openid profile RedDot.API.read';
userManagerSettings.redirect_uri = 'https://localhost:4200/login-callback';
userManagerSettings.post_logout_redirect_uri = 'https://localhost:4200/logout-callback';
userManagerSettings.automaticSilentRenew = true;
userManagerSettings.silent_redirect_uri = 'https://localhost:4200/silent-callback';
userManagerSettings.userStore = new WebStorageStateStore({
store: window.localStorage,
}); // store information about Authentication in localStorage
this._userManager = new UserManager(userManagerSettings);
this._userManager.getUser().then((user) => {
this._user = user;
this.isUserDefined = true;
});
}
}
Rest of authorize class in angular:
import { Injectable } from "#angular/core";
import { User, UserManager, WebStorageStateStore } from "oidc-client";
import { UserManagerSettings } from "../_models/usermanager.settings";
#Injectable()
export class AuthenticationService {
isUserDefined = false;
private _user: User | null;
private _userManager: UserManager;
isLoggedIn() {
return this._user != null && !this._user.expired;
}
getName() {
return this._user?.profile.nickname;
}
getAccessToken() {
return this._user ? this._user.access_token : "";
}
getClaims() {
return this._user?.profile;
}
startAuthentication() : Promise<void> {
this.getUserManager();
return this._userManager.signinRedirect();
}
completeAuthentication() {
this.getUserManager();
return this._userManager.signinRedirectCallback().then((user) => {
this._user = user;
this.isUserDefined = true;
});
}
startLogout(): Promise<void> {
this.getUserManager();
return this._userManager.signoutRedirect();
}
completeLogout() {
this.getUserManager();
this._user = null;
return this._userManager.signoutRedirectCallback();
}
silentSignInAuthentication() {
this.getUserManager();
return this._userManager.signinSilentCallback();
}
}
Dunno where the problem can be and why I'm not authorized on client-side.
I've tried to change response type and protocol from http to https and conversely with no effect. Maybe somebody had the same problem.
The code you provided doesn't look like there is anything wrong. According to your description, you can log in and log out successfully, but you are not authorized. Does it mean that you cannot access the corresponding resources? Do you have any error messages?
I used https://demo.duendesoftware.com site to test with Angular application (here is the code of Angular), but did not reproduce your situation. You can see how the configuration items in it perform, and whether your configuration is missing some steps compared to it.
In addition, I found a sample code that is similar to your Angular program, but the Scope configuration is slightly different from yours. I am not sure if this is the reason, but you can use it as a reference

Send Session Id to SignalR Core OnConnected method

I am trying to send the Session Id of an ASP.NET Core 5 application using Razor Pages to my SignalR Core Hub. However, the Session Id is only added to the negotiate request, not to the actual WebSocket that is being opened:
How can I add it to the sessionsHub request as well, which is used by the OnConnected() method in the hub?
The Razor Page .cs:
public class IndexModel : PageModel
{
private readonly HttpContext _httpContext;
public string SessionId { get; set; }
public IndexModel(IHttpContextAccessor httpContextAccessor)
{
_httpContext = httpContextAccessor.HttpContext;
}
public async Task OnGet()
{
SessionId = _httpContext.Session.Id;
}
}
The .cshtml using a querystring, I've also tried adding a Session-Id as Header to the request, same result:
#page
#model IndexModel
<script type="text/javascript">
var sessionId = "#Model.SessionId";
class CustomHttpClient extends signalR.DefaultHttpClient {
send(request) {
var url = new URL(request.url);
if(!url.search){
url.href = url.href + '?sessionId="' + sessionId + '"';
}else{
url.href = url.href + '&sessionId="' + sessionId + '"';
}
request.url = url.href;
return super.send(request);
}
}
var connection = new signalR.HubConnectionBuilder().withUrl("/sessionsHub", { httpClient: new CustomHttpClient() }).build();
connection.start().then(() => {
}).catch(function (err) {
return console.error(err.toString());
});
</script>
The hub:
public class SessionHub : Hub{
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
string sessionId = GetSessionId();
}
private string GetSessionId()
{
HttpContext httpContext = Context.GetHttpContext();
List<StringValues> sessionIdQueryString = httpContext.Request.Query.Where(x => x.Key == "sessionId").Select(x => x.Value).ToList();
if (sessionIdQueryString.Count == 0)
{
throw new NullReferenceException();
}
string sessionId = sessionIdQueryString.First();
return sessionId;
}
}
You need to enter the sessionid value inside the url when creating the connetion.
so that it can be accessed in all hub methods The code will look like this :
var connection = new signalR.HubConnectionBuilder()
.withUrl("/sessionsHub/?sessionId=#Model.SessionId")
.build();
connection.start().then(function(){
//ToDo
}).catch(function (err) {
console.log(err);
});

401 (Unauthorized) request on Angular 7, .netcore application

Iam getting a 401 unauthorized error while authorizing an user to consume an api on .net core and angular 7 application.
My angular service has a function :-
getUserProfile() {
var tokenHeader = new HttpHeaders({'Authorization':'Bearer ' + localStorage.getItem('token')});
console.log(tokenHeader)
return this.http.get('/api/ApplicationUser/UserProfile', { headers: tokenHeader});
}
on tokenHeader I am sending the user jwt token.
My api is
[HttpGet]
[Authorize]
[Route("UserProfile")]
//'api/userProfile'
public async Task<Object> GetUserProfile()
{
string userId = User.Claims.First(c => c.Type == "UserID").Value;
var user = await _userManager.FindByIdAsync(userId);
return new { user.fullName, user.Email, user.UserName };
}
I have tried some answers from other questions but nothing helps.
Any helps appreciated.
Your code should be like this
const httpOptions = {
headers: new HttpHeaders({
'Authorization': `Bearer ${localStorage.getItem('token')}`
})
};
return this.http.get('/api/ApplicationUser/UserProfile', httpOptions);
Also make sure you have this line in your controller
[Authorize(AuthenticationSchemes = "Bearer")]
Can you share your request from network tab.
Also I recommend to use interceptors for make it global
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(public authService: AuthService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authService.getJwtToken()) {
request = this.addToken(request, this.authService.getJwtToken());
}
return next.handle(request).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handle401Error(request, next);
} else {
return throwError(error);
}
}));
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(token.jwt);
return next.handle(this.addToken(request, token.jwt));
}));
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
}
Full example https://github.com/bartosz-io/jwt-auth-angular/blob/master/src/app/auth/token.interceptor.ts

How to make a post and get works in a MVC project

I have an ASP.NET MVC app and I'm struggling with the connection between the typescript and the C#.
I can see that the C# is giving the response in the Inspect, the value is there but I don't know how to treat in Typescript.
C# Code:
namespace TEST.Controllers
{
[Route("api/[controller]")]
public class TestController : Controller
{
// GET api/GetTest
[HttpGet("GetTest")]
public IEnumerable<string> GetTest()
{
return new string[] { "Teste1", "Teste2" };
}
}
}
TypeScript SERVICE Code:
public getTest(): Observable<any> {
return this.dataService.get(this.baseUrl + '/GetTest')
.map((response: Response) => <any>response.json())
// .do(data => console.log("All: " + JSON.stringify(data)))
.catch(this.handleError);
}
Data Service Code (TypeScript):
public get<T>(url: string, params?: any): Observable<T> {
const options = new DataServiceOptions();
options.method = RequestMethod.Get;
options.url = url;
options.params = params;
return this.request(options);
}
private request(options: DataServiceOptions): Observable<any> {
options.method = (options.method || RequestMethod.Get);
options.url = (options.url || '');
options.headers = (options.headers || {});
options.params = (options.params || {});
options.data = (options.data || {});
this.interpolateUrl(options);
this.addXsrfToken(options);
this.addContentType(options);
this.addAuthToken(options);
// this.addCors(options);
const requestOptions = new RequestOptions();
requestOptions.method = options.method;
requestOptions.url = options.url;
requestOptions.headers = options.headers;
requestOptions.search = this.buildUrlSearchParams(options.params);
requestOptions.body = JSON.stringify(options.data);
this.pendingCommandsSubject.next(++this.pendingCommandCount);
const stream = this.http.request(options.url, requestOptions)
.catch((error: any) => {
this.handleErrors(error);
return Observable.throw(error);
})
.map(this.unwrapHttpValue)
.catch((error: any) => {
return Observable.throw(this.unwrapHttpError(error));
})
.finally(() => {
this.pendingCommandsSubject.next(--this.pendingCommandCount);
});
return stream;
}
The Calling:
private getDataBase() {
this.service.getTest().subscribe((res) => {
console.log(res);
this._proceduresImportData = res;
});
}
OBS: I also can console the observable, but I cannot treat it.
The best way to approach this is to have a generic request service and encapsulate your service calls, then inject that in where you need it. Taking get for an example (this can be expanded upon)
request.service.ts
import { Injectable } from "#angular/core";
import { Http, Response } from "#angular/http";
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/map";
import { WindowRef } from "./window.service";
#Injectable()
export class RequestService {
private baseUrl: string;
constructor(private http: Http, private windowRef: WindowRef) {
this.baseUrl = this.getBaseUrl();
}
public get<T>(resource: string): Observable<T> {
return this.http.get(this.baseUrl + resource)
.map<Response, T>(this.extractData);
}
private extractData(response: Response) {
return response.json();
}
private getBaseUrl(): string {
if (this.windowRef.getNativeWindow().location.hostname === "localhost") {
return "http://localhostAddress/api/";
} else if (this.windowRef.getNativeWindow().location.hostname === "anotherEnviroment") {
return "https://anotherAddress/api/";
}
}
}
window.service.ts
import { Injectable } from "#angular/core";
#Injectable()
export class WindowRef {
public getNativeWindow(): any {
return window;
}
}
This then returns an observable of the object you are expecting, used with a resolver or onInit it can be subscribed to where needed.
get-stuff.service.ts
import { Injectable } from "#angular/core";
import { Observable } from "rxjs/Observable";
import { RequestService } from "../common/request.service";
#Injectable()
export class Service {
constructor(private requestService: RequestService) { }
public getTestService(): void {
let requestedStuff: Observable<string[]> = this.requestService.get<string[]>(`GetTest`);
requestedStuff.subscribe(stuff: string[]) => {
//do stuff with your string
}
}
}
Then subscribe and use your data
Hope that helps

How to handle async data in ngOnInit (routing with parameter) Angular 4

I am trying to load data from my web api controller.
Currently I am using my API service which I call from the ngOnInit function of the component.
But, nothing return in the view because it's an asynchronous data
Web api controller
[HttpGet("[action]")]
public async Task<UserModel> GetUserById(int id)
{
Response.StatusCode = 200;
try
{
_context = new AuthentificationDbContext();
UserModel user = await _context.User.SingleOrDefaultAsync(m => m.id == id);
if (user == null)
{
return null;
}
else
return (user);
}
catch (SqlException ex)
{
throw ex;
}
}
userService.ts
getUserId(id: number) : Observable<User>{
return this.http.get(this.url + 'userApi/GetUserById/?id=' + id)
.map(res => <User>res.json())
.catch(this.handleError);
}
app.routing.ts
{ path: 'user/:id', component: UserComponent}
export const routing = RouterModule.forRoot(appRoutes,{
enableTracing:true});
export const routedComponents = [UserComponent];
user.component.ts
export class UserComponent implements OnInit {
private user: User;
constructor(private userService: UserService, private route: ActivatedRoute, private router: Router) { }
ngOnInit() {
this.route.paramMap
.switchMap((params: ParamMap) =>
this.userService.getUserId(+params.get('id')))
.subscribe((user: User) => {
this.user = user;
});
}
}
user.cshtml
<div *ngIf="user">{{ user.name}}</div>
But, when I tried with that example, that's work because not asynchronous
import { Injectable, Inject } from '#angular/core';
import { Http, Response, RequestOptions, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
export class User {
constructor(public id: number, public name: string) { }
}
let Users = [
new User(11, 'Mr. Nice'),
new User(12, 'Narco')
];
let usersPromise = Promise.resolve(Users);
#Injectable()
export class UserService {
constructor( #Inject(Http) public http: Http) { }
getUserId(id: number | string) {
return usersPromise
.then(users => users.find(user => user.id === +id));
}
}
My question : how to load async data in ngOnInit?
I used by promise also, but doesn't work
If you use
{{user.view}}
in the components template, you'll get an error, because user isn't available immediately.
{{user?.view}}
(safe-nativation operator) avoids this error by not throwing an exception when user is null.
I can resolve my problem (it's related to routing) :
my code just need to insert this :
<script>document.write('<base href="' + document.location + '" />');</script>
at the top of the 'head' section.
And to insert in constructor from app.component.ts, this methode:
click() {
this.router.navigate(['', { foo: 'bar' }]);
}

Categories