ejemplo - Cómo hacer componentes sensibles en Angular2.
page title angular 4 (2)
Estoy entrando en Angular2. Mi objetivo es crear una aplicación receptiva que cargue diferentes componentes en respuesta a diferentes consultas de medios para anchos de dispositivos. Mi ejemplo de trabajo tiene un MatchMediaService:
import { Injectable } from ''@angular/core'';
@Injectable()
export class MatchMediaService
{
constructor()
{
}
rules =
{
print: "print",
screen: "screen",
phone: ''(max-width: 767px)'',
tablet: ''(min-width: 768px) and (max-width: 1024px)'',
desktop: ''(min-width: 1025px)'',
portrait: ''(orientation: portrait)'',
landscape: ''(orientation: landscape)'',
retina: ''(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)''
};
Check = function (mq)
{
if (!mq)
{
return;
}
return window.matchMedia(mq).matches;
};
/**********************************************
METHODS FOR CHECKING TYPE
**********************************************/
IsPhone()
{
return window.matchMedia(this.rules.phone).matches;
};
IsTablet = function ()
{
return window.matchMedia(this.rules.tablet).matches;
};
IsDesktop = function ()
{
return window.matchMedia(this.rules.desktop).matches;
};
IsPortrait = function ()
{
return window.matchMedia(this.rules.portrait).matches;
};
IsLandscape = function ()
{
return window.matchMedia(this.rules.landscape).matches;
};
IsRetina = function ()
{
return window.matchMedia(this.rules.retina).matches;
};
/**********************************************
EVENT LISTENERS BY TYPE
**********************************************/
OnPhone(callBack)
{
if (typeof callBack === ''function'')
{
var mql: MediaQueryList = window.matchMedia(this.rules.phone);
mql.addListener((mql: MediaQueryList) =>
{
if (mql.matches)
{
callBack(mql);
}
});
}
};
OnTablet(callBack)
{
if (typeof callBack === ''function'')
{
var mql: MediaQueryList = window.matchMedia(this.rules.tablet);
mql.addListener((mql: MediaQueryList) =>
{
if (mql.matches)
{
callBack(mql);
}
});
}
};
OnDesktop(callBack)
{
if (typeof callBack === ''function'')
{
var mql: MediaQueryList = window.matchMedia(this.rules.desktop);
mql.addListener((mql: MediaQueryList) =>
{
if (mql.matches)
{
callBack(mql);
}
});
}
};
OnPortrait(callBack)
{
if (typeof callBack === ''function'')
{
var mql: MediaQueryList = window.matchMedia(this.rules.portrait);
mql.addListener((mql: MediaQueryList) =>
{
if (mql.matches)
{
callBack(mql);
}
});
}
};
OnLandscape(callBack)
{
if (typeof callBack === ''function'')
{
var mql: MediaQueryList = window.matchMedia(this.rules.landscape);
mql.addListener((mql: MediaQueryList) =>
{
if (mql.matches)
{
callBack(mql);
}
});
}
};
}
Luego, dentro de un componente ''principal'' (HomeComponent), uso MatchMediaService para determinar qué componente secundario (HomeMobileComponent o HomeDesktopComponent) se carga según lo que MatchMediaService devuelve, así como los eventos de escucha que se activan cuando el navegador cambia de tamaño a través de diferentes dimensiones:
import { Component, OnInit, NgZone } from ''@angular/core'';
import { MatchMediaService } from ''../shared/services/match-media.service'';
import { HomeMobileComponent } from ''./home-mobile.component'';
import { HomeDesktopComponent } from ''./home-desktop.component'';
@Component({
moduleId: module.id,
selector: ''home.component'',
templateUrl: ''home.component.html'',
providers: [ MatchMediaService ],
directives: [ HomeMobileComponent, HomeDesktopComponent ]
})
export class HomeComponent implements OnInit
{
IsMobile: Boolean = false;
IsDesktop: Boolean = false;
constructor(
private matchMediaService: MatchMediaService,
private zone: NgZone
)
{
//GET INITIAL VALUE BASED ON DEVICE WIDTHS AT TIME THE APP RENDERS
this.IsMobile = (this.matchMediaService.IsPhone() || this.matchMediaService.IsTablet());
this.IsDesktop = (this.matchMediaService.IsDesktop());
var that = this;
/*---------------------------------------------------
TAP INTO LISTENERS FOR WHEN DEVICE WIDTH CHANGES
---------------------------------------------------*/
this.matchMediaService.OnPhone(
function (mediaQueryList: MediaQueryList)
{
that.ShowMobile();
}
);
this.matchMediaService.OnTablet(
function (mediaQueryList: MediaQueryList)
{
that.ShowMobile();
}
);
this.matchMediaService.OnDesktop(
function (mediaQueryList: MediaQueryList)
{
that.ShowDesktop();
}
);
}
ngOnInit()
{
}
ShowMobile()
{
this.zone.run(() =>
{ // Change the property within the zone, CD will run after
this.IsMobile = true;
this.IsDesktop = false;
});
}
ShowDesktop()
{
this.zone.run(() =>
{ // Change the property within the zone, CD will run after
this.IsMobile = false;
this.IsDesktop = true;
});
}
}
<home-mobile *ngIf="(IsMobile)"></home-mobile>
<home-desktop *ngIf="(IsDesktop)"></home-desktop>
Este enfoque funciona. Puedo cargar el componente apropiado en respuesta al dispositivo. Me permite personalizar un componente (contenido, estilo, funcionalidad, etc.) para el dispositivo, permitiendo así la mejor experiencia de usuario. Esto también me brinda la capacidad de dirigir diferentes componentes para dispositivos móviles, tabletas y equipos de escritorio (aunque en este caso solo me estoy enfocando en dispositivos móviles y equipos de escritorio).
¿Hay una mejor manera de hacer esto? La desventaja es que estoy obligando a que cada componente comprenda el componente principal para determinar a través de MatchMediaService qué componente secundario cargar. ¿Será escalable para funcionar en una aplicación de nivel de producción en toda regla? Estoy muy interesado en sus comentarios sobre un mejor enfoque, o si este enfoque es aceptable y escalable para una aplicación de producción completa. Gracias por sus comentarios.
¿No podría evitar toda esa lógica enrutando a módulos cargados perezosamente? móvil, de escritorio, lo que sea, al hacer que app.component navegue a la ruta del módulo correspondiente en base a navigator.userAgent? Desde https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
Recomendamos buscar la cadena “Mobi” en cualquier parte del Agente de usuario para detectar un dispositivo móvil.
Puede crear una directiva estructural personalizada que tenga en cuenta los medios *ngIf
o *ngSwitch
para hacerlo menos repetitivo.
https://angular.io/docs/ts/latest/guide/structural-directives.html