typescript - Flera sidor med samma struktur i Angular


Jag är relativt ny på Angular och jag har svårt att skapa samma beteende för flera sidor med en gemensam modul.

Här är situationen: Jag skapar en CRUD SPA med flera "sidor" (med routing). Det visar sig att flera av dessa sidor delar samma struktur och funktioner, och skiljer sig endast (visuellt) i hur objekten renderas i en lista och dess detaljer när de är valda. Tänk dig flera sidor av Asana, där den enda skillnaden mellan sidorna är hur listan och detaljerna renderas.

Jag tänkte skapa en modul för var och en av dessa sidor, men det verkade tråkigt att kopiera och klistra in all kod bara för att ändra en liten del av filerna (att uppdatera skulle också vara en mardröm).

Lösningen jag försöker uppnå är att skapa en huvud-CRUD-modul, med en komponent/html/css där jag på något sätt kan parametrera vilken renderer som ska användas för varje sida. Eller någon annan lösning som har så lite kopiera-klistra som möjligt.

Tack för hjälpen.

Svar #1

Jag läste om verktyget och några andra saker som dynamiskt skapade komponenter, men ingen var exakt vad jag ville ha (speciellt med tanke på inlärningskurvorna). Så det tog mig lite tid, men jag hittade en (ganska galen) lösning på egen hand.

Först skapade jag en Basmodul. Inom den skapade jag en PageComponent som anropar de två delarna av sidorna som jag behövde: en för listan och en annan för detaljerna (som Asana). page.component.html-mallen ser ut så här:

<div class="mdl-grid">
    <div class="mdl-cell mdl-cell--8-col">
        <app-list></app-list>
    </div>
    <div class="mdl-cell mdl-cell--4-col">
        <app-details></app-details>
    </div>
</div>`

I denna basmodul skapade jag en abstrakt basklass för varje komponent som jag behövde (en AbstractListComponent och AbstractDetailsComponent). Jag hade vissa problem med beroendeinjektion och automatisk variabelskapande med accessordet i konstruktorn, så min lösning var följande:

export abstract class AbstractDetailsComponent {
    protected service: BaseService;

    protected constructor(service: BaseService) {
        this.service = service;
    }
}

Observera att jag inte använde @Component-dekorationen eftersom den kommer att ignoreras (även i PageComponent). En annan sak var att vissa Base*-klasser och gränssnitt skapades för att säkerställa konsekvensen (som BaseService).

Som du kan se kommer ingen att svara när PageComponent begär <app-list> och <app-details>. Här börjar den galna delen.

För varje sida skapade jag en modul och inom den komponenter som fyllde luckorna. Dessutom utökar huvudsakliga komponenten för sidan PageComponent. Till exempel, i min app har jag en Skills-sida, som leder till följande SkillsComponent:

@Component({
    selector: 'skills-page', // doesn't really matter, this will be called by the router
    templateUrl: '../base-module/page.component.html',
    styleUrls: [ '../base-module/page.component.css', './skills.component.css' ]
})
export class SkillsComponent extends PageComponent { }

Observera att templateUrl och en av stilarna pekar på filer inuti baskomponenten. Detta var sättet jag hittade för att dela samma sidstruktur mellan alla sidor.

Nu, luckorna. Båda komponenterna som jag behövde är liknande. I princip utökar jag basklassen för varje och injicerar rätt tjänst. Här är SkillDetailsComponent:

@Component({
    selector: 'app-details', // <-- the name required by the PageComponent template
    templateUrl: './skill-details.component.html',
    styleUrls: [ '../base-module/details.component.css', './skills-details.component.css' ]
})
export class SkillsDetailsComponent extends DetailsComponent {
    public constructor(service: SkillsService) {
        super(service);
    }
}

Selectorn selector är exakt den som krävs av page.component.html. Alla list- och detaljkomponenter för alla sidor delar samma tillvägagångssätt. Detta gör att samma mall är användbar på alla sidor, eftersom selectorn är densamma.

En annan sak är konstruktören. Jag var tvungen att göra den på det här sättet för att injicera rätt tjänst.

Den slutliga filstrukturen ser ut så här:

app/
    ├ base-module/
    │    ├ base.component.css
    │    ├ base.component.html
    │    ├ base.component.ts
    │    ├ base.module.ts
    │    ├ details.component.css
    │    ├ details.component.ts
    │    ├ list.component.css
    │    ├ list.component.html
    │    └ list.component.ts
    └ skills-module/
        ├ skills-details.component.css
        ├ skills-details.component.ts
        ├ skills-list.component.css
        ├ skills-list.component.ts
        ├ skills.component.css
        ├ skills.component.html
        ├ skills.component.ts
        └ skills.module.ts

Jag vet inte om det är den bästa lösningen, men det fungerar som jag behövde. Det var lite svårt att förklara alla steg jag har tagit (vanligtvis är detta ett tecken på en för komplicerad lösning) men jag hoppas att det är förståeligt.

Svar #2

Min gissning är att du använder ng-route. Försök kolla in ui-router https://github.com/angular-ui/ui-router

UI-Router är den de facto standard klient-sidiga routing-ramverket för AngularJS som stöder inbäddade vyer. Konceptet bakom UI-Router är att modellera vyer som ett hierarkiskt träd av tillstånd.

Relaterade artiklar