web-dev-qa-db-fra.com

comment faire des composants sensibles dans Angular2

Je patauge dans Angular2. Mon objectif est de créer une application responsive qui charge différents composants en réponse à différentes requêtes de support pour les largeurs de périphérique. Mon exemple de travail a 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);
                }
            });
        }
    };
}

Ensuite, dans un composant 'parent' (HomeComponent), j'utilise MatchMediaService pour déterminer le composant enfant (HomeMobileComponent ou HomeDesktopComponent) à charger en fonction de ce que MatchMediaService renvoie, ainsi que des événements d'écoute qui se déclenchent lorsque le navigateur est redimensionné:

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>

Cette approche fonctionne. Je peux charger le composant approprié en réponse au périphérique. Cela me permet de personnaliser un composant (contenu, style, fonctionnalité, etc.) sur le périphérique, offrant ainsi la meilleure expérience utilisateur possible. Cela me permet également de cibler différents composants pour Mobile, Tablet et Desktop (bien que je ne me concentre que sur les mobiles et les ordinateurs de bureau par exemple).

Y a-t-il une meilleure manière de faire cela? L'inconvénient est que je force chaque composant à être composé du composant parent pour déterminer via MatchMediaService le composant enfant à charger. Sera-ce évolutif pour fonctionner dans une application de niveau de production complet? Je suis très intéressé par vos commentaires sur une meilleure approche, ou si cette approche est acceptable et évolutive pour une application de production complète. Merci pour vos commentaires.

22
Tom Schreck

Vous pouvez créer une directive structurelle *ngIf ou *ngSwitch personnalisée avec support pour le rendre moins répétitive.

https://angular.io/docs/ts/latest/guide/structural-directives.html

4
Günter Zöchbauer

Ne pourriez-vous pas éviter toute cette logique en routant vers des modules chargés paresseusement, c.-à-d. mobile, ordinateur de bureau, peu importe, en faisant app.component naviguer vers l'itinéraire du module correspondant basé sur navigator.userAgent? De https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent

nous vous recommandons de rechercher la chaîne «Mobi» n'importe où dans l'agent d'utilisateur pour détecter un appareil mobile.

https://embed.plnkr.co/NLbyBEbNoWd9SUW7TJFS/

0
Robert Brower