typescript - Angular2, generell overlay-komponent for alle andre komponenter inne i appen


Jeg har følgende to komponenter:

overlegg

@Component({
    selector: 'overlay',
    template: '<div class="check"><ng-content></ng-content></div>'
})
export class Overlay {
    save(params) {
        //bunch of stuff that are commonly used
    }
}

myComponent (det finnes mange flere som dette)

@Component({
    selector: 'myComponent',
    directives: [Overlay],
    template: '<overlay><form (ngSubmit)="save({ name: 'sam', lastName: 'jones' })">I'm the component. Click <input type="submit">Here</input> to save me!</form></overlay>'
})
export class MyComponent {

}

Denne løsningen fungerer ikke uten at det kastes noen feil, Angular2 hopper rett og slett over initialiseringen av begge komponentene. Uansett, jeg tror du forstår poenget her. Det finnes en felles komponent som vil trenge å omslutte mange andre komponenter, derfor må den være generell. Den har et template, så tjenester gjør ikke jobben. Jeg har ikke jobbet mye med tilpassede annotasjoner, kanskje de kan gjøre noe slikt? Noen tanker om hvordan man kan oppnå en slik funksjonalitet?

Merk: overlegget inneholder både logikk og mal, som begge er nødvendige for enhver annen komponent som trenger å bruke det. Malen er en kompleks modaldialog med animasjoner, meldinger, fading osv. som er vanlig i hele løsningen.

Oppdatering

Fant dette: Angular2: fremdrifts-/lastingsoverlegg-direktiv

Svar #1

Jeg laget en for denne spesifikke oppgaven og vurderte å bruke Bootstrap-modalen som en mal, men støtte på et par problemer, for eksempel at den eneste nyttige delen av Bootstrap-modalene ville være klasse-navnene. Ikke overraskende ønsket jeg ikke det for denne spesifikke oppgaven siden jeg trengte å skille mellom vindu-modaler og fremdriftsmodaler på komponenter.

Overlegg

@Component({
    selector: 'overlay',
    template:
    `<div [ngClass]="isOpen ? 'opened' : 'closed'">
         <div class="modal" role="dialog">
            <div class="modalBody">
                <div *ngIf="isSaving">
                    <span class="text-success text-bold">
                        Saving...
                    </span>
                </div>
                <div *ngIf="isSaved">
                    <span class="text-success text-bold">
                        Saved.
                    </span>
                </div>
            </div>
        </div>
    </div>`,
    styles: [
        '.modal { position:absolute; width: 100%; height: 100%; margin: -30px; background-color: rgba(255, 255, 255, 0.7); z-index: 1000; text-align: center; }',
        '.closed { visibility: hidden; }',
        '.opened { visibility: visible; }',
        '.modalBody { top: 45%; left: 25%; width: 50%; position: absolute; }',
        '.text-bold { font-weight: 800; font-size: 1.5em; }'
    ]
})
export class Overlay implements OnChanges, AfterContentInit {
    @Input() isSaving: boolean = false;
    @Input() isSaved: boolean = false;
    @Input() containerElement: HTMLElement;

    isOpen = false;

    private modalElement;

    constructor(private element: ElementRef, private animationBuilder: AnimationBuilder) { }

    ngOnChanges() {
        if (this.modalElement) {
            if (this.isSaving == true || this.isSaved == true) {
                this.toggleAnimation(true);
            }
            else if (this.isSaving == false && this.isSaved == false) {
                this.toggleAnimation(false);
            }
        }
    }

    ngAfterContentInit() {
        this.containerElement.style.position = 'relative';
        this.modalElement = this.element.nativeElement.querySelector('.modal');
    }

    private toggleAnimation(isOpen) {
        var startCss = { backgroundColor: 'rgba(255, 255, 255, 0)' };
        var endCss = { backgroundColor: 'rgba(255, 255, 255, 0.7)' };

        if (isOpen) {
            this.isOpen = true

            this.animation(
                true,
                this.modalElement,
                400,
                startCss,
                endCss,
                null
            );
        }
        else {
            this.animation(
                isOpen,
                this.modalElement,
                400,
                startCss,
                endCss,
                () => {
                    this.isOpen = false;
                }
            );
        }
    }

    private animation(isStart, element, duration, startCss, endCss, finishedCallback) {
        var animation = this.animationBuilder.css();

        animation.setDuration(duration);

        if (isStart) {
            animation.setFromStyles(startCss).setToStyles(endCss);
        } else {
            animation.setFromStyles(endCss).setToStyles(startCss)
        }

        animation.start(element);

        if (finishedCallback) {
            setTimeout(finishedCallback, duration);
        }
    }
}

Bruk

Som det er, vil du trenge en relativ beholder for at overlay skal fylle foreldreelementet sitt. CSS-en må definitivt endres for å tilpasses noen scenarier som mobiltelefoner og ikke-posisjonerte beholdere. Slik brukes det for øyeblikket:

HTML

<form action="/edit" method="post" #myForm="ngForm" (ngSubmit)="save ajax method that will update the isSaving and isSaved accordingly" novalidate>
    <div style="position: relative;" #overlayContainer>
        <overlay [isSaving]="isSaving" [isSaved]="isSaved" [containerElement]="overlayContainer"></overlay>
    </div>
</form>

Etter å ha lagret form, vises et overlay innenfor containerElement i 400 ms, og blir deretter tonet ut og skjult inntil neste lagringsforsøk. Bindingverdiene isSaving og isSaved er ansvaret til enhver komponent som ønsker å bruke overlay.

Svar #2

Ok, kanskje dette ikke er et direkte svar på spørsmålet ditt, men ut fra kommentaren din antar jeg at du vil implementere modaler i appen din. Jeg har ikke gjort det ennå, men i nær fremtid vil jeg gjøre det (dette er en del av kravene til appen min).

Først bruker jeg ikke bootstrap javascript fordi jeg allerede bruker angular. Det eneste du trenger er bootstrap css.

vurder nedenstående rotenmal:

 <my-app></my-app>

 <my-modal>
    <div class="modal-container">
       <div class="modal-content">{{modalContent}}</div>
    </div>
 </my-modal>

vis din modal: Global hendelse

Du kan vise modalen din fra hvilken som helst component. Trikset er å injisere en modal-service for å sende en global hendelse. Se dette svaret for å se hvordan det fungerer.

Du kan sende hvilket som helst objekt som event. Dette event-objektet kan inneholde innholdet i modalen din eller en key slik at modal-component kan hente det fra et annet sted.

ModalComponent

Din ModalComponent bør abonnere på den hendelsen. Ved hendelsen kan du vise og skjule den (modalkomponenten) på flere måter som jeg er sikker på at du er klar over. Å bruke strukturelle direktiver er en måte.

Stil

En modal er en container (.modal-container) med width og height som viewport med transparent bakgrunn. Modalens innhold (.modal-content) er en annen container med fast width og height og absolute posisjon. Hvis du vil ha Bootstrap-stiler kan du legge det til, og for animasjoner kan du bruke Angular-animajsoner.

Jeg bruker for tiden en lignende tilnærming for menylinjen, og det fungerer veldig bra!!

Relaterte artikler