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';
html, body { height: 100%; }
body { margin: 0; }
.title {
font-size: 24px;
font-weight: 100;
}
.icontitle {
margin-right: 32px !important;
}
.iconbtn {
margin-right: 16px;
}
.appmenu {
position: absolute;
overflow: auto;
}
.sidefooter {
height: 60px;
}
.appmenu::-webkit-scrollbar {
width: 4px;
}
/* .appmenu::-webkit-scrollbar-track {
background-color: white;
} */
.appmenu::-webkit-scrollbar-thumb {
background: rgb(235, 235, 235);
}
.appmenu::-webkit-scrollbar-thumb:hover {
background: #a5a5a5;
}
/*XL*/
@media screen and (min-width: 1920px) and (max-width: 4999.98px) {
.container {
width: 95%;
margin: 32px auto;
}
.bigfont {
font-size: 18px;
font-weight: 100;
line-height: 1.8;
}
}
/*LG*/
@media screen and (min-width: 1280px) and (max-width: 1919.98px) {
.container {
width: 95%;
margin: 32px auto;
}
.bigfont {
font-size: 18px;
font-weight: 100;
line-height: 1.8;
}
}
/*MD*/
@media screen and (min-width: 960px) and (max-width: 1279.98px) {
.container {
width: 90%;
margin: 24px auto;
}
.bigfont {
font-size: 16px;
font-weight: 100;
line-height: 1.5;
}
}
/*SM*/
@media screen and (min-width: 600px) and (max-width: 959.98px) {
.container {
width: 90%;
margin: 18px auto;
}
.bigfont {
font-size: 14px;
font-weight: 100;
line-height: 1.5;
}
}
/*XS*/
@media screen and (min-width: 0px) and (max-width: 599.98px) {
.container {
width: 90%;
margin: 18px auto;
}
.title {
font-size: 16px;
font-weight: 100;
width: 120px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.icontitle {
margin-right: 16px !important;
}
.bigfont {
font-size: 14px;
font-weight: 100;
line-height: 1.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 { trigger, state, style, transition, animate } 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 { trigger, state, style, transition, animate, AnimationTriggerMetadata } 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 { trigger, state, style, transition, animate } 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 { Component, OnInit } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { eSideNav, eSideContent } 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();
isOpen: boolean | undefined;
isSmall: boolean | undefined;
isBackDrop: boolean | undefined;
isState: string | undefined;
topGap: string | undefined;
showhide: boolean | undefined;
constructor(public bpo: BreakpointObserver) {
// 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.
Comentarios
Publicar un comentario