I am brand new in Angular2 framework and I have a problem with http get request. I want to display all registered users on my homepage.
This is my code:
home.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class UserService {
constructor (
private http: Http
) {}
getUser() {
return this.http.get(`/app/home/home.php`)
.map((res:Response) => res.json());
}
}
home.component.ts:
// Imports
import { Component, OnInit } from '#angular/core';
import { UserService } from './home.service';
#Component({
templateUrl: './home.component.html'
})
// Component class implementing OnInit
export class HomeComponent{
// Private property for binding
constructor(private userService: UserService) {}
profile = {};
loadUser() {
this.userService.getUser().subscribe(data => this.profile = data);
}
}
home.php
<?php
$connection = new mysqli("127.0.0.1", "root", "", "flatmate");
if($connection->connect_error){
echo $connection->connect_error;
}
/* change character set to utf8 */
if (!$connection->set_charset("utf8")) {
echo $connection->error;
exit();
}
$getUser = 'SELECT * from users';
$result = mysqli_query($connection, $getUser);
$data = array();
while ($row = mysqli_fetch_array($result)) {
$data[] = $row;
}
echo json_encode($data);
home.component.html:
<div>
<button (click)="loadUser()">Load profile</button>
{{ profile | json }}
</div>
In my app.module.ts I added UserService provider. I prepared my code based on this tutorial. When I pass into get method URL from this example, code works properly. But if I change link to my home.php I get the error:
"errors.ts:42 ERROR SyntaxError: Unexpected token < in JSON at position 0"
Related
I have a problem with angular, the problem is the following, the method to obtain an http client, does not get any value from an api in php that is listening on the localhost port: 123. The php code would be the follow
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
//Get todos los usuarios
$app=new \Slim\App;
//Get todos los usuarios
$app->get('/api/clientes', function(Request $request,Response $response){
include "../src/db.php";
$query="select * from users";
$result = mysqli_query($con,$query);
if (!$result) {
die("Query fail");
}else {
$row_cate = mysqli_num_rows($result);
if($row_cate == null){
echo "no existen datos en la dbo";
}
else{
$users=array();
$cont=0;
while ($row = mysqli_fetch_array($result)){
$producto[$cont]=array(
"id" => $row['id'],
"name" => $row['name'],
"email" => $row['email']
);
$cont++;
}
echo json_encode($producto);
}
}
});
and the angular client method is as follows.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { User } from './user.model';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class DataService {
constructor(private http:HttpClient) { }
get(){
const url="http://localhost:1234/public/api/clientes";
return new Promise(
resolve=>{
this.http.get(url)
.subscribe(
data=>resolve(data)
)
}
);
}
}
I would appreciate your help.
Your code in get() method is invalid. Here is a better code if you want it to return Promise.
get(): Promise<any> {
const url="http://localhost:1234/public/api/clientes";
return this.http.get(url).toPromise();
}
Also, make sure your server responds with the data you expect
If you are just getting started with Angular, then my advice to you is to learn RxJS and use Observables instead of Promises with Angular.
I already managed to solve the error, the truth was a beginner error, the problem was in the api, I had not put the headers that allowed to perform the procedures "get", "post", etc in php
I have 2 different apps, one is based on Angular2 and the other with PHP.
I need to call a service's function of the first one (Angular) from the second one (PHP). Is it possible even if it is weird and ugly?
I know how to call a PHP file inside an Angular app, but not the other way.
Here are my test files:
webService.component.ts
import { Component } from '#angular/core';
import {Observable} from "rxjs/Observable";
import {ActivatedRoute, Params} from "#angular/router";
import {WebService} from "./webService.service";
#Component({
selector: 'webService-element',
template: `{{result}}`
})
export class WebServiceComponent {
webService: WebService;
result: any;
constructor(private route: ActivatedRoute, webService: WebService) {
this.webService = webService;
}
ngOnInit() {
this.route.params.subscribe((params: Params) => {
let aId = params['aId'];
let bId = params['bId'];
this.getPif(aId, bId);
});
}
getPif(aId: any, bId: any): any {
this.result = this.webService.getPif();
return this.webService.getPif();
}
}
webService.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class WebService {
constructor(private http: Http) {}
//getPif(): Observable<any> {
getPif(): any {
console.log("rraaahhhhh");
return "pif";
}
}
webService.routing.ts
import { Routes, RouterModule } from "#angular/router";
import {WebServiceComponent} from "./webService.component";
const WEBSERVICE_ROUTES: Routes = [
{ path: '', component: WebServiceComponent },
{ path: 'getTest/:aId/:bId', component: WebServiceComponent },
];
export const webServiceRouting = RouterModule.forChild(WEBSERVICE_ROUTES);
webService.module.ts
import { NgModule } from "#angular/core";
import {WebService} from "./webService.service";
import {WebServiceComponent} from "./webService.component";
import {webServiceRouting} from "./webService.routing";
#NgModule({
imports: [
webServiceRouting
],
declarations: [
WebServiceComponent
],
providers: [
WebService
]
})
export class WebServiceModule { }
I tried to launch "http://localhost:3000/webService/getTest/1/2"
Thanks a lot for your help!
Please read the question in detail since it is a long one with various edits and extended updates as per the request of other users.
I am trying to send data to php file using angular2. I am doing the angular project in Unix and the var/www/html/ is the php Xampp folder locations for running the php files.
My folder structure is like this:-
var/www/html/
|_(angproject)
|_(phpscript)
| |_login.php
|_(src)
|_(app)
|_(admin)
| |_(login)
| | |_login.component.ts
| |
| |_admin.component.ts
|
|_(_admin_service)
| |_admin.login.ts
|
|_(_animations)
|
|_(front)
|
|_(_models)
| |_admin.model.ts
|
|_app.module.ts
My app.module.ts file is like this:-
import { HttpModule, Http, Response, Headers, RequestOptions} from '#angular/http';
import { HttpClientModule } from '#angular/common/http';
import { BrowserModule } from '#angular/platform-browser';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { FormsModule } from '#angular/forms';
import { APP_BASE_HREF } from '#angular/common';
import { CanActivate } from "#angular/router";
import { AppComponent } from './app.component';
import { FrontComponent } from './front/front.component';
import { AdminComponent } from './admin/admin.component';
import { LoginComponent } from './admin/login/login.component';
import { DashboardComponent } from './admin/dashboard/dashboard.component';
import { HeaderComponent } from './admin/header/header.component';
import { FooterComponent } from './admin/footer/footer.component';
import { LeftmenuComponent } from './admin/leftmenu/leftmenu.component';
import { NavbarComponent } from './admin/navbar/navbar.component';
import { ShortcutbarComponent } from './admin/shortcutbar/shortcutbar.component';
import { AdminLoginService } from './_admin_service/admin.login';
const appRoutes: Routes = [
{ path: 'admin',
component: AdminComponent,
children: [
{ path: '', component: LoginComponent},
{ path: 'dashboard', component: DashboardComponent}
]
}
];
#NgModule({
declarations: [
AppComponent,
FrontComponent,
AdminComponent,
LoginComponent,
DashboardComponent,
HeaderComponent,
FooterComponent,
LeftmenuComponent,
NavbarComponent,
ShortcutbarComponent
],
imports: [
HttpModule,
HttpClientModule,
BrowserModule,
BrowserAnimationsModule,
FormsModule,
RouterModule.forRoot(
appRoutes,
{ enableTracing: true } // <-- debugging purposes only
)
],
providers: [{provide: APP_BASE_HREF, useValue : '/' },AdminLoginService],
bootstrap: [AppComponent]
})
export class AppModule { }
My login.component.ts file is this:-
import { Component, OnInit, Input } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { fadeInAnimation } from '../../_animations/index';
import { Admin } from '../../_models/admin.model';
import { AdminLoginService } from '../../_admin_service/admin.login';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
animations: [fadeInAnimation],
host: { '[#fadeInAnimation]': '' },
providers: [AdminLoginService]
})
export class LoginComponent implements OnInit {
loading = false;
returnUrl: string;
responseStatus:Object= [];
status:boolean ;
//#Input() admin:Admin;
model = new Admin('', '', '', 'Emailsss','Passwordsss');
constructor(
private route: ActivatedRoute,
private router: Router,
private _adminLogin: AdminLoginService
){}
submitPost()
{
//console.log("submit Post click happend " + this.model.email)
//console.log(this.model);
this._adminLogin.postLogin(this.model).subscribe(
data => console.log(this.responseStatus = data),
err => console.log(err),
() => console.log('Request Completed')
);
this.status = true;
}
ngOnInit() {
}
}
The service file admin.login.ts file is this:-
import { Http, Response, Headers, RequestOptions} from '#angular/http';
import { HttpHeaders } from '#angular/common/http';
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
import { Admin } from '../_models/admin.model';
#Injectable()
export class AdminLoginService {
http : Http;
actionUrl : string;
admin_login_Url: string;
postData: Admin;
constructor(public _http: Http) {
this.http = _http;
this.admin_login_Url = 'http://localhost/angproject/phpscript/login.php';
}
postLogin(postData:Admin) {
let headers = new Headers();
headers.append("Access-Control-Allow-Origin","*");
headers.append("Access-Control-Allow-Methods","GET, POST");
headers.append("Content-Type","application/json");
let options = new RequestOptions({ headers: headers });
console.log(postData);
this.actionUrl = this.admin_login_Url;
return this.http.post(this.actionUrl, {postData}, options)
.map(res => res.json());
}
}
And ultimately, my login.php file is this:-
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST");
header("Access-Control-Allow-Headers: *");
header("Content-Type: application/json; charset=utf-8");
include('connection.php');
$rawData = file_get_contents("php://input");
$data = json_decode($rawData, true);
$error = array();
if(isset($data['postData']['email']) && !empty($data['postData']['email']))
$email = $data['postData']['email'];
else
$error[] = "Email was not entered";
if(isset($data['postData']['password']) && !empty($data['postData']['password']))
$password = $data['postData']['password'];
else
$error[] = "Password was not entered";
if(empty($error))
{
$runQuery = "SELECT * FROM users WHERE email = '$email' AND password = '$password'";
$result = $conn->query($runQuery);
if ($result->num_rows > 0)
{
$response['status'] = 1;
$response['message'] = "Login successfully";
$response['error'] = 0;
}
else
{
$response['status'] = 0;
$response['message'] = "An error occured while logging in";
$response['error'] = $conn->error;
}
}
else
{
$response['status'] = 0;
$response['message'] = "Parameter missing";
$response['error'] = $error;
}
$respond = json_encode($response);
echo $respond;
exit;
?>
Now here is the problem. When testing in Chrome, clicking the submit button once is firing calling for the php script (Ajax) twice. The first call doesn't send any data and hence it shows validation message in return response. The second call sends the form's data and hence fetching the desired result by matching the data sent.
In case of firefox, I am getting this response:-
How can I solve it?
Note:
Here are the Request Headers of the "first call" in chrome:
Here are the Request Headers of the "second call" in chrome:
EDIT: Dated- 04-04-2018:
As per the suggestions from David, I made subsequent changes in my login.php file:
header("Access-Control-Allow-Origin: http://localhost:4200");
header("Access-Control-Allow-Headers: application/x-www-form-urlencoded");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
In the admin.login.ts file, I made the following changes:-
postLogin(postData:Admin) {
let headers = new Headers();
headers.append("Accept", "q=0.8;application/json;q=0.9");
this.actionUrl = this.admin_login_Url;
return this.http.post(this.actionUrl, {postData}, { headers: headers })
.map(res => res.json()).share();
}
}
Question 1:
For the above code, I took reference from this site. Yet, I am still getting the error. How can I fix this in Firefox?
In the above code, I added the line headers.append("Accept", "q=0.8;application/json;q=0.9"); to overcome an error shown below:-
How can I overcome this problem?
Question 2:
In Firefox, the console reads:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost/angproject/phpscript/register.php. (Reason: invalid token ‘application/x-www-form-urlencoded’ in CORS header ‘Access-Control-Allow-Headers’).
When checking in Chrome, I can see two calls in the network tab in console. The first call's request type is OPTIONS, while the second call has request type as POST.
In case of Firefox, I am getting only one call with request type OPTIONS. The POST request isn't taking place at all.
How can I fix this?
Having 2 requests is normal, due to CORS implementation (client on localhost:4200, server on localhost = different ports).
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Your problem is that you specified some CORS headers in your request
headers.append("Access-Control-Allow-Methods","GET, POST");
headers.append("Access-Control-Allow-Origin","*");
These headers are meant to be added server side, you don't need to send them from angular. They are actually causing the problem.
Remove them and it should work.
More information about Access-Control-Allow-Headers
Access-Control-Allow-Headers normally accepts a list of comma-separated headers.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
The wildcard (*) value that you have on your PHP code is not supported on all browsers. On Firefox, the wildcard is not implemented yet
https://bugzilla.mozilla.org/show_bug.cgi?id=1309358
That's why firefox is bloking your POST, since the preflight (OPTIONS) request kind of fails
You don't need to specify Content-Type in your list since it's accepted by default
The simple headers, Accept, Accept-Language, Content-Language, Content-Type (but only with a MIME type of its parsed value (ignoring parameters) of either application/x-www-form-urlencoded, multipart/form-data, or text/plain), are always available and don't need to be listed by this header.
Edit: actually, you do need to specify content-type in the list of accepted headers since you are sending application/json, which is not in the above list
I got the same issue while running the angular app on chrome browser in windows. Even I setup CORS configuration in a backend that issue didn't resolve. Then I realize that I need to disable the web security while running on a local machine or
localhost/portNumber
Just hit the following command on cmd
TASKKILL /F /IM chrome.exe
start chrome.exe --args --disable-web-security –-allow-file-access-from-files
This resolved my problem :)
EDIT 1:
Thank you for #Narm for the solution. I got it working!
In let myTemplate = '<div class="test" (tap)="test();">Test</div>';, I have a tap function.
When I click on it to invoke the function, it does not work and gives an error:
ERROR TypeError: _co.another_test is not a function
Here is what I have so far:
ngOnInit(){
...
let myTemplate = `<div class="test" (tap)="test();">Test</div>`;
...
}
test(){
console.log("Test");
}
Any thoughts?
Original Question Below
From php using REST, I am getting html markup with Angular components:
From php:
function send_html(){
$html = '<div class="test" *ngIf="data">This is an example</div>';
return $html;
};
Then in my angular project, I am trying to add this html dynamically using componentFactoryResolver: (I understand that it only accepts Angular component)
Here is my though process:
In main.ts (shown below): call the getDynamicREST() and get the $html from php.
When the data is fetched, then send this to my_component.ts to make this as an Angular component.
Once the html markup becomes a part of Angular component, then use createComponent to create the component.
Of course, it doesn't work...
This is what I have so far: Please feel free to tear it apart.
main.html
<div class="top">
<ng-template #main></ng-template>
</div>
main.ts
import { Component, ViewChild, ComponentFactoryResolver, ViewContainerRef } from '#angular/core';
import { my_component } from './my_component';
#Component({
selector: 'page-main_page',
templateUrl: 'main_page.html'
})
export class main_page {
#ViewChild('main', { read: ViewContainerRef }) entry: ViewContainerRef;
data: any;
constructor(public resolver: ComponentFactoryResolver,
public mine: my_component ){
};
ngOnInit(){
this.getDynamicREST().then((res)=>{
this.mine.data = res;
const factory = this.resolver.resolveComponentFactory(my_component);
this.entry.createComponent(factory);
})
};
}
my_component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my_component ',
template: '<div class="my_component">{{data}}</div>'
})
export class my_component {
data: any;
}
How would I achieve this so that I can fetch angular components dynamically and display them?
Any help will be much appreciated.
Thank you.
You're on the right path. I ran into this very same scenario just a few months ago, here is the solution to accomplish your goals.
Here I attached a StackBlitz if you want to see the code running live and have a chance to play with it. Dynamic components can be a challenge to understand at first.
Live Demo
app.component.ts
This component acts as the container to the dynamic component and is where it will be created.
import {
Component, ViewChild, OnInit, OnDestroy,
AfterViewInit, ComponentFactoryResolver,
Input, Compiler, ViewContainerRef, NgModule,
NgModuleRef, Injector
} from '#angular/core';
import { CommonModule } from '#angular/common';
import { BrowserModule } from '#angular/platform-browser';
#Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
#ViewChild('vc', { read: ViewContainerRef }) _container: ViewContainerRef;
private cmpRef;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private compiler: Compiler,
private _injector: Injector,
private _m: NgModuleRef<any>) { }
ngOnInit() { }
ngAfterViewInit() {
let myTemplate = `<h2> Generated on the fly</h2>
<p>You can dynamically create your component template!</p>`
#Component({
template: myTemplate
})
class dynamicComponent {
constructor(){}
}
#NgModule({
imports: [
BrowserModule
],
declarations: [dynamicComponent]
})class dynamicModule {};
const mod = this.compiler.compileModuleAndAllComponentsSync(dynamicModule);
const factory = mod.componentFactories.find((comp) =>
comp.componentType === dynamicComponent
);
this.cmpRef = this._container.createComponent(factory);
}
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
}
Then in the template for your app component
app.component.html
<ng-template #vc></ng-template>
Then in your app.module you need to import the compiler and declare it as a provider:
app.module.ts
import {Compiler, COMPILER_OPTIONS, CompilerFactory} from '#angular/core';
import {JitCompilerFactory} from '#angular/platform-browser-dynamic';
export function createCompiler(compilerFactory: CompilerFactory) {
return compilerFactory.createCompiler();
}
#NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
RouterModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
routing
],
providers: [
{provide: COMPILER_OPTIONS, useValue: {}, multi: true},
{provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
{provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory]}
],
bootstrap: [AppComponent]
})
export class AppModule {}
*** Edit for 'tap' question
What you want to do is bind to the (click) event, I am not aware of any (tap) events and there are none listed in the Events reference by MDN Events Reference MDN
In your dynamic component add the following:
let myTemplate = `<h2> Generated on the fly</h2>
<p>You can dynamically create your component template!</p>
<div class="test" (click)="test();">Test</div>`
#Component({
template: myTemplate
})
class dynamicComponent {
constructor(){}
public test(){
alert("I've been clicked!");
}
}
Note I updated the StackBlitz if you want to see it in action!
I have an error in this Component and that error is not caught by command prompt.
Dashboard.component.ts
import { Component, OnInit } from '#angular/core';
import { User } from './user.component';
import { UserService } from './user.service';
#Component({
selector : 'dashboard',
templateUrl : './views/dashboard-component.html',
styleUrls: ['./views/css/dashboard-component.css'],
providers: [UserService]
})
export class DashboardComponent {
users: User[] = [];
constructor(private userservice: UserService){}
ngOnInit() : void{
this.userservice.getusers().then(users => this.users = users.slice(1,5) );
}
}
I can't understand what the problem is because i have defined "users" in the class.
The method is called from this service.
user.service.ts
import { Injectable } from '#angular/core';
import { User } from './user.component';
import { USERS } from './mock-users';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class UserService {
private usersUrl = "http://localhost/user/src/app/userlist.php";
constructor(private http: Http) { }
getusers() : Promise<User[]>{
return this.http.get(this.usersUrl)
.toPromise()
.then(response => response.json().data as User[])
.catch(this.handleError);
}
getuser(id: number): Promise<User> {
const url = '${this.usersUrl}/${id}';
return this.http.get(url)
.toPromise()
.then(response => response.json().data as User)
.catch(this.handleError);
}
private handleError(error : any): Promise<any> {
console.error('an error occured', error);
return Promise.reject(error.message || error);
}
}
The data I am getting is :
[{"id":"1","fname":"Vishwas","lname":"Jadav","email":"vjadav#live.com","dpic":"2017-10-7--09-12-19.jpeg","phone":"7621823474","passw":"illj123","type":"2"}]
You don't have a data property in your response, as we can see you are simply getting an array, so you should return that. So in your service method getusers, instead of...
.then(response => response.json().data as User[])
do:
.then(response => response.json() as User[])
Sidenote, you might have the same problem with your getuser (?) just for future reference if you run into same problem there :)
Try assigning to array and then remove
ngOnInit() : void{
this.userservice.getusers().then(users => this.users = users;
this.users =this.users.slice(1,5);
});