Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

need to separate the graphql login logic from my angular component, to make it more generic

I'm having some fun programming with Angular 10 and I'm trying to to the following:

I created a user library that is in charge of the profile, login and register pages and components. I use apollo-graphql in my project to login to the server.

so my login() function in the user library in LoginTabComponent is this:

  login(): void {
    if (this.loginForm.invalid) {
      return;
    }
    const params = this.loginForm.value;
    this.graphQlService.login(params.email, params.password).subscribe(({data}: LoginDataToken) => {
      if (!data.login) {
        this.openSnackBar('Invalid email and/or password');
      } else {
        this.auth.storeToken(data.login);
        this.router.navigate(['/']).finally(() => {
          this.openSnackBar('Hello ' + data.login.name);
        });
      }
    });
  }

the login data token is define here:

export interface Token {
  token: string;
  exp: number;
  name: string;
}

export interface LoginToken {
  login: Token;
}

export interface LoginDataToken {
  data: LoginToken;
}

so i'm checking if the form is valid, and if it is, getting the params, executing a login function from a graphQL service that I created, and if the data is valid, login the user, if not, showing a snackbar of the error.

this is the login function from the graphql service:

const mutationLogin = gql`
    mutation Login($email: String!, $password: String!) {
      login(email: $email, password: $password) {token,exp,name}
    }
`;

  public login(email: string, password: string): any {
    return this.apollo.mutate({
      mutation: mutationLogin,
      variables: {email, password}
    });
  }

now I want my LoginTabComponent to receive a generic function, that will receive to parameters user and password and that it will return some sort of observable so i will be able to subscribe to it. just so the user can implement the server interaction anyway he wants.

so I created a LoginCallback type:

export type LoginCallback = (username: string, password: string) => Observable<LoginDataToken>;

and in the LoginTabComponent I added:

 @Input() loginCallback: LoginCallback | undefined;
 
 ngOnInit(): void {
  if (this.loginCallback === null || this.loginCallback === undefined) {
   throw new TypeError('The input ‘loginCallback’ is required');
  }
 }

and then in the login function I changed the the code that executes the graphql service to this:

  this.loginCallback(params.email, params.password).subscribe(({data}: LoginDataToken) => {

now the problem that I have left is that LoginTabComponent is being called by LoginPageComponent which is part of this library. and LoginPageComponent is being executed in a route, so I have no idea how to paste the proper function to this component.

I saw that components when you initialize them in your module you've got forRoot() and you can add parameters. I have no clue how to do that.

so.. am I going in the right direction ? and if so how do I paste the function to be executed, properly to that module ?

thanks

like image 308
ufk Avatar asked Nov 22 '25 07:11

ufk


1 Answers

One possible solution to your problem is, You can provide a service with your User library module.

let's call it UserLibService.

In the UserLibService you can have one property named loginCallback like below.

@Injectable()
export private class UserLibService {
    loginCallback: (userName: string, password: string) => Observable<LoginDataToken>;
}

You can set the value of this property from your actual application where you are using User library. All you have to do is to inject this UserLibService.

And your LoginTabComponent can also inject this service and inside login() function use this callback to perform login.

So in the constructor of the LoginTabComponent:

constructor(private userLibService: UserLibService){}

And update user login function to use the loginCallback:

this.userLibService.loginCallback(params.email, params.password).subscribe(({data}: LoginDataToken) => {

And you can also perform the null check and any other validation before calling the function.

OR

You can drop the idea of providing a service and do the configuration of loginCallback using forRoot() method.

I found a good explanation of configuring libraries with forRoot() method here.

https://offering.solutions/blog/articles/2019/12/31/configuring-angular-libraries/

I think you are going in the right direction because it is important to provide the flexibility of being able to configure the login function while using the library. And I think configuring the library using forRoot() method feels more natural to me in angular.

like image 127
HirenParekh Avatar answered Nov 24 '25 22:11

HirenParekh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!