I tried to make example Ionic application with API with my laravel project. I made Laravel json response api routes. But in my Ionic home page nothing shows up and it reports me this error: Access to XMLHttpRequest at 'http://b.1p1eqpotato.com/ib/?p=1' from origin 'http://localhost:8100' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
zone-evergreen.js:2952 POST http://b.1p1eqpotato.com/ib/?p=1 net::ERR_FAILED.
And this is console.log of my laravel posts (3) [{…}, {…}, {…}]0: {id: 1, name: "John Doe", course: "Udemy Academy", created_at: "2020-02-01 13:14:40", updated_at: "2020-02-01 13:14:40"}1: {id: 2, name: "Mike Doe", course: "Software Engineering", created_at: "2020-02-01 13:16:43", updated_at: "2020-02-01 13:16:43"}2: {id: 3, name: "Trevor Doe", course: "Api Ionic Laravel", created_at: "2020-02-01 13:58:24", updated_at: "2020-02-01 13:58:24"}length: 3__proto__: Array(0)
udnefined home.page.ts:24
So this is my Ionic code.
My app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { RouteReuseStrategy } from '#angular/router';
import { IonicModule, IonicRouteStrategy } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule, HttpClient } from '#angular/common/http';
#NgModule({
declarations: [ AppComponent ],
entryComponents: [],
imports: [ BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule ],
providers: [ StatusBar, SplashScreen, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy } ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
My api.service.ts:
import { Injectable } from '#angular/core';
import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
import { catchError, tap, map } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({'Content-type': 'applications/json'})
}
const apiUrl = "http://localhost:8000/api/students";
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) { }
private handleError(error: HttpErrorResponse) {
if(error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error(
`Backend returned code ${error.status},` +
`body was: ${error.error}`
);
}
return throwError('Something is wrong, please try again.');
}
private extractData(res: Response) {
let body = res;
return body || { };
}
getDataUser(): Observable<any> {
return this.http.get(apiUrl, httpOptions).pipe(
map(this.extractData),
catchError(this.handleError)
);
}
}
This is home.page.ts:
import { Component } from '#angular/core';
import { ApiService } from './../api.service';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
datauser: any;
constructor(
public api: ApiService
) {}
ngOnInit(){
this.getDataUser();
}
async getDataUser() {
await this.api.getDataUser()
.subscribe(res => {
console.log(res);
this.datauser = res.results;
console.log(this.datauser);
}, err => {
console.log(err);
});
}
}
And this is my home.page.html:
<ion-header>
<ion-toolbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item *ngFor="let data of datauser">
{{data.name}}
</ion-item>
</ion-list>
</ion-content>
So it is showing me array in console but on the Home page view there is nothing. And this error:
Is there any solutions for not showing on the view and this XMLHttpRequest error? Please help! Thank you
EDITED
This is my ApiController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Student;
class ApiController extends Controller
{
public function getAllStudents() {
// logic to get all students goes here
$students = Student::get()->toJson(JSON_PRETTY_PRINT);
return response($students, 200);
}
public function createStudent(Request $request) {
// logic to create a student record goes here
$student = new Student;
$student->name = $request->name;
$student->course = $request->course;
$student->save();
return response()->json([
"message" => "student record created"
], 201);
}
public function getStudent($id) {
// logic to get a student record goes here
}
public function updateStudent(Request $request, $id) {
// logic to update a student record goes here
}
public function deleteStudent ($id) {
// logic to delete a student record goes here
}
}
This is my api.php routes:
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::get('students', 'ApiController#getAllStudents');
Route::get('students/{id}', 'ApiController#getStudent');
//Route::post('students', 'ApiController#createStudent');
Route::put('students/{id}', 'ApiController#updateStudent');
Route::delete('students/{id}','ApiController#deleteStudent');
And this is my Student.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
protected $table = 'students';
protected $fillable = ['name', 'course'];
}
According to Mozilla developer website, CORS happen when a web application tried to send requests to resource that has a different origin (domain, protocol, and port) than its own origin. In my case, http request was made from same host as target resource (localhost) but different port number.
Solution
If you google “how to fix/allow CORS” then you’ll find some websites tells you to add extra headers to your HTTP response by modifying web server configuration. But there is another way you can do in case you have a very little experience with server configuration or you were unable to reach infra guys to ask their help, fix it with Middleware.
What is Middleware?
Middleware is a mechanism for filtering HTTP request coming to your application so you can easily modify HTTP Request and Response in a very convenient way.
Now let’s get to step-by-step:
Create The Class
Create a new file CorsMiddleware.php inside directory app\Http\Middleware
<?php
/**
* Location: /app/Http/Middleware
*/
namespace App\Http\Middleware;
use Closure;
class CorsMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$headers = [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST, GET, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Max-Age' => '86400',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With'
];
if ($request->isMethod('OPTIONS'))
{
return response()->json('{"method":"OPTIONS"}', 200, $headers);
}
$response = $next($request);
foreach($headers as $key => $value)
{
$response->header($key, $value);
}
return $response;
}
}
This class is used to modify Http Response header, what important here is line number 22 where we tell browser to accept request from any sources (*)
'Access-Control-Allow-Origin' => '*'
Register Middleware
In order to make CorsMiddleware class known by Laravel next you have to Register it in app\Http\Kernel.php
$app->middleware([
//...
App\Http\Middleware\CorsMiddleware::class
]);
Now you should find that error no more.
Full reading source
Related
I am using JWT Authentication in my project and it is working well.
The issue I am facing is that before using HTTP INTERCEPTOR I was able to get a normal JSON response from the backend (Spring Boot REST API).
But, after using HTTP INTERCEPTOR (for adding AUTHENTICATION header in all HTTP requests) I am not getting JSON response, instead I am getting response as [Object object].
Most important, the backend is giving response in JSON format, I checked it using postman.
auth.interceptor.ts file
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
//get token from auth service
let token: any = this.authService.getToken();
//check if guest user is a first time visitor
if (!token) {
alert('no token present');
return next.handle(request.clone());
}
//add token and header to the request
request = this.addTokenAndHeader(request, token);
//return
return next.handle(request).pipe(
catchError((err: HttpErrorResponse) => {
alert('inside catch and pipe');
//redirect to login page if error code 401 or 403
if (err.status === 401 || err.status === 403) {
alert(err.status);
this.authService.clear();
this.router.navigateByUrl('/access/login');
}
return throwError('Something went wrong.');
})
);
}
//add token to http request
private addTokenAndHeader(request: HttpRequest<any>, token: string) {
alert('inside add token and header method');
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
}
loader.interceptor.ts
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
this.loaderService.isLoading.next(true);
return next.handle(request).pipe(
finalize(() => {
this.loaderService.isLoading.next(false);
})
);
}
app.module.ts file
#NgModule({
declarations: [AppComponent, NoInternetComponent],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
MaterialModule,
BrowserAnimationsModule,
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: LoaderInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
user.service.ts - where I am calling API URL. Here I was able to get normal JSON response. But, not getting after using HTTP INTERCEPTOR. Most important, the backend is giving response in JSON format, I checked it using postman.
getUserByPhone(phone: any) {
return new Promise((resolve) => {
this.http
.get(this.constants.apiURL + '/user/get/phone/' + phone)
.subscribe((data: any) => {
alert('inside getuserbyphone method');
alert(data);
resolve(data);
});
});
}
Your help will be highly appreciated. Please come forward to help me in this case, if you have any information reagrding the same. Thanks in advance for solving my problem. Really appreciate it.
There are two ways to handle this , and one of those you already tried i.e using 'JSON.stringify'.Although this may not be a bad option considering interceptors will remain in place. But if you can't/don't want to update your application code already written and just wanted to achieve this via interceptor then in that case I believe you need to update your interceptor code to format response data to a JSON format before consuming it in the application.
You should create a separate interceptor ( best practice and is completely optional if you want to do this in same interceptor) just to format the response. Since you have not share it , do you mind checking the typeof the [Object object] in the response, I assume it should be obviously HTTPResponse type.
By default , you should see response data in the 'body' key of the returned data. I have created a quick example and below are the snippet for the interceptor.
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { Observable } from "rxjs";
import { filter, map } from "rxjs/operators";
#Injectable({
providedIn: 'root',
})
export class FormatResponseInterceptor implements HttpInterceptor {
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
filter(event => event instanceof HttpResponse),
map((event: HttpResponse<any>) => event.clone({ body: event.body }))
);
}
}
so as you see the response if event.body and you can directly consume it in your application , as below :
import { SampleService } from './service/sample.service';
export interface CatInterface {
fact: string | '',
length: number | 0;
}
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular ' + VERSION.major;
apiData: CatInterface;
constructor(private sampleService: SampleService){}
ngOnInit(){
this.invokeAPI();
}
private invokeAPI(){
this.sampleService.readData().subscribe({
next: (res)=>{
this.apiData = {...res};
},
error: (err)=>{},
complete:()=>{console.log('Service Subscription Completed!');
}
})
}
}
in the above code , I just deconstructed the response object.
So you should first need to check the response object structure and consume it accordingly.
For additional notes, here is my app.component.html code :
<hello name="{{ name }}"></hello>
<p><span style="background-color: yellow"> FACT from the API is : </span> <b> {{apiData?.fact}}</b></p>
<p> <span style="background-color: yellow">LENGTH from the API is : </span><b> {{apiData?.length}}</b></p>
and below is the screen shot for the output :
Hopefully this will help you to solve your problem. Please let me know by providing your feedback so that it will help others as well in future.
I have .Net 4.6.2 VS 2017 Mvc application, with Angular 5, "rxjs": "^5.5.10"
I am trying to get data for Kendo UI grid through controller. The controller is returning data which I can see, but in the service class at code .map(response => response.json()), it says illegal return statement.(Please see attached image)
err img2
Here is vto.service.ts
import { Injectable } from '#angular/core';
import { VTO } from './vto';
import { Http, HttpModule, Headers, Response } from '#angular/http';
import { HttpClientModule, HttpClient, HttpHeaders} from '#angular/common/http';
import { Location, LocationStrategy, PathLocationStrategy } from '#angular/common';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
import {
toDataSourceRequestString,
translateDataSourceResultGroups,
translateAggregateResults,
DataResult,
DataSourceRequestState
} from '#progress/kendo-data-query';
import 'rxjs/add/operator/map';
import { GridDataResult, DataStateChangeEvent } from '#progress/kendo-angular-grid';
#Injectable()
export class Vtos {
// private vtoUrl = location.href.replace(location.hash, '') + '/home/GetVtos';
private vtoUrl = 'http://localhost:63213/Home/GetVtos';
constructor(private http: Http) { }
public getVtos(state: DataSourceRequestState): Observable<DataResult> {
const queryStr = `${toDataSourceRequestString(state)}`; //serialize the state
const hasGroups = state.group && state.group.length;
return this.http
.get(`${this.vtoUrl}?${queryStr}`) //send the state to the server
.map(response => response.json())
.map(({ data, total/*, aggregateResults*/ }) => // process the response
(<GridDataResult>{
//if there are groups convert them to compatible format
data: hasGroups ? translateDataSourceResultGroups(data) : data,
total: total,
// convert the aggregates if such exists
//aggregateResult: translateAggregateResults(aggregateResults)
}))
}
}
HomeController call to GetVots
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using VTO.DTO;
using VTO.DAL;
using Kendo.Mvc.UI;
using Kendo.Mvc.Extensions;
namespace VTO.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpGet]
public JsonResult GetVtos([DataSourceRequest]DataSourceRequest request)
{
return new JsonResult
{
ContentType = "application/json",
Data = Vto.GetVtos().ToDataSourceResult(request),
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
MaxJsonLength = int.MaxValue
};
}
}
A couple of observations here, this module is deprecated. See details here. Remove it from your app.
import { Http, HttpModule, Headers, Response } from '#angular/http';
You should use HttpClientModule,
import { HttpClient, HttpHeaders} from '#angular/common/http';
Keep it mind you have to import HttpClientModule on your app.module.ts (or any other module you have a dependency for it)
import { HttpClientModule } from '#angular/common/http';
Since HttpClientModule came into play. You not longer need for response.json(). Now HttpClient.get() returns an Observable of typed HttpResponse rather than just the JSON data. See docs. (vto.service.ts)
Remove,
.map(response => response.json())
Then you have,
constructor(private http: HttpClient) { }
public getVtos(state: DataSourceRequestState): Observable<DataResult> {
...
return this.http
.get(`${this.vtoUrl}?${queryStr}`)
.map(({ data, total/*, aggregateResults*/ }) =>
(<GridDataResult>{
data: hasGroups ? translateDataSourceResultGroups(data) : data,
total: total,
translateAggregateResults(aggregateResults)
}))
}
Sharing what worked for me. As Luillyfe mentioned Http is now deprecated, HttpClient is to be used. The returned response is already in Json, so no longer need to use .Json method.
constructor(private http: HttpClient) { }
public getVtos(state: DataSourceRequestState): Observable<DataResult> {
const queryStr = `${toDataSourceRequestString(state)}`; //serialize the state
const hasGroups = state.group && state.group.length;
return this.http
.get(`${this.vtoUrl}?${queryStr}`) //send the state to the server
.pipe(
map(<DataResult>({ Data, Total/*, aggregateResults*/ }) => {// process the response
console.log(Data);
return (<GridDataResult>{
data: hasGroups ? translateDataSourceResultGroups(Data) : Data.map(item => {
item.ReportDate = new Date(item.ReportDate); // convert to actual JavaScript date object
return item;
}),
total: Total
})
})
)
}
problem
Cannot Access Data from Symfony to Angular2 Component.
what i tried
Angular2 snippet
dashboard.component.ts
import { Component, OnInit } from '#angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
#Component({
selector: 'app-dashboard',
moduleId: module.id,
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
constructor(public http: Http) {}
ngOnInit() {
}
public persons: any;
data() {
this.http.get('http://localhost/api')
.map((res: Response) => res.json())
.subscribe((res: any) => {
this.persons = res;
});
}
}
dashboard.component.html
<a *ngFor="let person of persons" [routerLink]="['/detail', hero.id]" class="col-1-4">
<div class="module hero">
<h4>{{person.name}}</h4>
</div>
</a>
</div>
json data
{
id: "592413bc93d2992800000029",
name: "A Foo Bar",
price: 19.99
},
{
id: "5924145b93d299a812000029",
name: "A Foo Bar",
price: 19.99
}
we have backend as symfony2 we are just passing json into component.
when i tried to print {{persons}} in dashboard.component.html file it doesn't print something.
can anyone suggest what im missing while rendering data from rest api.
any suggestion is most welcome.
i have found problem no Http Request were Triggered in Browser when data() function triggered by angular2 component.
you need to call the data method. Otherwise, http request will not trigger.
constructor(public http: Http) {
this.data();
}
i have fixed the problem
they are 2 issue in in_memory_api or fake-bakend was main problem
app.module.ts
imports:
[
InMemoryWebApiModule.forRoot(InMemoryDataService)
]
instead try with:
imports:
[
InMemoryWebApiModule.forRoot(InMemoryDataService, {passThruUnknownUrl: true}), // fake in memory API simulation
]
if you use fake provider
providers: [
fakeBackendProvider,//comment this,if you use this xhr request would go 404
]
service.ts
private headers = new Headers({'Content-Type': 'application/json','Access-Control-Allow-Origin':'*'});
private heroesUrl = 'http://localhost/api';
getApiData(): Promise<ApiDashboard[]> {
return this.http.get(this.heroesUrl)
.toPromise()
.then(response => response.json() as ApiDashboard[])
.catch(this.handleError);
}
try these step im sure this will works if http request gets 404.
Also it seems your router link is wrong in the HTML. SHouldn't it be
[routerLink]="['/detail', person.id]" ?
Is there a way to pass arguments rendered on the backend to angular2 bootstrap method? I want to set http header for all requests using BaseRequestOptions with value provided from the backend. My main.ts file looks like this:
import { bootstrap } from '#angular/platform-browser-dynamic';
import { AppComponent } from "./app.component.ts";
bootstrap(AppComponent);
I found how to pass this arguments to root component (https://stackoverflow.com/a/35553650/3455681), but i need it when I'm fireing bootstrap method... Any ideas?
edit:
webpack.config.js content:
module.exports = {
entry: {
app: "./Scripts/app/main.ts"
},
output: {
filename: "./Scripts/build/[name].js"
},
resolve: {
extensions: ["", ".ts", ".js"]
},
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts-loader'
}
]
}
};
update2
Plunker example
update AoT
To work with AoT the factory closure needs to be moved out
function loadContext(context: ContextService) {
return () => context.load();
}
#NgModule({
...
providers: [ ..., ContextService, { provide: APP_INITIALIZER, useFactory: loadContext, deps: [ContextService], multi: true } ],
See also https://github.com/angular/angular/issues/11262
update an RC.6 and 2.0.0 final example
function configServiceFactory (config: ConfigService) {
return () => config.load();
}
#NgModule({
declarations: [AppComponent],
imports: [BrowserModule,
routes,
FormsModule,
HttpModule],
providers: [AuthService,
Title,
appRoutingProviders,
ConfigService,
{ provide: APP_INITIALIZER,
useFactory: configServiceFactory
deps: [ConfigService],
multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
If there is no need to wait for the initialization to complete, the constructor of `class AppModule {} can also be used:
class AppModule {
constructor(/*inject required dependencies */) {...}
}
hint (cyclic dependency)
For example injecting the router can cause cyclic dependencies.
To work around, inject the Injector and get the dependency by
this.myDep = injector.get(MyDependency);
instead of injecting MyDependency directly like:
#Injectable()
export class ConfigService {
private router:Router;
constructor(/*private router:Router*/ injector:Injector) {
setTimeout(() => this.router = injector.get(Router));
}
}
update
This should work the same in RC.5 but instead add the provider to providers: [...] of the root module instead of bootstrap(...)
(not tested myself yet).
update
An interesting approach to do it entirely inside Angular is explained here https://github.com/angular/angular/issues/9047#issuecomment-224075188
You can use APP_INITIALIZER which will execute a function when the
app is initialized and delay what it provides if the function returns
a promise. This means the app can be initializing without quite so
much latency and you can also use the existing services and framework
features.
As an example, suppose you have a multi-tenanted solution where the
site info relies on the domain name it's being served from. This can
be [name].letterpress.com or a custom domain which is matched on the
full hostname. We can hide the fact that this is behind a promise by
using APP_INITIALIZER.
In bootstrap:
{provide: APP_INITIALIZER, useFactory: (sites:SitesService) => () => sites.load(), deps:[SitesService, HTTP_PROVIDERS], multi: true}),
sites.service.ts:
#Injectable()
export class SitesService {
public current:Site;
constructor(private http:Http, private config:Config) { }
load():Promise<Site> {
var url:string;
var pos = location.hostname.lastIndexOf(this.config.rootDomain);
var url = (pos === -1)
? this.config.apiEndpoint + '/sites?host=' + location.hostname
: this.config.apiEndpoint + '/sites/' + location.hostname.substr(0, pos);
var promise = this.http.get(url).map(res => res.json()).toPromise();
promise.then(site => this.current = site);
return promise;
}
NOTE: config is just a custom config class. rootDomain would be
'.letterpress.com' for this example and would allow things like
aptaincodeman.letterpress.com.
Any components and other services can now have Site injected into
them and use the .current property which will be a concrete
populated object with no need to wait on any promise within the app.
This approach seemed to cut the startup latency which was otherwise
quite noticeable if you were waiting for the large Angular bundle to
load and then another http request before the bootstrap even began.
original
You can pass it using Angulars dependency injection:
var headers = ... // get the headers from the server
bootstrap(AppComponent, [{provide: 'headers', useValue: headers})]);
class SomeComponentOrService {
constructor(#Inject('headers') private headers) {}
}
or provide prepared BaseRequestOptions directly like
class MyRequestOptions extends BaseRequestOptions {
constructor (private headers) {
super();
}
}
var values = ... // get the headers from the server
var headers = new MyRequestOptions(values);
bootstrap(AppComponent, [{provide: BaseRequestOptions, useValue: headers})]);
In Angular2 final release, the APP_INITIALIZER provider can be used to achieve what you want.
I wrote a Gist with a complete example: https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9
The gist example is reading from JSON files but can be easily changed to read from a REST endpoint.
What you need, is basically:
a) Set up APP_INITIALIZER in your existent module file:
import { APP_INITIALIZER } from '#angular/core';
import { BackendRequestClass } from './backend.request';
import { HttpModule } from '#angular/http';
...
#NgModule({
imports: [
...
HttpModule
],
...
providers: [
...
...
BackendRequestClass,
{ provide: APP_INITIALIZER, useFactory: (config: BackendRequestClass) => () => config.load(), deps: [BackendRequestClass], multi: true }
],
...
});
These lines will call the load() method from BackendRequestClass class before your application is started.
Make sure you set "HttpModule" in "imports" section if you want to make http calls to the backend using angular2 built in library.
b) Create a class and name the file "backend.request.ts":
import { Inject, Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class BackendRequestClass {
private result: Object = null;
constructor(private http: Http) {
}
public getResult() {
return this.result;
}
public load() {
return new Promise((resolve, reject) => {
this.http.get('http://address/of/your/backend/endpoint').map( res => res.json() ).catch((error: any):any => {
reject(false);
return Observable.throw(error.json().error || 'Server error');
}).subscribe( (callResult) => {
this.result = callResult;
resolve(true);
});
});
}
}
c) To read the contents of the backend call, you just need to inject the BackendRequestClass into any class of you choice and call getResult(). Example:
import { BackendRequestClass } from './backend.request';
export class AnyClass {
constructor(private backendRequest: BackendRequestClass) {
// note that BackendRequestClass is injected into a private property of AnyClass
}
anyMethod() {
this.backendRequest.getResult(); // This should return the data you want
}
}
Let me know if this solves your problem.
Instead of having your entry point calling bootstrap itself, you could create and export a function that does the work:
export function doBootstrap(data: any) {
platformBrowserDynamic([{provide: Params, useValue: new Params(data)}])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
}
You could also place this function on the global object, depending on your setup (webpack/SystemJS). It also is AOT-compatible.
This has the added benefit to delay the bootstrap, whenit makes sense. For instance, when you retrieve this user data as an AJAX call after the user fills out a form. Just call the exported bootstrap function with this data.
The only way to do that is to provide these values when defining your providers:
bootstrap(AppComponent, [
provide(RequestOptions, { useFactory: () => {
return new CustomRequestOptions(/* parameters here */);
});
]);
Then you can use these parameters in your CustomRequestOptions class:
export class AppRequestOptions extends BaseRequestOptions {
constructor(parameters) {
this.parameters = parameters;
}
}
If you get these parameters from an AJAX request, you need to bootstrap asynchronously this way:
var appProviders = [ HTTP_PROVIDERS ]
var app = platform(BROWSER_PROVIDERS)
.application([BROWSER_APP_PROVIDERS, appProviders]);
var http = app.injector.get(Http);
http.get('http://.../some path').flatMap((parameters) => {
return app.bootstrap(appComponentType, [
provide(RequestOptions, { useFactory: () => {
return new CustomRequestOptions(/* parameters here */);
}})
]);
}).toPromise();
See this question:
angular2 bootstrap with data from ajax call(s)
Edit
Since you have your data in the HTML you could use the following.
You can import a function and call it with parameters.
Here is a sample of the main module that bootstraps your application:
import {bootstrap} from '...';
import {provide} from '...';
import {AppComponent} from '...';
export function main(params) {
bootstrap(AppComponent, [
provide(RequestOptions, { useFactory: () => {
return new CustomRequestOptions(params);
});
]);
}
Then you can import it from your HTML main page like this:
<script>
var params = {"token": "#User.Token", "xxx": "#User.Yyy"};
System.import('app/main').then((module) => {
module.main(params);
});
</script>
See this question: Pass Constant Values to Angular from _layout.cshtml.
I'm struggling to do a http get request with Angular 2. I've made a file with the JSON information that I want to "get" with my TeacherInfo class and use it to display information by the account component which is used in a routing.
If I click in the routerLink for this element nothing is displayed and if I switch to another routerLink there is neither ( there was before, all routerLinks worked just fine )
file: TeacherInfo.service.ts
import {Injectable, OnInit} from '#angular/core';
import { Http, Response , Headers} from '#angular/http';
import { account } from '../components/account.component';
import {Observable} from "rxjs";
#Injectable()
export class TeacherInfo {
constructor ( private http : Http) {}
private url = '../test.json';
getInfo(){
return this.http.get(this.url)
.toPromise()
.then(response => response.json().data as account );
}
}
file: account.component.ts
import {Component, OnInit} from '#angular/core';
import { TeacherInfo } from '../services/TecherInfo.service';
#Component({
template:`
<h2>This is not ready jet!</h2>
<p>
Willkommen {{name}}! <br/>
E-mail: {{email}}<br/>
</p>
`
})
export class account implements OnInit{
public id : number;
public name : string;
public email: string;
private acc : account;
constructor(private accountinfoservice : TeacherInfo) {
}
getInfo() {
this.accountinfoservice.getInfo()
.then(( info : account ) => this.acc = info );
}
ngOnInit () {
this.getInfo();
if ( this.acc != null ) {
this.id = this.acc.id;
this.name = this.acc.name;
this.email = this.acc.email;
}else {
console.log("there is no data! ");
}
}
and finally test.json :
{
"id" : "1",
"name": "testname",
"email": "testemail"
}
I'm using the latest versions of node and npm and I get no compilation errors and just unrelated errors in the browser console ( other SPA's parts which aren't ready yet). The observable implementations are there because at first I tried to do it that way and came to the conclusion it's easier at first to use a promise.
I subscribe for simple json gets
Calling code
ngOnInit(): void {
this._officerService.getOfficers()
.subscribe(officers => this.officers = officers),
error => this.errorMessage = <any> error;
}
And service code
import { Injectable } from 'angular2/core';
import { Http, Response } from 'angular2/http';
import { Observable } from 'rxjs/Observable';
import { Officer } from '../shared/officer';
#Injectable()
export class OfficerService{
private _officerUrl = 'api/officers.json';
constructor(private _http: Http){ }
getOfficers() : Observable<Officer[]>{
return this._http.get(this._officerUrl)
.map((response: Response) => <Officer[]>response.json())
.catch(this.handleError);
}
private handleError(error: Response){
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
That is returning the data as an array and casting it to the correct type though you can also use any and return [0] if you just expect one.
Hope that helps