npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@jussanjuan/angular-pjsj

v12.2.5

Published

La motivación proncipal de la librearia es la de crear elementos comunes que se presentan en distintos desarrollos para poder distribuirlos desde este repositorio y centralizar su evolución. Con esta libreria vas a poder crear aplicaciones angular de mane

Downloads

114

Readme

Angular PJSJ

La motivación proncipal de la librearia es la de crear elementos comunes que se presentan en distintos desarrollos para poder distribuirlos desde este repositorio y centralizar su evolución. Con esta libreria vas a poder crear aplicaciones angular de manera mucho mas ágil, centrado la total atención en el problema a resolver y no al desarrollo de componentes como modales, alertas, tablas, etc.

Getting Started

Install

npm i @jussanjuan/angular-pjsj
    import { AngularPjsjModule } from '@jussanjuan/angular-pjsj';
    
    NgModule({
          declarations: [
            ...
          ],
          imports: [
            ...
            AngularPjsjModule,
            ...
          ],

Dependencidas necesarias

npm i @angular/flex-layout
npm i @angular/material
npm i @angular/cdk

Configuración de Clase

La configuración de clase de una entidad es el elemento central sobre el cual se crearon los demas desarrollos genéricos. Esta configuración contiene desde el nombre de la entidad hasta el tipo de cada uno de los campos de la entidad. Cada una de las entidades que se quieran mostrar con los componentes debe tener definido el metodo estatico getClassConfig() : ClassConfig que va a retornar su configuración particular

Clases

ClassConfig

| Atributos | Tipo | Observaciones | | :-------- | :--- | :------------ | | entityName | String | Texto estatico que será el nombre con el cual se presentará la entidad. Por ejemplo el entityName de una entidad Person es 'Persona'. | | orderFilter? | string[] | Arreglo de String que contiene el nombre los atributos y el orden en el que se van a mostrar en el selector de atributos a filtrar | | idKeys | string[] | Arreglo que contiene el nombre de los atributos que forman la clave primaria o Id de la entidad | | deleteKeys? | string[] | Arreglo que contiene los atributos que se van a mostrar en un cartel de confirmación de eliminación de un elemento | | fieldConfig | FieldConfigHGeneric[] | Arreglo con toda la configuración de cada uno de los campos de la entidad en cuestion |

FieldConfigHGeneric

| Atributos | Tipo | Observaciones | | :-------- | :--- | :------------ | | key | string | Nombre del campo en la entidad. Por ejemplo si tenemos la clase Person en TypeScript que tiene el atributo de clase name entonces la key debe ser el string 'name' | | display | String | Es el texto con el que se va a presentar ese campo al usuario final. Siguiendo el ejemplo de Persona cuyo campo se llama name entonces un buen valor para display sería el string 'Nombre de Persona' | | isColumn | Boolean | Campo booleano que va a indicar si el campo en cuastion se va a mostrar al usuario o no, en la tabla generica o en otro componente genérico. | | isFilter | Boolean | Campo booleano que va a indicar si ese campo va a ser el valor de un filtro o no. | | typeFilter | FieldType | Este campo es el tipo de filtro que se va a pintar en el formulario de filtros del componente Filtro Genérico. Dependiendo del valor de este atributo se va a pintar un input del tipo texto o númerico o un select, etc | | url? | string | En caso de ser un campo de tipo select el que se va a pintar en un filtro. Se puede usar esta propiedad para indicarle al componente de filtro que debe ir a buscar la información del select de un servicio rest que debe ser un get | | options? | any[] | En caso de ser un campo de tipo select simple se puede usar esta propiedad para indicarle los valores que se van a mostrar. Un ejemplo es el select del sexo de una persona que siempre van a ser dos valores 'Masculino' y 'Femenino'. Otro uso de este atributo es para los datos de tipo booleano que se desean mostrar de una forma especifica en el componente que los muestre. En este caso se van a levantar los dos primeros valores como los representativos del mismo. Siendo el valor en la posición 0 del arreglo para el valor True y el contenido de la posición 1 es como se va a mostrar el valor False del atributo en cuestion. Un ejemplo es si queremos indicar si una persona esta casada o no y tenemos el campo isMarried en la clase y queremos mostrar los valores 'Si' cuando sea True y 'No' cuando sea False, entonces este campo debe contener el arreglo ['Si', 'No']. Adicionalmente en las tablas como en otros componentes se puede enviar HTML para pintar el campo a conveniencia. | optionsCheck? |any[] | Atributo auxiliar para uso exclusivo e interno del filtro genérico |

FieldType

Esta clase es un enumerador y los valores son los siguientes:

| Valores | Observaciones | | :-------- | :------------ | | inputText | Este enumerador va a indicar que el campo es del tipo texto, para trabajarlo como tal en html | | inputNumber | Este enumerador va a indicar que el campo es del tipo número. | | inputPassword | Este enumerador va a indicar que el campo es del tipo privado. | | radioButton | Este enumerador va a indicar que el campo es un radio button. Se va a requerir del atributo ClassConfig.options o url para mostrar sus valores | | textarea | Este enumerador va a indicar que el campo es del tipo texto y que la entrada es de una longitud considerable. | | select | Este enumerador va a indicar que el valor del campo se debe ingresar desde un selector de opciones. Por lo tanto tiene un número finito de opciones y que se tienen que recuperar ya sea desde la ClassConfig.url o de manera estatica de ClassConfig.options. | date | Este enumerador va a indicar que el valor del campo es una fecha y se le debe aplicar filtros para mostrarlas en el formato correcto. El ingreso del valor será un DatePicker. | file | Este enumerador va a indicar qeu el valor de entrada es un archivo. [Aún no esta soportado por los componentes genéricos] | | check | Este enumerador va a indicar que el valor es un dato booleano. |

Ejemplo

Este ejemplo es sobre la clase Hero donde tenemos el nombre del heroe su identificador y superpoderes.

    
    import { ClassConfig, FieldType } from 'projects/angular-pjsj/src/public-api';
    import { Superpower } from './superpower';

    export class Hero {
        id: number;
        name: string;
        powers?: Superpower[];

        public static getClassConfig(): ClassConfig {
            return {
                entityName: 'Heroes',
                idKeys: ['id'],
                fieldConfig: [
                    {
                    key: 'id',
                    display: 'Identificador',
                    isColumn: true,
                    isFilter: false,
                    typeFilter: FieldType.inputText
                },{
                    key: 'name',
                    display: 'Nombre',
                    isColumn: true,
                    isFilter: true,
                    typeFilter: FieldType.inputText
                },{
                    key: 'powers',
                    display: 'Superpoderes',
                    isColumn: false,
                    isFilter: false,
                    typeFilter: FieldType.select,
                    url: 'http:localhost:8080/selects/superpowers'
                }]
            }
        }
    }

Componentes

Home Genérico (GUI)

Este componente genera una GUI compuesta de un navbar, donde se muestra el título de la aplicación y un sidenav, donde se generan links de acceso a los potenciales recursos, más un link para realizar el logout de la aplicación. La GUI se generará en base a la clase de configuración HomeConfig.

Inputs de Home Genérico

  • Configuración de componente: La configuración es una instancia de HomeConfig y es donde se obtiene toda la información necesaria para mostrar y crear la GUI genérica.

Outputs de Home Genérico

  • Evento de logout: El componente Home expone la propiedad logoutEvent para emitir el evento click del link de logout (Salir). La propiedad se debe asociar a un método (p.e. 'logoutMethod()') donde se defina la lógica asociada al evento.

Modo de uso:

    <g-home [homeConfig]="homeConfig" (logoutEvent)="logoutMethod()"> <!--Cuerpo de la aplicación --> </g-home>

Clases de configuración.

HomeConfig

| Atributos | Tipo | Observaciones | | :-------- | :--- | :------------ | | appName | String | Texto estático que será el nombre de la aplicación que se mostrará en el navbar. | | linksMenu | LinkMenu[] | Arreglo de LinkMenu que contiene el nombre del link, path del recurso y el string del ícono que se mostrará en los links del sidenav. |

LinkMenu

| Atributos | Tipo | Observaciones | | :-------- | :--- | :------------ | | name | String | Nombre que se mostrará en el link. | | path | String | Texto que representa la ruta al recurso que invoca el link. | | icon | String | Texto que representa el ícono que acompaña el link. |

Filtro Genérico

Este componente genera filtros de una entidad según su configuración de clase. El formulario de filtros se generará en base a la configuración de cada campo. Por ejemplo si un campo tiene como typeFilter el tipo TypeFilter.inputText, entonces el filtro para ese campo sera un input del tipo texto y asi. Este componente esta compuesto de la siguiente manera:

            (*1)                                                                                                 
 Búscar ${entityName} por filtro 

 (*2)                          (*3)                           (*4)                (*5)                                      
 +------------------------+   +------------------------+      +--------------+    +------------------------+
 | Seleccionar campo    V |   | ${Input dinamico}      |      | Boton Búscar |    | Boton Limpiar Filtros  |
 +------------------------+   +------------------------+      +--------------+    +------------------------+
  • Referenicias:

    1. Es el nombre de la entidad recuperada del campo ClassConfig.entityName.
    2. El seleccionador de campos es un select donde se muestran todos los valores del atributo ClassConfig.fieldConfig.*.display, es decir el campo display de cada uno de los elementos del arreglo fieldConfig.
    3. En este espacio se va a poner el elemento que corresponda. Ya sea un DatePicker para un campo del tipo fecha, un input texto para otro del tipo string y asi.
    4. Este boton es el que libera el evento de busqueda e indica al componente que lo use que los valor a filtrar cambiaron, asi tambien envia toda la información del nuevo filtro.
    5. Elimina todos los valores de los filtros.

Inputs de Filtro Genérico

  • Configuración de clase: La configuración de clase es una instancia de ClassConfig y es donde se obtienen toda la información necesaria para mostrarla y crear los inputs genéricos.

Modo de uso:

    <g-filter #gFilter [classConfig]="classConfig"></g-filter>

Tabla Generica

Esta tabla esta diseñada para poder adaptarse y presentar cualquier tipo de información. Para poder usar está tabla se debe ingresar los siguientes elementos como "input":

  • Instancia del service a usar: Es una instancia de un servicio angular que herede de la superclase GenericService. Este servicio se debe inyectar en el componente que utilice La tabla generica. La tabla generica útiliza una instancia de GenericService de una cierta entidad para traer sus datos.

    +------------------------+          +-----------------------------+                                                  
    |                        |          |                             |                                         
    |    Tabla Generica      |          |       GenericService<C,R>     |                                         
    |                        |          |                             |                                         
    +------------^-----------+          +--------------^--------------+                                         
                 |                                     |                                                        
                 |Envia instancia                      |Hereda Funcionalidades                                  
                 |de ServiceE                          |                                                        
                 |                                     |                                                        
    +------------|-----------+              +----------|----------+                  +-------------------------+
    |                        |  Se Inyecta  |                     |  Solicita Datos  |                         |
    |  Componente que        <---------------       ServiceE      -------------------|       Web Service       |
    |  Usa tabla Generica    |              |                     |                  |                         |
    +------------------------+              +---------------------+                  +-------------------------+
  • Configuración de clase: La configuración de clase es una instancia de ClassConfig y es donde se obtienen toda la información necesaria para mostrar la información de cada campo de una cierta entidad.
  • [Opcional] Filtro: es una instancia del componente de g-filter. De esta manera se va a vincular con la tabla para indicarle cuando se filtre o no un dato y mostrar los elementos filtrados.
  • [Opcional] Acciones: Se entiende por acción a las acciones que se desean realizar por cada elemento de la tabla. Un ejemplo puede ser: ver el detalle, editar el elemento, etc. Cada una de las acciones se va a corresponder con un boton en la tabla en la columna de Acciones. El input del componente es un Arreglo de ActionConfig

| Atributo | Observaciónes | | :------- | :------------ | | event | Instancia de @angular/core/EventEmitter que es la que va a tener la función que manejará el click del botón. | | text | Texto estatic que va a ser el contenido del boton | | textResolver? | Función que recibe como parametro el elemento y retorna el texto del boton. Se va a invocar cada vez que cambie el elemento. | | colorResolver? | Función que recibe como parametro el elemento y puede retornar el color del boton. Siendo los colores posibles 'warn', 'accent', 'primary' o 'none'. |

Ejemplos

  • app.component.html
<g-home #gHome [homeConfig]="homeConfig" (logoutEvent)="logoutMethod()">

    <router-outlet></router-outlet>

</g-home>
  • app.component.ts
import { Component, EventEmitter } from '@angular/core';
import { HomeConfig } from 'projects/angular-pjsj/src/lib/domain/generic/home-config';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  homeConfig: HomeConfig = this.getHomeConfig();

  getHomeConfig(): HomeConfig {
    return {
      appName: 'Example App',
      linksMenu: [
        {
          name: 'Heroes',
          path: 'heroes',
          icon: 'fas fa-address-book'
        }, {
          name: 'Opción 1',
          path: 'opcion-1',
          icon: 'fas fa-adjust'
        }, {
          name: 'Opción 2',
          path: 'opcion-2',
          icon: 'fas fa-anchor'
        }, {
          name: 'Opción 3',
          path: 'opcion-3',
          icon: 'icono-arg-seguridad-red'
        }]
    }
  }

  constructor() { }

  salir() {
    // TODO lógica para evento logout del componente GHome.
    console.log("Click en Salir");
  }
}
  • app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent } from './heroes/heroes.component';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
import { Page3Component } from './page3/page3.component';

const routes: Routes = [
  { path: '', 
    redirectTo: '/heroes',
    pathMatch: 'full'
  }, {
    path: 'heroes',
    component: HeroesComponent
  }, {
    path: 'opcion-1',
    component: Page1Component
  }, {
    path: 'opcion-2',
    component: Page2Component
  }, {
    path: 'opcion-3',
    component: Page3Component
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { };
  • heroes.component.html
    <g-filter #gFilter [classConfig]="classConfig"></g-filter>
    
    <g-table 
        #tabservice 
        [gFilter]="gFilter" 
        [classConfig]="classConfig" 
        [service]="service" 
        [actions]="actions">
    </g-table>
  • heroes.component.ts
import { Component, EventEmitter } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
import { ClassConfig, ActionConfig, MessageService } from 'projects/angular-pjsj/src/public-api';
import { MatDialog } from '@angular/material/dialog';
import { ConfigSuperpowerComponent } from './config-superpower/config-superpower.component';

@Component({
  selector: 'app-heroes',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class HeroesComponent {
  classConfig: ClassConfig = Hero.getClassConfig();
  actions: ActionConfig[];
  
  constructor(public service: HeroService, private messageSerivce: MessageService, private dialog: MatDialog) {
    // Creción del evento de Carteles
    let dialogs = new ActionConfig();
    dialogs.event = new EventEmitter();
    // Se subscribe la funcion que se va a ejecutar al click del boton
    dialogs.event.subscribe((hero) => this.detail(hero));
    // Se agrega el texto del boton
    dialogs.text = 'Carteles';
    // Se agrega un color al boton, adicionalmente puede cambiar según los atributos del heroe
    dialogs.colorResolver = (hero) => {
      return 'accent'
    }
    let superpowers = new ActionConfig();
    superpowers.event = new EventEmitter();
    superpowers.event.subscribe((hero) => this.powerConfig(hero));
    superpowers.text = 'Super Poderes';
    this.actions = [superpowers, dialogs];
  }

  detail(hero: Hero) {
    ...
    ...
  }

  powerConfig(hero) {
     ...
     ...
  }
}
  • heroes.service.ts
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { HttpClient } from '@angular/common/http';
import { LoadService, MessageService, GenericService } from 'projects/angular-pjsj/src/public-api';

@Injectable({
  providedIn: 'root'
})
export class HeroService extends GenericService<Hero,Hero> {
  url: string;

  constructor(httpClient: HttpClient, loadService: LoadService, messageService: MessageService) {
    super(httpClient, loadService, messageService);
    this.url = '/heroes';
  }

}

Entity Data

Muestra todos los campos de una entidad que tengan en el atributo "column = true" del FieldConfig correspondiente.

Ejemplo de uso:

  • hero-detail.component.html
<mat-dialog-content>
    <entity-data [classConfig]="heroCC" [entity]="hero"></entity-data>
</mat-dialog-content>
<mat-dialog-actions>
    <button mat-raised-button [mat-dialog-close]="false">Cerrar</button>
</mat-dialog-actions>
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Hero } from '../hero';

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.sass']
})
export class HeroDetailComponent implements OnInit {

  heroCC = Hero.getClassConfig();
  hero: Hero;
  constructor(
    @Inject(MAT_DIALOG_DATA) public data: any
    ) {

      this.hero = data;
    }

  ngOnInit(): void {
  }

}

En el ejemplo anterior podemos ver como usarlo en un MatDialog.

Carteles

Message service

Este servicio de mensajes es el que centraliza todos los llamados a los carteles de alerta, dialogo, etc. Gracias a este servicio podemos mantener un unico estilo de carteles, alertas y formas para todas las aplicaciónes, pre construidos.

Ejemplo:

 messageServiceTest(hero: Hero) {
    this.messageSerivce.showError(['ERROR ::: Nombre: '+ hero.name, 'ERROR ::: Id: '+hero.id]).subscribe(() => {
      this.messageSerivce.showConfirmDialog('Heroe: ' + hero.name +' <p>Id: ' + hero.id +'</p>', 'Dialogo INFO!! Detalle del Heroe').subscribe(() => {
        this.messageSerivce.showAlertConfirmDialog('Heroe: ' + hero.name +' <p>Id: ' + hero.id +'</p>', 'Alerta!!  Detalle del Heroe').subscribe(() => {
          this.messageSerivce.showInfo('Info:: Heroe: ' + hero.name );
        });
      });
    });
  }

Indicadores de carga de información

Load service

Este servicio expone distintos metodos a los cuales se deben subscribir para poder reconocer cuando se esta ejecutando un request cualquiera y cuando termina. Esta pensado para que se indique al usuario de alguna manera que su pedido esta siendo procesado y que se esta esperando una respuesta del servicio web.

| Metodo | Parametros | Retorno | Observaciones | | :------ | :--------- | :------ | :------------ |

Pignus

Para poder conectarse con pignus necesitamos crear un componente y tenerlo vinculado a la ruta "/login". De esta manera se realiza con Angular Router:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';


const routes: Routes = [ {
    path: 'login',
    component: LoginTestComponent
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})

El componente LoginTestComponent debe ser un componente especial que se crea de una manera especifica, extendiendo de la clase PignusLogin, proveida desde la libreria. Donde la clase LoginTestComponent quedaria de la siguiente manera:

import { Component} from '@angular/core';
import { ActivatedRoute, Router, NavigationExtras } from '@angular/router';
import { LoadService, PignusLogin, AuthPignusService } from '@jussanjuan/angular-pjsj';
import { environment } from 'src/environments/environment';


@Component({
  selector: 'app-login-test',
  templateUrl: './login-test.component.html',
  styleUrls: ['./login-test.component.sass']
})
export class LoginTestComponent extends PignusLogin {
  
  constructor(  authService: AuthTestService,
      private router: Router,
     activatedRoute: ActivatedRoute,
     private loadService: LoadService) {
    super(authService, activatedRoute);
  }
  /**
   * Este metodo se invoca cuando detecta un acceso no permitido a la plataforma. 
   * Desde este espacio se debe siempre redirigir a la ruta de login de pignus, pero tambien se pueden ejecutar 
   * validaciones de datos, generar carteles, etc.
   * Pero sin redirigir a Pignus no se va a poder utilizar las acciones que sean privadas en un servidor
   *  
   */
  public redirectNotAuth() {
    console.log(LoginTestComponent.toString() + ": No autenticado")
    // urlLogin es la url de login de Pignus
    window.location.href = environment.urlLogin;
  }
  /**
   * Este metodo se invoca cuando se autentica correctamente con pignus. 
   * Desde este espacio se pueden ejecutar diferentes tipos de redirecciones, validaciones y demás.
   *  
   */
  public successAuth() {
    console.log(LoginTestComponent.toString() + ": Autenticado")
    // Cuando se autentique correctamente, es necesario redirigirlo a la ruta que intentaba acceder. O en esta caso siempre al home
    this.router.navigate(['/home']);
  }

}

Como podemos ver en el ejemplo tenemos que implementar dos métodos abstractos, donde cada uno tiene su acción especifica a ejecutar y que se ejecutan en lugares especificos.

Además tenemos que tener en cuenta que existe una buena practica y es la de agregar como propiedad de configuración del entorno la ruta de redirección a Pignus. Ya que para QA es una ruta, para desarrollo es otra y para producción otra.

Para lo cual necesitamos ingresar una entrada en el archivo de src/app/enviroments/enviroment.ts para desarrollo/QA y en src/app/enviroments/enviroment-prod.ts para producción.

Un ejemplo de la configuración de la ruta de redirección es la siguiente:


export const environment = {
  production: false,
  urlLogin : 'http://${direccion-servidor-pignus}/api/?publickey=${public-key-sistema-en-cuestion}'
};

ADVERTENCIA!! NO se debe implementar la interfaz OnInit en el componente de Login ya que esta implementada en la clase abstracta y es usado el Metodo NgOnInit() para ejecutar lógica especifica de autenticación.

Especificacion de la clase PignusLogin | Método | Abstracto | Observaciones | | :------ | :-------- | :------------ | | NgOnInit | No | Metodo sobre escrito desde la interzaf OnInit y es el encargado de iniciar la ejecución del método initalEvaluate | | initalEvaluate | No | Método que ejecuta la lógica necesaria para saber si el usuario esta logueado o no. En caso de tener authcode y no JWT, invoca al metodo exchange del AuthPignusService. Si no tiene authcode, invoca el método abstracto redirectNotAuth, en caso de reconocer que esta todo bien invoca al método abstracto successAuth | | redirectNotAuth | Si | Este metodo se invoca cuando detecta un acceso no permitido a la plataforma. Desde este espacio se debe siempre redirigir a la ruta de login de pignus, pero tambien se pueden ejecutar validaciones de datos, generar carteles, etc. Pero sin redirigir a Pignus no se va a poder utilizar las acciones que sean privadas en un servidor. | | successAuth | Si | Este metodo se invoca cuando se autentica correctamente con pignus. Desde este espacio se pueden ejecutar diferentes tipos de redirecciones, validaciones y demás. |

Interceptor

Por último agregar interceptor, ya que sin agregarlo lo anterior no va a funcionar correctamente. Este es el que llama la ruta /login cuando encuentra un problema de permisos.

@NgModule({
  ...
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: PignusHttpInterceptor,
      multi: true
    }
  ],
  ....
})
export class AppModule { }

Code scaffolding

Run ng generate component component-name --project angular-pjsj to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module --project angular-pjsj.

Note: Don't forget to add --project angular-pjsj or else it will be added to the default project in your angular.json file.

Build

Run ng build angular-pjsj to build the project. The build artifacts will be stored in the dist/ directory.

Publishing

After building your library with ng build angular-pjsj, go to the dist folder cd dist/angular-pjsj and run npm publish.

Running unit tests

Run ng test angular-pjsj to execute the unit tests via Karma.

Further help

To get more help on the Angular CLI use ng help or go check out the Angular CLI README.