angular2 http requist handling chunke of responses - php

While I'm experimenting with angular2 a small obstacle came up:
I have php code witch returns chunks of responses using "ob_flush".
In the front end I successfully made "xhr=XMLHttpRequest" requests and received the responses and handle it using "xhr.onprogress()" and "xhr.onreadystatechange()".
Now when I tried to get the same functionality using angular2 http.get(), I couldn't output the results as they arrive from the server! instead the results are shown by angular at the end of the process after receiving the last response.
I think the rxjs Observer object is buffering the responses!.
So how can I change this behavior?
here is my php code, testing.php:
echo date('H:i:s')." Loading data!";
ob_flush();
flush();
sleep(5);
echo "Ready to run!";
here is my angular2 code:
template: `
<div>
<h3>experimenting!</h3>
<button (click)="callServer()">run the test</button>
<div>the server says: {{msg}}</div>
</div>`
export class AppComponent {
msg:any;
constructor (private http:Http){}
callServer(){
this.http.get("localhost/testing.php")
.subscribe(res=> this.msg= res.text());
}
}
When I run this code it shows after 5 seconds:
(19:59:47 Loading data!Ready to run!).
It should instantly output: (19:59:47 Loading data!).
Then after 5 seconds replaces the previous message with:(Ready to run!)

You need to extend the BrowserXhr class to do that in order to configure the low level XHR object used:
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor(private service:ProgressService) {}
build(): any {
let xhr = super.build();
xhr.onprogress = (event) => {
service.progressEventObservable.next(event);
};
return <any>(xhr);
}
}
and override the BrowserXhr provider with the extended:
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
See this question for more details:
Angular 2 HTTP Progress bar

After studying rxjs and reading Angular2 source code, I came up with this solution
I found it's better to make custom_backend, I think this is the recommended approach by angular Dev team.
my_backend.ts
import {Injectable} from "angular2/core";
import {Observable} from "rxjs/Observable";
import {Observer} from "rxjs/Observer";
import {Connection,ConnectionBackend} from "angular2/src/http/interfaces";
import {ReadyState, RequestMethod, ResponseType} from "angular2/src/http/enums";
import {ResponseOptions} from "angular2/src/http/base_response_options";
import {Request} from "angular2/src/http/static_request";
import {Response} from "angular2/src/http/static_response";
import {BrowserXhr} from "angular2/src/http/backends/browser_xhr";
import {Headers} from 'angular2/src/http/headers';
import {isPresent} from 'angular2/src/facade/lang';
import {getResponseURL, isSuccess} from "angular2/src/http/http_utils"
export class MyConnection implements Connection {
readyState: ReadyState;
request: Request;
response: Observable<Response>;
constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
this.request = req;
this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
let _xhr: XMLHttpRequest = browserXHR.build();
_xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
// save the responses in array
var buffer :string[] = [];
// load event handler
let onLoad = () => {
let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;
//_xhr.respons 1 = "Loading data!"
//_xhr.respons 2 = "Loading data!Ready To Receive Orders."
// we need to fix this proble
// check if the current response text contains the previous then subtract
// NOTE: I think there is better approach to solve this problem.
buffer.push(body);
if(buffer.length>1){
body = buffer[buffer.length-1].replace(buffer[buffer.length-2],'');
}
let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
let url = getResponseURL(_xhr);
let status: number = _xhr.status === 1223 ? 204 : _xhr.status;
let state:number = _xhr.readyState;
if (status === 0) {
status = body ? 200 : 0;
}
var responseOptions = new ResponseOptions({ body, status, headers, url });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
let response = new Response(responseOptions);
//check for the state if not 4 then don't complete the observer
if(state !== 4){
//this will return stream of responses
responseObserver.next(response);
return;
}
else{
responseObserver.complete();
return;
}
responseObserver.error(response);
};
// error event handler
let onError = (err: any) => {
var responseOptions = new ResponseOptions({ body: err, type: ResponseType.Error });
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
responseObserver.error(new Response(responseOptions));
};
if (isPresent(req.headers)) {
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
}
_xhr.addEventListener('progress', onLoad);
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
_xhr.send(this.request.text());
return () => {
_xhr.removeEventListener('progress', onLoad);
_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();
};
});
}
}
#Injectable()
export class MyBackend implements ConnectionBackend {
constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
createConnection(request: Request):MyConnection {
return new MyConnection(request, this._browserXHR, this._baseResponseOptions);
}
}
In the main component we have to provide the custom bakend like this:
providers: [
HTTP_PROVIDERS,
PostSrevice,
MyBackend,
provide(XHRBackend, {useExisting:MyBackend})
]
Now when we use http.get() it will return a stream of Observable

Related

Vuex Laravel Loads all data

I've been trying to load all of my data when the time I log in. Currently, I've only managed to display data through the console through vuex file. I just want to achieve this because wherever it loads all data when login, it will easier for me to call every function on every page.
I think the first step is to display it on vue devtools?
This is what I've tried.
I have this file on my "./store/modules/currentUser.js"
import axios from "axios";
const state = {
};
const getters = {};
const actions = {
loadEmployee({}){
axios.post(BASE_URL + '/transportation/driver/autoComplete').then(response => {
console.log(response.data); // how to pass result to devtools?
});
}
};
const mutations = {};
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
Login.vue
<script>
export default {
data () {
return {
listdata:[]
}
},
methods: {
login() {
this.$store.dispatch('currentUser/loadEmployee', this.listdata);
}
}
}
</script>
This is my vuedevtools looks like
And I want to fetch all data on listdata array vue devtools
If you are using vuex, you should probably load state in dev tools.
However, your issue is caused by not having a mutation, so you never update the state.
import axios from "axios";
const state = {
transansportDrivers: []
};
const getters = {};
const actions = {
loadEmployee({}){
axios.post(BASE_URL + '/transportation/driver/autoComplete').then(response => {
commit('setTransportDrivers', response.data)
});
}
};
const mutations = {
setTransportDrivers: (state, drivers) => {
state.transansportDrivers= drivers;
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations
}

Display Data from Backend in Ionic

I have a user.service, a list.page.ts and a list.page.html.
And I want to display data from the backend (php). I am getting Data in the console log, but it doesn't display on the website. Please Help.
Thank you!
user.servive
export enum SearchType {
all = '',
name = 'name',
stadt = 'stadt'
}
#Injectable({
providedIn: 'root'
})
export class UserService {
url = 'http://127.0.0.1:8000/getallrestaurants';
constructor(public http: HttpClient) { }
searchData(name: string, type: SearchType): Observable<any> {
return this.http.get('http://127.0.0.1:8000/getallrestaurants')
.pipe(
map(results => {
console.log('RAW: ', results);
return results['Search'];
})
);
}
list.page.ts
export class ListPage implements OnInit {
results: Observable<any>;
searchTerm = '';
type: SearchType = SearchType.all;
constructor(private userService: UserService) { }
ngOnInit() {}
searchChanged() {
this.results = this.userService.searchData(this.searchTerm, this.type);
}
list.page.html
<ion-searchbar [(ngModel)]="searchTerm" (ionChange)="searchChanged($event)"></ion-searchbar>
<ion-item>
<ion-label>Select Searchtype</ion-label>
<ion-select [(ngModel)]="type" (ionChange)="searchChanged($event)">
<ion-select-option value="">All</ion-select-option>
<ion-select-option value="stadt">Stadt</ion-select-option>
<ion-select-option value="name">Name</ion-select-option>
</ion-select>
</ion-item>
<ion-list>
<ion-item button *ngFor="let item of (results | async)" [routerLink]="['/', 'list', item.imdbID ]">
<ion-icon [name]="item.icon" slot="start"></ion-icon>
<p>Hallo User: {{item.name}}</p>
</ion-item>
This is my console.log, as you see i get the data, now i want it to show for example the name on frontend.
Ok I see the problem here in the class user.service you have to change it like this.
searchData(name: string, type: SearchType): Observable<any> {
return this.http.get('http://127.0.0.1:8000/getallrestaurants')
.pipe(
map(results => {
console.log('RAW: ', results);
return results;
})
);
}
You have to remove the ['Search'] because your JSON Response has no entry which is like 'Search'.
If you're seeing console.log update then this is most likely an issue with the html template not knowing some data changed that it needs to re-render and update the view for. Angular has built in change detection strategy but as the developer you can also manually interact with it. Checkout (https://angular.io/api/core/ChangeDetectorRef) for further explanation on this topic.
As for your code, try inserting the change detection reference as so by importing ChangeDetectorRef from #angular/core, and then check for changes of data to tell the view to re-render.
list.page.ts
export class ListPage implements OnInit {
results: Observable<any>;
searchTerm = '';
type: SearchType = SearchType.all;
constructor(private userService: UserService, private ref: ChangeDetectorRef) { }
ngOnInit() {}
searchChanged() {
this.results = this.userService.searchData(this.searchTerm, this.type);
this.ref.detectChanges()
}
Note: this will execute every time the user types and it may slow down your application because you're forcing angular to check for changes every time you call searchChanged(). I would thereby implement some sort of debounce technique to stall redundant or unnecessary calls to searchChanged(). Check out this resources to learn more about debouncing if this issue comes up (https://rxjs-dev.firebaseapp.com/api/operators/debounce).

Ionic 3 Uncaught (in promise): [object Object]

I am new to Ionic 3 and mobile development. I am trying to connect a MySQL DB to my Ionic app and a PHP Restful API. I tested the API with Postman and it is working just fine, in order to implement it in Ionic I did the following,
I first made a provider named Authservice:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import 'rxjs/add/operator/map';
let apiUrl = "http://localhost/api/"
/*
Generated class for the AuthServiceProvider provider.
See https://angular.io/guide/dependency-injection for more info on
and Angular DI.
*/
#Injectable()
export class AuthServiceProvider {
constructor(public http: HttpClient) {
console.log('Hello AuthServiceProvider Provider');
}
postData(credentials, type) {
return new Promise((resolve, reject) => {
let headers = new HttpHeaders();
this.http.post(apiUrl + type, JSON.stringify(credentials), { headers: headers })
.subscribe(res => {
resolve(res.json());
}, (err) => {
reject(err);
});
});
}
}
And a Signup page:
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { AuthServiceProvider } from '../../providers/auth-service/auth- service';
/**
* Generated class for the SignupPage page.
*
* See https://ionicframework.com/docs/components/#navigation for more info on
* Ionic pages and navigation.
*/
#IonicPage()
#Component({
selector: 'page-signup',
templateUrl: 'signup.html',
})
export class SignupPage {
responseData: any;
userData = {"username": "","password": "", "name": "","email": ""};
constructor(public navCtrl: NavController, public authServiceProvider: AuthServiceProvider) {
}
signUp() {
this.authServiceProvider.postData(this.userData, "signup").then((result) =>{
this.responseData = result;
console.log(this.responseData);
localStorage.setItem('userData', JSON.stringify(this.responseData));
});
}
goToLogin() {
this.navCtrl.pop();
}
}
When running this I am getting an Uncaught (in promise): [object Object] error as can be seen here.
UPDATE
I am now getting the following error:
Object { headers: {…}, status: 404, statusText: "Not Found", url: "http://localhost/PHP-SLIM-RESTFUL/API/signup", ok: false, name: "HttpErrorResponse", message: "Http failure response for http://localhost/PHP-SLIM-RESTFUL/API/signup: 404 Not Found", error: "<html><head><title>404 Page Not Found</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana,sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{display:inline-block;width:65px;}</style></head><body><h1>404 Page Not Found</h1><p>The page you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly. If all else fails, you can visit our home page at the link below.</p>Visit the Home Page</body></html>" } signup.ts:36:6
postData(credentials, type) {
let headers = new HttpHeaders();
return this.http.post(apiUrl + type, JSON.stringify(credentials), { headers: headers });
}
this will return observable on the signup page, just subscribe it.
You can make use of Typescript's async methods to make your life easier
Your postData method in async
AuthServiceProvider:
public async postData(credentials, type): Promise<any> {
let headers = new HttpHeaders();
await this.http.post(apiUrl + type, JSON.stringify(credentials), { headers: headers }).toPromise();
}
Signup page:
public async signUp(): void {
try {
// request successful
this.responseData = await this.authServiceProvider.postData(this.userData, "signup");
console.log(this.responseData);
localStorage.setItem('userData', JSON.stringify(this.responseData));
}
catch(e) {
// some error occured, handle it here..
console.log(e);
}
}
Don't forget to import toPromise operator in AuthServiceProvider
import 'rxjs/add/operator/toPromise';
Try importing HttpModule in app.module.ts;
{import HttpModule } from '#angular/http'
Then add HttpModule to the imports;
imports :
[BrowserModule,
HttpModule,
IonicModule.forRoot(MyApp)
]

Delete data in Angular2 app in PHP

How would one delete data from a MySql database using PHP code in an Angular2 application? The closest advice is for Angular 1 and is as follows:
$scope.deleteProduct = function(id){
// ask the user if he is sure to delete the record
if(confirm("Are you sure?")){
// post the id of product to be deleted
$http.post('delete_product.php', {
'id' : id
}).success(function (data, status, headers, config){
// tell the user product was deleted
Materialize.toast(data, 4000);
// refresh the list
$scope.getAll();
});
}
}
Is it possible to use the post method similarly:
import { Injectable } from '#angular/core';
import { Http, Response, Headers } from '#angular/http';
import 'rxjs/Rx';
#Injectable()
export class HttpService {
constructor(private http: Http) {}
deleteData() {
return this.http.post('delete_record.php')
}
}
Any insight/experience with Angular2/PHP would be appreciated.
Yes, the http post works similarly in angular2. Since you want to use post, i guess you also want to add a body to the request.
import { Injectable } from 'angular/core';
import { Http } from 'angular/http';
#Injectable()
export class HttpService {
constructor(private http: Http) {}
deleteData(data: SomeObject) {
let url = "delete_record.php";
let body = JSON.stringify(data);
return this.http.post(url, body)
.subscribe(
result => console.log(result),
error => console.error(error)
);
}
}
You can also send a delete request, which would be "best practice".
return this.http.delete(url)
.subscribe(
result => console.log(result),
error => console.error(error)
});
More about the http-client here https://angular.io/docs/ts/latest/guide/server-communication.html

(Ionic 2) Getting data from php services is undefined

I am new in ionic 2 and try to get my data from php services that i have created for ionic 1. It is works on ionic 1 but when i am getting data from ionic 2 it is undefined. It is works when i am getting data from php server on some website (from tutorial) but when getting my data from my local host it is undefined. It is the problem in my php code or i need to change my services. Thanks...
Here is my php code:
<?php
header('Access-Control-Allow-Origin: *');
$db_name = 'kuliner';
$hostname = 'localhost';
$username = 'root';
$password = '';
$dbh = new PDO("mysql:host=$hostname;dbname=$db_name", $username, $password);
$sql = 'SELECT * FROM promo';
$stmt = $dbh->prepare($sql);
// execute the query
$stmt->execute();
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );
$json = json_encode( $result );
echo $json;
?>
Here is my services:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import 'rxjs/add/operator/map';
/*
Generated class for the PromoService provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class PromoService {
data: any = null;
constructor(public http: Http) {}
load() {
if (this.data) {
// already loaded data
return Promise.resolve(this.data);
}
// don't have the data yet
return new Promise(resolve => {
// We're using Angular Http provider to request the data,
// then on the response it'll map the JSON data to a parsed JS object.
// Next we process the data and resolve the promise with the new data.
this.http.get('http://localhost:9999/KY Mobile/Promo/selectPromo.php')
.map(res => res.json())
.subscribe(data => {
this.data = data.results;
resolve(this.data);
});
});
}
}
My home page:
import {Page} from 'ionic-angular';
import {PromoService} from '../../providers/promo-service/promo-service';
#Page({
templateUrl: 'build/pages/home/home.html',
providers: [PromoService]
})
export class Home {
public promos: any;
constructor(public promoService : PromoService) {
this.loadPromo();
};
loadPromo(){
this.promoService.load()
.then(data => {
this.promos = data;
console.log("ABCD" + data);
});
}
}
My html:
<ion-content padding class="page1">
<ion-list>
<ion-item *ngFor="let promo of promos">
<ion-avatar item-left>
<img src="{{promo.image}}">
</ion-avatar>
</ion-item>
</ion-list>
</ion-content>
Update:
Solved now, the code must be this.data = data in PromoService. Thanks..
If res in
this.http.get('http://localhost:9999/KY Mobile/Promo/selectPromo.php')
.map(res => res.json())
is undefined then you don't get a value from http.get()
Other suggestions
You can use toPromise like
return this.http.get('http://localhost:9999/KY Mobile/Promo/selectPromo.php')
.map(res => res.json())
.do(data => this.data = data.results);
.toPromise();
or just return the promise returned from http.get() and subscribe on the call site
load() {
if (this.data) {
// already loaded data
return Observable.of(this.data);
}
return this.http.get('http://localhost:9999/KY Mobile/Promo/selectPromo.php')
.map(res => res.json())
.do(data => this.data = data.results);
}
and then just use subscribe() instead of then()
loadPromo(){
this.promoService.load()
.subscribe(data => {
this.promos = data;
console.log("ABCD" + data);
});
}
See also What is the correct way to share the result of an Angular 2 Http network call in RxJs 5?
Hint map, do, toPromise, ... need to be imported to become available.
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/toPromise';
or just
import 'rxjs/Rx';
I have had some issues with Promises myself in Ionic2/Angular2. You could try to replace the Promise with an Observable instead in your service:
return Observable.create(observer => {
this.http.get('http://localhost:9999/KY Mobile/Promo/selectPromo.php').map(res =>res.json()).subscribe(data=>{
observer.next(data)
observer.complete();
});
});
Then, in the home page.
loadPromo(){
this.promoService.load().subscribe(data=>{
this.promos = data;
console.log("ABCD" + data);
});
}

Categories