Authservice vs AngularFirestore - php

I am using AuthService to call my MySql database with PHP api and therefore my PHP functions for my Ionic 3 Angular 4 app.
This is how I call to get userData and it works fine
import { AuthService } from "../../providers/auth-service";
...
constructor(
public authService: AuthService,
private loadingCtrl: LoadingController
) {
const data = JSON.parse(localStorage.getItem("userData"));
this.userDetails = data.userData;
this.userPostData.uid = this.userDetails.uid;
let loader = this.loadingCtrl.create({
content: 'Please wait...'
});
}
I have another project that uses AngularFirestore
import { AngularFirestore, AngularFirestoreDocument } from 'angularfire2/firestore';
import { AngularFireAuth } from 'angularfire2/auth';
import { Storage } from '#ionic/storage';
and calls the constructor like this
private users: AngularFirestoreDocument<{}>;
constructor(
public navCtrl: NavController,
private afService: AngularFirestore,
private storage: Storage,
private loadingCtrl: LoadingController,
private afAuth: AngularFireAuth
) {
this.uid = localStorage.getItem('uid');
this.users = this.afService.collection('users').doc(this.uid);
let loader = this.loadingCtrl.create({
content: 'Please wait...'
});
loader.present();
this.users.valueChanges()
.subscribe((res: any) => {
this.canDoMessage = res.canDoMessage;
loader.dismiss();
}, error => {
loader.dismiss();
});
}
I just want to understand what calling this.afService.collection does?
Basically what i am asking is how to call the 'users' on AuthService
constructor( public authService: AuthService,,
like this on Firestone
constructor(private afService: AngularFirestore) { **this.users = afService.collection('users')**; }

You're injecting AngularFirestore as a dependency and you will have an instance of it in afService.
When you call this.afService.collection('users') it returns to you an AngularFirestoreCollection<{}> which is a wrapper around the native Firestore SDK's CollectionReference and Query types. It is a generic service that provides you with a strongly typed set of methods like valueChanges(), snapshotChanges(), stateChanges(), and auditTrail() for streaming data.
When you call this.afService.collection('users').doc(this.uid), it returns to you an AngularFirestoreDocument<{}> which is a wrapper around the native Firestore SDK's DocumentReference type. It is a generic service that provides you with a strongly typed set of methods like valueChanges() and snapshotChanges() for streaming data.
Since Firebase is event based, these APIs that you have on AngularFirestoreCollection and AngularFirestoreDocument gives you a way to stream data in a form that you're familiar with(using Observables in Angular)
Please follow the links to go through the docs to read more about these types.

I just want to understand what calling this.afService.collection does?
Ans:-
The AngularFirestoreCollection service is a wrapper around the native Firestore SDK's CollectionReference and Query types. It is a generic service that provides you with a strongly typed set of methods for manipulating and streaming data. This service is designed for use as an #Injectable().
The AngularFirestoreCollection is a service you use to create streams of the collection and perform data operations on the underyling collection.
this.itemsCollection = afs.collection<Item>('items');
Try This Code
this.users = this.afService.collection<user>('users').doc(this.uid);
Check Out The Link :- https://github.com/angular/angularfire2/blob/master/docs/firestore/collections.md

Related

Authenticate a use using Angular and Laravel passport with grant_type authorization_code

What i'am trying to do is setting up an authentication system for my website using Laravel passport as back-end and Angular as front-end
From the official documentation I understood that I first need to make a GET Request to /oauth/authorize route with the following data:
'client_id' : 1,
'redirect_uri' : `${this.baseURL}`,
'response_type' : 'code',
'scope' : '',
'state' : random_string(40)
and then make a POST request to /oauth/token endpoint with the code generated from the previous response to require an access_token
At the current state thou when I make the first request (/oauth/authorize) using postman, Laravel responds with this error:
Route [login] not defined.
From what I reed, I need first to authenticate the user in some ways before requesting for authorization to the endpoint but I cannot find out how to do this.
Do I need to define some login logic? like a login function in my controller that should do something with my user data?
ADDITIONAL INFOS:
-I am trying to authenticate users coming from my front-end so they are first-party users
-I don't want to use "password grant tokens" as it's not recommended by the docs
You do not have to send a GET request every time to receive tokens of the authentication system, since they are static and are generated when the command is executed: php artisan passport:install.
This data is stored in a table (schema) of the database oauth_clients.
You can write them, for example, in the environments of the Angular app and use them without making unnecessary requests to the server and database every time. If, of course, Personal Access Client and Password Grant Client tokens are constantly updated, for example, by CRON, then of course, in this case it is necessary and it makes sense to send a preliminary GET request to the server.
And the code for the client part of the authentication system will be as follows:
services/auth/auth.service
import { Injectable } from '#angular/core'
import { HttpClient } from '#angular/common/http'
import { catchError, tap } from 'rxjs/operators'
interface IResponseHTTPLogin { // can be placed in a separate module or model at the discretion
token_type: string
expires_in: number
access_token: string
refresh_token: string
}
#Injectable({
providedIn: 'root'
})
export class AuthService {
/**
* Auth Laravel Passport: Password Grant Client
*
* #private
*/
private clientSecret: string = 'YOUR client secret token from oauth_clients table()' // <-- add enviroments
private clientId: number = 2 // <-- id client from oauth_clients table() | add enviroments
constructor (private http: HttpClient) {}
/**
* Login in cabinet.
*
* #param email
* #param password
*/
login (email: string, password: string) {
return this.http.post<IResponseHTTPLogin>('/oauth/token', {
grant_type: 'password',
client_id: this.clientId,
client_secret: this.clientSecret,
username: email,
password
})
.pipe(
tap(({ access_token, refresh_token }) => {
if (access_token) {
this.setToken(access_token)
this.setRefreshToken(refresh_token)
}
}),
catchError((err) => {
throw new Error(err)
})
)
}
getToken (): string {
return sessionStorage.getItem('token') ?? ''
}
getRefreshToken (): string {
return sessionStorage.getItem('token_refresh') ?? ''
}
protected setToken (token: string): void {
sessionStorage.setItem('token', token) // OR use localStorage
}
protected setRefreshToken (refresh_token: string): void {
sessionStorage.setItem('token_refresh', refresh_token)
}
isAuthenticate (): boolean {
return !!sessionStorage.getItem('token')
}
logout (): void {
sessionStorage.removeItem('token')
sessionStorage.removeItem('token_refresh')
}
}
interceptor/token.interceptor
import { Injectable } from '#angular/core'
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '#angular/common/http'
import { Observable } from 'rxjs'
import { AuthService } from '../../services/auth/auth.service'
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor (private authService: AuthService) {}
intercept (request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
if (this.authService.isAuthenticate()) {
request = request.clone({ setHeaders: { Authorization: `Bearer ${this.authService.getToken()}` } })
}
return next.handle(request)
}
}
Very important! In order for your authorization request to be fulfilled and there are no CORS errors, it must be redirected to your backend, for example using the following approach using a proxy.
At the root of the project, create a proxy.conf.json file. Where to add the following code:
{
"/oauth/token": {
"target": "ADRESS YOR DOMAIN",
"secure": false,
"logLevel": "debug",
"changeOrigin": true
},
"/api/*": {
"target": "ADRESS YOR DOMAIN",
"secure": false,
"logLevel": "debug",
"changeOrigin": true
},
}
Next, tell Angular that all requests need to be proxied, for this, in package.json in the scripts section, specify:
...
"start": "ng serve --proxy-config proxy.conf.json", <------
...
In the AuthServiceProvider backend you can customize your tokens in the boot() method:
...
Passport::tokensExpireIn(now()->addHours(24));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addDays(30));
...
Functionality testing in development environment:
Laravel ^9.48
laravel-passport ^11.5.1
Angular ^15
I think you will find my answer helpful. If something is not clear, ask!

how to use react-query with a Laravel api

I am following a example that uses react-query, the example works fine with the test data, but when it hits a backend that I am building is not working as expected, I mean, it retrieves the information, but don't cache the information, always hits the server, but using the example, that was not happening.
hooks/user.js
import { useQuery } from "react-query";
import axios from "axios";
export const useUsers = (activePage) => {
return useQuery(
// Add activePage as a dependency
["users", activePage],
async () => {
const { data } = await axios.get(
//works fine here
`https://reqres.in/api/users?page=${activePage}`
//Here not works properly
//`http://127.0.0.1:8000/api/users?page=${activePage}`
);
return data;
},
// This tells React-Query that this is Query is part of
// a paginated component
{ keepPreviousData: true }
);
};
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
const queryClient = new QueryClient(); // Global Store Instance
ReactDOM.render(
<QueryClientProvider client={queryClient}>
<React.StrictMode>
<App />
</React.StrictMode>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>,
document.getElementById('root')
);
reportWebVitals();
App.js
import { useState } from "react";
import TableHeader from "./components/tableHeader/TableHeader";
import TableRow from "./components/tableRow/TableRow";
import Pagination from "./components/pagination/Pagination";
// Import the hook
import { useUsers } from "./hooks/users";
const App = () => {
const [activePage, setActivePage] = useState(1);
// React Query takes care of calling
// userUsers hook when App.js is rendered.
const usersList = useUsers(activePage);
return (
<>
<TableHeader />
{/* map through users list */}
{usersList.data &&
usersList.data.data.map((user) => <TableRow user={user} key={user.id} />)}
<Pagination
activePage={activePage}
setActivePage={setActivePage}
pages={2}
/>
</>
);
};
export default App;
In the example, when I hit the api again, I mean, I go to other page twice, in the network tab is says this: (disk cache)
Which is the expected behaviour, but when it is using my laravel api, then is not working properly, it is able to retrieve the information, but always hits the server, not the cache
In my laravel app I have this:
routes/api.php
Route::get('/users', [UsersController::class, 'index']);
UsersController.php
...
public function index()
{
return User::paginate(10);
}
...
The frontend is using this url:
http://localhost:3000/
and the backend is using this:
http://localhost:8000/
maybe is because of the port? but using the external api: https://reqres.in/api/users?page=1 it works without problem, it uses the cache as expected, do weird. I think I need to modify my api
This is the response of my local api:
{
"current_page":1,
"data":[
{
"id":1,
"first_name":"sergio",
"avatar_url":"https:\/\/url",
"age":30,
"created_at":"2022-09-11T22:29:52.000000Z",
"updated_at":"2022-09-11T22:29:52.000000Z"
},
{
"id":2,
"first_name":"jhon",
"avatar_url":"https:\/\/url",
"age":39,
"created_at":"2022-09-11T22:30:03.000000Z",
"updated_at":"2022-09-11T22:30:03.000000Z"
},
...
...
],
"first_page_url":"http:\/\/127.0.0.1:8000\/api\/users?page=1",
"from":1,
"last_page":9,
"last_page_url":"http:\/\/127.0.0.1:8000\/api\/users?page=9",
"next_page_url":"http:\/\/127.0.0.1:8000\/api\/users?page=2",
"path":"http:\/\/127.0.0.1:8000\/api\/users",
"per_page":3,
"prev_page_url":null,
"to":3,
"total":25
}
What can I do? thanks.
Replace return in your controller by this one
return response()->json(User::paginate(10));

Next js - Call a function on server side each time a route is changed

I am moving an old stack to next js and i need some help.
I have a php backend and I want to check a controller from this backend each time a route is changed, and I want to do it on server side.
For changing route, I use Link of next-routes, which have an effect only on the client side.
Is there any way I can call the controller on server-side without refreshing the app?
You can take advantage of Router's events and Custom App watching for a route change and perform the actions you need. Below you can find two sample implementations, one using class component and one using hooks. Both should do the trick.
// class component
import NextApp from 'next/app';
import Router from 'next/router';
class App extends NextApp {
componentDidMount() {
Router.events.on('routeChangeStart', () => {
// call to your backend
});
}
render() {
const { Component, pageProps } = this.props;
return <Component {...pageProps} />;
}
}
export default App;
// using hooks
import Router from 'next/router';
const App = ({ Component, pageProps }) => {
React.useEffect(() => {
const doMagic = () => {
// do your thing
}
Router.events.on('routeChangeStart', doMagic); // add listener
return () => {
Router.events.off('routeChangeStart', doMagic); // remove listener
}
}, []);
return <Component {...pageProps} />;
}
export default App;

Observable does not returns api response

I am new in angular.
My Project is in angular5 and I am using a web API created in PHP.
My WebApi gives response in postman but not from angular.
I am using HttpClient to post request
export class GalleryComponent implements OnInit {
private apiUrl = 'http://localhost:8080/fullpath';
private response;
private data=[];
constructor(private http: HttpClient) {
this.response =this.getCategory();
}
getCategory (): Observable<any> {
return this.http.post(this.apiUrl,{});
}
ngOnInit() {
console.log(this.response);
}
}
My console Returns
What I have to do to get proper response from API?
When using Observables, you have to subscribe to them.
The actual user of the observable needs to subscribe(), because without
subscribe() the observable won't be executed at all.
subscribe() returns a Subscription that can not be subscribed to, but it can be used to cancel the subscription.
getCategory (): Observable<any> {
return this.http.post(this.apiUrl,{}).subscribe(
//Do whatever
(res)=> console.log(res),
(error)=> console.log(error)
);
}
For more information you can go to the Angular documentation for Observables:
https://angular.io/guide/observables.
Also, remember that most of the subscriptions terminate by themselves, but is always good practice to terminate them using ngOnDestroy(); to avoid memory leaks.
How to use ngDestroy()?
In you component.ts file
Make sure you have the import
import { Component, OnDestroy, OnInit } from '#angular/core';
export class YourComponent implements OnInit, OnDestroy {
.
.
.
ngOnDestroy() {
this.response.unsubscribe();
}
}
Now, related to your CORS Problem, reference the question that has been asked on stackoverflow before:
Response to preflight request doesn't pass access control check
you can try this solution
ngOnInit() {
this.getCategory().subscribe(response => {
console.log(response);
}, err => {
console.log(err);
});
}
You need to subscribe to an Observable to consume it.
Instead of this
private http: HttpClient) {
this.response =this.getCategory();
}
Try this
private http: HttpClient) {
this.getCategory().subscribe(result => this.responce = result);
}
Great! You can try that
this.getCategory().subscribe(
response => console.log(response),
err => console.log(err)
);
But the best way to use that, you need to create service for POST Request, and you just call him im component. Like that
getCategory(data: any): Observable<any> {
return this._http.post(`url`, JSON.stringify(data))
}
And use that in component
this.categoryService.getCategory(data).subscribe(
data => console.log(data),
err => console.log(err)
)
And you can use Google DevTools to see if is worked the request, and see the request Response! In network! If problem is CORS, you need to set CORS domain in your server!
Try that and tell me if worked! Hope that helps! Thanks

Angular 2 stable: templateUrl variable

I am fairly new to AngularJS (using 2 stable). I have an existing PHP/Codeigniter3 app and my job is to make an SPA. I am running into a problem where I can't access router params at all to add them in a templateUrl.
For example:
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute, Params } from '#angular/router';
#Component({
selector: 'apps_container',
// template: `You just accessed app: {{app_name}}` // This binding works obviously.
templateUrl: (() => {
// return 'app/' + this.route.params['app_name'] // Will never work no matter what because I have no access to route
return 'app/:app_name'; // Treated as a string.
})()
})
export class AppViewComponent {
app_name: any;
constructor(
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
this.route.params.forEach((params: Params) => {
this.app_name = params['app_name'];
});
}
}
That being said, you could to do that via dynamic component loading.
In angular2 final it might look like this:
#Component({
selector: 'app-container',
template: '<template #vcRef></template>'
})
export class AppContainerComponent {
#ViewChild('vcRef', { read: ViewContainerRef }) vcRef: ViewContainerRef;
constructor(
private route: ActivatedRoute,
private cmpFactoryResolver: ComponentFactoryResolver,
private compiler: Compiler
) { }
ngOnInit() {
this.route.params.forEach((params: Params) => {
this.loadDynamicComponent(params['app_name']);
});
}
loadDynamicComponent(appName) {
this.vcRef.clear();
#Component({
selector: 'dynamic-comp',
templateUrl: `src/templates/${appName}.html`
})
class DynamicComponent { };
#NgModule({
imports: [CommonModule],
declarations: [DynamicComponent]
})
class DynamicModule { }
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
.then(factory => {
const compFactory = factory.componentFactories
.find(x => x.componentType === DynamicComponent);
const cmpRef = this.vcRef.createComponent(compFactory);
cmpRef.instance.prop = 'test';
cmpRef.instance.outputChange.subscribe(()=>...);;
});
}
}
Plunker Example
I guess there are other ways to do that like ngSwitch or ngTemplateOutlet
Severals interresting answers related to your question can be found here : Dynamic template in templatURL in angular2
templateUrl callback will be executed before creating component, in other words before route injection, so there are two ways to do what you want:
Use window.location to get current url and manually parse parameters.
Add router listener to detect RoutesRecognized event and use its state:RouterStateSnapshot property to find parameters and save it to static variable to use in template callback.
yurzui - does the dynamically added component gets all the binding in the parent component - AppContainerComponent in your example?

Categories