Ir al contenido principal

Angular Admin Panel Con Flex Layout

 

También sirve para una página de documentación o cualquier otro sitio técnico.

 


Crear un proyecto nuevo

Creamos un proyecto nuevo (ng new {nombre del proyecto}) seleccionando siguientes opciones.

Al abrir el proyecto en Visual Code, podemos borrar todo el contenido de componente app.componet.html, y al siguiente abrimos el terminal e introducimos los siguientes comandos.

1.       ng add @angular/material - siguiendo las instrucciones

2.       npm i -s @angular/flex-layout

y para que podemos aprovechar todos los beneficios en app.module.ts importar dicho modulo y unos cuantos más, que aprovechamos para construir la pagina.

import { BrowserModule } from '@angular/platform-browser';

import { NgModule } from '@angular/core';

 

import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { FlexLayoutModule } from '@angular/flex-layout';

import { MatSidenavModule } from '@angular/material/sidenav';

import { MatToolbarModule } from '@angular/material/toolbar';

import { MatButtonModule } from '@angular/material/button';

import { MatMenuModule } from '@angular/material/menu';

import { MatListModule } from '@angular/material/list';

import { MatIconModule } from '@angular/material/icon';

import { MatCardModule } from '@angular/material/card';

 

@NgModule({

  declarations: [

    AppComponent,

  ],

  imports: [

    BrowserModule,

    AppRoutingModule,

    BrowserAnimationsModule,

    FlexLayoutModule,

    MatSidenavModule,

    MatToolbarModule,

    MatButtonModule,

    MatMenuModule,

    MatListModule,

    MatIconModule,

    MatCardModule

  ],

  providers: [],

  bootstrap: [AppComponent]

})

export class AppModule { }

 

 

Todos los módulos necesarios utilizados en la aplicación siempre importamos al archivo app.module.ts

Preparar la pantalla del navegador

El principal elemento de la página será SideNav sobre cual se puede leer más bajo este enlace: https://material.angular.io/components/sidenav/overview

Tanto como panel de navegación y panel de contenido se encuentran en un componente, que puede ser:

1.       Mat-Drawer-Container

2.       Mat-Sidenav-Container

 tenemos que definir la pantalla para que todo lo que ocurre está definido dentro de este espacio.

Esto podemos hacer en dos maneras, una es por medio de una clase de CSS o utilizando Flex-Layout.

Primero CSS

Creamos un div y añadimos una clase con nombre screen-container


En el archivo CSS creamos esta clase y añadimos los siguientes parámetros.



El parámetro background-color es solamente para visualizar que toda la pantalla está cubierta.

Para probar escribe ng serve -o en el terminal y vea el resultado en http://localhost:4200

Y la otra manera con Flex-Layout será siguiente.



El resultado será igual que el anterior.



Antes de añadir los componentes preparamos unos archivos con animaciones que van controlar el comportamiento de lo componentes y un archivo css para buena visualización.

 

Archivo CSS

Par comprobar el comportamiento fácilmente he importado archivo de @angular/material a la hoja de estilos, en esta manera cambiando el tema podemos visualizar rápidamente los cambios sin necesidad de depurar.

@import '../node_modules/@angular/material/prebuilt-themes/indigo-pink.css';

htmlbody { height100%; }

body { margin0; }

 

.title {

    font-size24px;

    font-weight100;

}

 

.icontitle {

    margin-right32px !important;

}

 

.iconbtn {

    margin-right16px;

}

 

.appmenu {

    positionabsolute

    overflowauto

}

 

.sidefooter {

    height60px;

}

 

.appmenu::-webkit-scrollbar {

    width4px;

  }

 

/* .appmenu::-webkit-scrollbar-track {

    background-color: white;    

  }   */

 

.appmenu::-webkit-scrollbar-thumb {

    backgroundrgb(235235235);

  }

 

.appmenu::-webkit-scrollbar-thumb:hover {

    background#a5a5a5

  }

 

/*XL*/

@media screen and (min-width1920px) and (max-width4999.98px) {

    .container {

        width95%;

        margin32px auto;

    }

    .bigfont {

        font-size18px;

        font-weight100;

        line-height1.8;

    }   

}

 

/*LG*/

@media screen and (min-width1280px) and (max-width1919.98px) {

    .container {

        width95%;

        margin32px auto;

    }

    .bigfont {

        font-size18px;

        font-weight100;

        line-height1.8;

    }

}

 

/*MD*/

@media screen and (min-width960px) and (max-width1279.98px) {

    .container {

        width90%;

        margin24px auto;

    }

    .bigfont {

        font-size16px;

        font-weight100;

        line-height1.5;

    }

}

 

/*SM*/

@media screen and (min-width600px) and (max-width959.98px) {

    .container {

        width90%;

        margin18px auto;

    }

    .bigfont {

        font-size14px;

        font-weight100;

        line-height1.5;

    }

}

 

/*XS*/

@media screen and (min-width0px) and (max-width599.98px) {

    .container {

        width90%;

        margin18px auto;

    }

    .title {

        font-size16px;

        font-weight100;

        width120px;

        overflowhidden;

        white-spacenowrap;

        text-overflowellipsis;

    }

    .icontitle {

        margin-right16px !important;

    }

    .bigfont {

        font-size14px;

        font-weight100;

        line-height1.5;

    }

}

 

Animaciones

Ahora creamos una carpeta con el nombre animaciones y dentro creamos un archivo con el nombre SideNav.ts. Dentro ponemos dos constantes de animación con nombre SideNav, que será responsable del comportamiento de Sidenav y otro SideContent que será responsable del comportamiento de SideContent.

SideNav, SideContent

import { triggerstatestyletransitionanimate } from '@angular/animations';

export const eSideNav = trigger('eSideNav', [

    state('close',

        style({

            width: '0px',

        })

    ),

    state('middle',

        style({

            width: '58px',

        })

    ),

    state('open',

        style({

            minWidth: '260px',

        })

    ),

    transition('close => open'animate('300ms ease-in')),

    transition('open => close'animate('300ms ease-in')),

    transition('open => middle'animate('300ms ease-in')),

    transition('middle => open'animate('300ms ease-in')),

]);

 

export const eSideContent = trigger('eSideContent', [

    state('close',

        style({

            marginLeft: '0px',

        })

    ),

    state('middle',

        style({

            marginLeft: '58px',

        })

    ),

    state('open',

        style({

            marginLeft: '260px',

        })

    ),

    transition('close => open'animate('300ms ease-in')),

    transition('open => close'animate('300ms ease-in')),

    transition('open => middle'animate('300ms ease-in')),

    transition('middle => open'animate('300ms ease-in')),

]);

 

Como podéis ver hay tres estados – close, middle y open. Esto se debe que Sidenav va tener esto tres estados, para que en las pantallas grandes y medianas podemos esconder parcialmente Sidenav con su lista de navegación.

ShowHide

Responsable para demonstrar o ocultar algunos elementos depende de tamaño de la pantalla.

import { triggerstatestyletransitionanimateAnimationTriggerMetadata } from '@angular/animations';

export const eShowHide = trigger('eShowHide', [

    state('show'style({

        opacity: '1',

        width: '180px'

    })),

    state('hide'style({

        opacity: '0',

        width: '0px'

    })),

    transition('hide => show', [

        animate('800ms ease-in-out')

    ]),

    transition('show => hide', [

        animate('800ms ease-in-out')

    ]),

]);

 

Rotation

Y ultima animación para cambiar la posición de un botón en el momento de abrir o cerrar Sidenav.

import { triggerstatestyletransitionanimate } from '@angular/animations';

 

export const eRotation = trigger('eRotation', [

    state('open'style({

        transform: 'rotate(0deg)',

    })),

    state('close'style({

        transform: 'rotate(180deg)',

    })),

    transition('hide => show', [

        animate('300ms')

    ]),

    transition('show => hide', [

        animate('300ms')

    ]),

])

 

 Revelación

Para que todo tiene sentido y seguir una pauta ahora preparamos todo lo necesario en archivo app.component.ts, los variables, diseño y un método.

app.component.ts

Archivo con el código para controlar el comportamiento de todos los elementos y componentes de la página es el siguiente.

En primer lugar, importamos las animaciones y en secundo BreakpointObserver y Breakpoints, para controlar el diseño de la página.

BreakpointObserver nos proporciona utilidades a la hora de reaccionar cunado el tamaño de la pantalla cambia y Breakpoints contiene todas las opciones de tamaño de las pantallas, así conjunto podemos crear diferentes opciones para cada tipo de pantalla.

En este ejemplo he dividido BreakpointObserver en tres agrupados con diferentes tamaños, el cuatro que esta bajo XS SM es solamente para controlar el margen arriba, que está representado por la variable topGap, de Scrollbar en caso si el teléfono está en posición vertical o horizontal para mejor experiencia del diseño.

Las variables

Como sidenav en total va tener tres estados lo principal es posición abierta o cerrada y para esto añadimos una variable ‘isOpen’ que va controlar el estado de Sidenav, en caso si Sidenav esta abierto tendremos otras dos opciones que sacamos de la animación eSideNav, las posiciones ‘open’ y ‘middle’.

isSmall nos indica si la pantalla esta pequeña o grande que es este caso definimos que los teléfonos son pequeños y todo desde Tablet son grandes y conjunto con las variables isOpen y isState controlaran cada posición de los elementos en la pantalla.

isState recoge los triggers de las animaciones para poner en el sitio adecuado los elementos de la página.

isBackDrop es responsable en caso cuando la pantalla es pequeña sidenav se abre por encima añade un fondo para cubrir la pagina por de bajo.

showhide para mostrar u ocultar elementos.

Breakpoints

En el contructor creamos opciones para diferentes tipos de las pantallas

Tal como podéis ver cada grupo de los tamaños de la pantallas tiene asignados diferentes opciones para las variables.

import { ComponentOnInit } from '@angular/core';

import { BreakpointObserverBreakpoints } from '@angular/cdk/layout';

import { eSideNaveSideContent } from './animation/eSideNav';

import { eShowHide } from './animation/eShowHide';

import { eRotation } from './animation/eRotation';

 

@Component({

  selector: 'app-root',

  templateUrl: './app.component.html',

  styleUrls: ['./app.component.css'],

  animations: [

    eShowHide,

    eSideNav,

    eSideContent,

    eRotation

  ],

})

export class AppComponent implements OnInit {

  title = 'Angular Admin Panel con flex-layout';

  menulist = new Array();

  contentlist = new Array();

 

  isOpenboolean | undefined;

  isSmallboolean | undefined;

  isBackDropboolean | undefined;

  isStatestring | undefined;

  topGapstring | undefined;

  showhideboolean | undefined;

 

  constructor(public bpoBreakpointObserver) {

    // XS SM

    this.bpo.observe([

      Breakpoints.XSmall,

      Breakpoints.HandsetPortrait

    ]).subscribe(small => {

      if (small.matches === true) {

        this.isOpen = false;

        this.isSmall = true;

        this.isBackDrop = true;

        this.isState = 'close';

        this.showhide = true;

        this.topGap = '60';

      }

    });

    this.bpo.observe([

      Breakpoints.HandsetLandscape

    ]).subscribe(small => {

      if (small.matches === true) {

        this.isOpen = false;

        this.isSmall = true;

        this.isBackDrop = true;

        this.isState = 'close';

        this.showhide = true;

        this.topGap = '68';

      }

    });

    // SM

    bpo.observe([

      Breakpoints.TabletPortrait,

      Breakpoints.WebPortrait,

    ]).subscribe(web => {

      if (web.matches === true) {

        this.isOpen = true;

        this.isSmall = false;

        this.isBackDrop = false;

        this.isState = 'middle';

        this.showhide = false;

      }

    });

    // MD LG XL

    this.bpo.observe([

      Breakpoints.WebLandscape,

      Breakpoints.TabletLandscape,

      Breakpoints.XLarge,

    ]).subscribe(large => {

      if (large.matches === true) {

        this.isOpen = true;

        this.isSmall = false;

        this.isBackDrop = false;

        this.isState = 'open';

        this.showhide = true;

      }

    });

  }

 

  toggle(): void {

    this.showhide = !this.showhide;

    setTimeout(() => {

      if (this.isState === 'open') {

        this.isState = 'middle';

      } else {

        this.isState = 'open';

      }

    }, 200);

  }

 

  ngOnInit(): void {

    this.menulist = Array.from({ length: 25 }, (_i=> `Nav Item ${i + 1}`);

    this.contentlist = Array.from({ length: 15 }, () => 'Un texto más largo que este…');

  }

}

 

Si has llegado hasta aquí lo ultimo para que podemos comprobar nuestro diseño es añadir algún menú y contenido, para esto hemos creado dos array, tal como se ve arriba en método ngOnInit.

app.component.html

Y aquí el resultado final, la única en este apartado será por [style.zIndex], si hemos añadido la sombra en toolbar en configuración por defecto no se va ver por esto si la pantalla es grande ponemos zIndex a 2, o más y en caso de pantallas pequeñas para que no salga por encima del menú zIndex ponemos a 0. Lo de más recomiendo analizar al introducir el código al programa.

Ver todas las variables como interactúan con los elementos y los parámetros de un componente.

<div fxLayout="column" fxFill>

    <!-- Toolbar -->

    <mat-toolbar class="mat-elevation-z3" [style.zIndex]="isSmall ? '0' : '2'">

        <button mat-icon-button class="icontitle" (click)="sidenav.toggle()">

            <mat-icon>menu</mat-icon>

        </button>

        <span class="title">{{title}}</span>

        <div fxFlex></div>

        <div>

            <button mat-button>

                <mat-icon class="iconbtn">login</mat-icon>

                <span *ngIf="!isSmall">Login</span>

            </button>

            <button mat-button>

                <mat-icon class="iconbtn">person_add</mat-icon>

                <span *ngIf="!isSmall">Register</span>

            </button>

        </div>

    </mat-toolbar>

    <!-- Side Nav Container -->

    <mat-sidenav-container [hasBackdrop]="isBackDrop" fxFlex>

        <mat-sidenav #sidenav [opened]="isOpen" [mode]="!isSmall ? 'side' : 'over'"

            [disableClose]="isSmall ? false : true" [fixedInViewport]="isSmall ? true : false"

            [@eSideNav]="isSmall ? 'open' : isState" style="overflow: hidden;">

            <mat-toolbar [fxLayoutAlign]="showhide ? 'space-between center' : 'center center'">

                <div fxLayout="row" fxLayoutAlign="center center">

                    <mat-icon [style.marginRight.px]="showhide ? 16 : 0">maps_home_work

                    </mat-icon>

                    <h3 *ngIf="showhide">Admin Panel</h3>

                </div>

                <button *ngIf="showhide" mat-icon-button>

                    <mat-icon>settings</mat-icon>

                </button>

            </mat-toolbar>

            <div class="appmenu" [@eSideNav]="isOpen ? isState : 'open'" [style.bottom.px]="isSmall ? '3' : '61'"

                [style.top.px]="isSmall ? topGap : '65'">

                <mat-nav-list>

                    <mat-list-item *ngFor="let m of menulist">

                        <mat-icon class="iconbtn">face</mat-icon>

                        <span matLine [@eShowHide]="showhide ? 'show' : 'hide'">

                            {{m}}

                        </span>

                    </mat-list-item>

                </mat-nav-list>

            </div>

            <div style="position: absolute; bottom: 0px; right: 0px; left: 0px;">

                <mat-toolbar fxLayoutAlign="end center" *ngIf="!isSmall" class="sidefooter">

                    <button mat-icon-button (click)="toggle()" style="margin-right: -6px;">

                        <mat-icon [@eRotation]="showhide ? 'open' : 'close'">chevron_left

                        </mat-icon>

                    </button>

                </mat-toolbar>

            </div>

        </mat-sidenav>

        <mat-sidenav-content [@eSideContent]="isSmall ? 'close' : isState">

            <div class="container bigfont">

                <p *ngFor="let c of contentlist">{{c}}</p>

            </div>

        </mat-sidenav-content>

    </mat-sidenav-container>

</div>

 

Espero les gusto mi tutorial y lo aprovecháis  en vuestros diseños

Aqui les dejo enlaces para ver que realmente funciona.

Proyecto - https://angular-ivy-hqm3wo.stackblitz.io
Editor - https://stackblitz.com/edit/angular-ivy-hqm3wo?file=src/app/app.component.html

Comentarios

Entradas populares de este blog

Angular Chart - Gráficos

Intro La base de este tutorial he encontrado bajo este enlace al busca de información sobro como añadir los gráficos a una aplicación de Angular. https://www.smashingmagazine.com/2020/07/responsive-dashboard-angular-material-ng2-charts-schematics/ También hay informaciones del creador de este componente que se puede encontrar aquí: https://www.npmjs.com/package/ng2-charts Es bastante útil, no obstante, he encontrado unos errores que tenia que modificar y adaptar a la app que estoy creando, tanto por el uso de Flex-Layout y adaptar a (responsive app) en cada aspecto y para cada dispositivo. Si hayas practicado con Angular Admin Panel bajo este enlace: https://angularprojekt.blogspot.com/2021/07/angular-admin-panel-con-flex-layout.html Hoy vamos a añadir unos gráficos, porque gráficos siempre molan y cada panel de administración debería tener unos cuántos. Modificaciones Pero antes de empezar vamos a modificar el proyecto. En primer lugar, vamos a eliminar el conte

Firebase Cloud Messaging en Angular 13

  Hoy vamos a añadir Push messag e en nuestra página con Firebase 9.0.0 y Angular 13 .Si has seguido los tutoriales anteriores por ahora lo dejamos y vamos a crear un proyecto nuevo desde cero. En primer lugar, nos aseguramos tener todos los framework y SDK al día. Utilizando Command Prompt escribimos ng version el resultado debería ser como en la imagen arriba. Si no, hay que instalar Node.js y Angular/Cli. Creamos proyecto nuevo ng new <nombre de proyecto> y abrimos Visual Code con el proyecto recién creado. Para poder trabajar con Firebase primero vamos a crear un proyecto en consola de firebase. Como crear lo puedes leer aquí https://firebase.google.com/docs/projects/learn-more Para comprobar la versión de firebase escribimos en terminal o en Command Prompt comando firebase –version . Si no hay ninguna instalamos con este comando: npm install -g firebase-tools y después en el terminal del proyecto npm install firebase para utilizar las bibliotecas. Preparación. Al crear

Angular Chart – conectamos con los datos

Conectar gráficos con los datos En este articulo conectamos los gráficos con los datos, en forma local y online. Intro Podríamos crear un archivo .json como abajo. "test" : [         {              "dane" : [                 {                      "mdane" : [ 65 ,  59 ,  80 ],                      "label" :  "Product A"                 },                 {                      "mdane" : [ 28 ,  48 ,  40                     ],                      "label" :  "Product B"                 },                 {                      "mdane" : [ 12 ,  28 ,  32 ],                      "label" :  "Product C"                 }             ],              "years" : [ "2018" ,  "2019" ,  "2020"             ]         }     ],   Y conectar el grafico con este simple código a través de HttpClient ge