Come estendere / ereditare i componenti?


160

Vorrei creare estensioni per alcuni componenti già distribuiti in Angular 2, senza doverli riscrivere quasi completamente, poiché il componente di base potrebbe subire modifiche e desiderare che tali modifiche si riflettessero anche nei suoi componenti derivati.

Ho creato questo semplice esempio per cercare di spiegare meglio le mie domande:

Con il seguente componente di base app/base-panel.component.ts:

import {Component, Input} from 'angular2/core';

@Component({
    selector: 'base-panel',
    template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
    styles: [`
    .panel{
    padding: 50px;
  }
  `]
})
export class BasePanelComponent { 

  @Input() content: string;

  color: string = "red";

  onClick(event){
    console.log("Click color: " + this.color);
  }
}

Vorresti creare un altro componente derivato solo alterare, ad esempio, un comportamento di base del componente nel caso del colore di esempio app/my-panel.component.ts:

import {Component} from 'angular2/core';
import {BasePanelComponent} from './base-panel.component'

@Component({
    selector: 'my-panel',
    template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
    styles: [`
    .panel{
    padding: 50px;
  }
  `]
})
export class MyPanelComponent extends BasePanelComponent{

  constructor() {
    super();
    this.color = "blue";
  }
}

Esempio di lavoro completo in Plunker

Nota: ovviamente questo esempio è semplice e potrebbe essere risolto altrimenti non è necessario utilizzare l'ereditarietà, ma è inteso solo per illustrare il vero problema.

Come puoi vedere nell'implementazione del componente derivato app/my-panel.component.ts, gran parte dell'implementazione è stata ripetuta e la singola parte realmente ereditata è stata la class BasePanelComponent, ma @Componentin sostanza ha dovuto essere completamente ripetuta, non solo le parti modificate, come la selector: 'my-panel'.

Esiste un modo per creare un'eredità letteralmente piena di un componente Angular2, ereditando la classdefinizione dei segni / annotazioni, come ad esempio @Component?

Modifica 1 - Richiesta funzionalità

Richiesta di funzione angular2 aggiunta al progetto su GitHub: Estendi / Eredita annotazioni componenti angular2 # 7968

Modifica 2 - Richiesta chiusa

La richiesta è stata chiusa, per questo motivo , che per un breve periodo non saprebbe come unire il decoratore verrà effettuata. Lasciandoci senza opzioni. Quindi la mia opinione è citata nel numero .


Controllare questa risposta stackoverflow.com/questions/36063627/... Saluti
NicolasB

Ok NicolasB. Ma il mio problema è con l'eredità del decoratore @Component, che non si applica ai metadati dell'ereditarietà. = /
Fernando Leal,

persone, per favore evitate di usare l'eredità con angolare. es. la classe di esportazione PlannedFilterComponent estende AbstractFilterComponent implementa OnInit {è molto male. Esistono altri modi per condividere il codice, ad esempio servizi e componenti più piccoli. L'ereditarietà non è la via angolare. Sono su un progetto angolare in cui hanno usato l'ereditarietà e ci sono cose che si rompono come l'esportazione di componenti che ereditano da componenti astratti mancanti di input della classe astratta.
Robert King,

1
usa invece la proiezione dei contenuti, ad es. github.com/angular/components/blob/master/src/material/card/… non usare l'ereditarietà
robert king

Risposte:


39

Soluzione alternativa:

Questa risposta di Thierry Templier è un modo alternativo per aggirare il problema.

Dopo alcune domande con Thierry Templier, sono arrivato al seguente esempio di lavoro che soddisfa le mie aspettative come alternativa alla limitazione dell'eredità menzionata in questa domanda:

1 - Crea decoratore personalizzato:

export function CustomComponent(annotation: any) {
  return function (target: Function) {
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);

    var parentAnnotation = parentAnnotations[0];
    Object.keys(parentAnnotation).forEach(key => {
      if (isPresent(parentAnnotation[key])) {
        // verify is annotation typeof function
        if(typeof annotation[key] === 'function'){
          annotation[key] = annotation[key].call(this, parentAnnotation[key]);
        }else if(
        // force override in annotation base
        !isPresent(annotation[key])
        ){
          annotation[key] = parentAnnotation[key];
        }
      }
    });

    var metadata = new Component(annotation);

    Reflect.defineMetadata('annotations', [ metadata ], target);
  }
}

2 - Componente di base con decoratore @Component:

@Component({
  // create seletor base for test override property
  selector: 'master',
  template: `
    <div>Test</div>
  `
})
export class AbstractComponent {

}

3 - Componente secondario con decoratore @CustomComponent:

@CustomComponent({
  // override property annotation
  //selector: 'sub',
  selector: (parentSelector) => { return parentSelector + 'sub'}
})
export class SubComponent extends AbstractComponent {
  constructor() {
  }
}

Plunkr con l'esempio completo.


3
Presumo che questo non sia compatibile con il compilatore di modelli offline.
Günter Zöchbauer,

@ GünterZöchbauer, non ho alcuna conoscenza del "modello di compilatore offline" di Angular2. Ma penso che potrebbe non essere compatibile e sarebbe un'opzione alternativa. Dove sarebbe utile la modalità "compilatore di modelli offline" di Angular2? Puoi mostrarmi qualcosa per capirlo meglio? Quindi posso capire l'importanza di questa compatibilità per il mio progetto.
Fernando Leal,

Il compilatore di modelli offline (OTC) non è ancora funzionante anche se è già incluso in RC.3. OTC analizzerà i decoratori e genererà il codice durante una fase di compilazione quando viene generato lo schieramento. OTC consente di rimuovere il parser e il compilatore Angular2 che elaborano decoratori e associazioni in fase di esecuzione, il che porta a dimensioni di codice notevolmente inferiori e a un'inizializzazione più rapida dell'applicazione e dei componenti. Probabilmente OTC diventerà utilizzabile con uno dei prossimi aggiornamenti.
Günter Zöchbauer,

1
@ GünterZöchbauer, ora capisco l'importanza di mantenere la funzionalità compatibile con OTC. Sarà una pre-compilation di decoratori angolari che ridurranno le spese generali per inizializzare i componenti. Mi piacerebbe sapere del funzionamento di questo processo e perché la soluzione di questa risposta non sarà compatibile con OTC? Com'è la pre-compilation di decoratori? Avendo questa conoscenza, potremmo pensare a qualcosa per mantenere questa alternativa funzionale all'OTC. Grazie per il chiarimento!
Fernando Leal,

24

Angular 2 versione 2.3 è stata appena rilasciata e include l'ereditarietà dei componenti nativi. Sembra che tu possa ereditare e sovrascrivere quello che vuoi, ad eccezione di modelli e stili. Alcuni riferimenti:


Un "gotcha" qui si verifica quando si dimentica di specificare un nuovo "selettore" nel componente figlio. In More than one component matched on this elementcaso contrario, verrà visualizzato un errore di runtime .
The Aelfinn,

@TheAelfinn Sì: ogni componente deve avere una specifica completa nel @Component()tag. Tuttavia, è possibile condividere .html o .css facendo riferimento allo stesso file, se lo si desidera. Tutto sommato, è un grande vantaggio.
Daniel Griscom,

Nel tuo secondo link scotch.io/tutorials/component-inheritance-in-angular-2 , l'autore afferma che i componenti ereditano i servizi iniettati di dipendenza dei loro genitori, il mio codice suggerisce il contrario. Puoi confermare che questo è supportato?
The Aelfinn,

18

Ora che TypeScript 2.2 supporta i mixin attraverso le espressioni di classe, abbiamo un modo molto migliore di esprimere i mixin sui componenti. Tieni presente che puoi anche utilizzare l'ereditarietà dei componenti dall'angolare 2.3 ( discussione ) o un decoratore personalizzato come discusso in altre risposte qui. Tuttavia, penso che i Mixin abbiano alcune proprietà che li rendono preferibili per il riutilizzo del comportamento tra i componenti:

  • I mixin si compongono in modo più flessibile, ovvero è possibile mescolare e abbinare i mixin sui componenti esistenti o combinare i mixin per formare nuovi componenti
  • La composizione di Mixin rimane facile da capire grazie alla sua ovvia linearizzazione a una gerarchia ereditaria di classe
  • Puoi evitare più facilmente problemi con decoratori e annotazioni che affliggono l'ereditarietà dei componenti ( discussione )

Consiglio vivamente di leggere l'annuncio di TypeScript 2.2 sopra per capire come funzionano i Mixin. Le discussioni collegate su questioni angolari di GitHub forniscono ulteriori dettagli.

Avrai bisogno di questi tipi:

export type Constructor<T> = new (...args: any[]) => T;

export class MixinRoot {
}

E quindi puoi dichiarare un Mixin come questo Destroyablemixin che aiuta i componenti a tenere traccia degli abbonamenti che devono essere disposti in ngOnDestroy:

export function Destroyable<T extends Constructor<{}>>(Base: T) {
  return class Mixin extends Base implements OnDestroy {
    private readonly subscriptions: Subscription[] = [];

    protected registerSubscription(sub: Subscription) {
      this.subscriptions.push(sub);
    }

    public ngOnDestroy() {
      this.subscriptions.forEach(x => x.unsubscribe());
      this.subscriptions.length = 0; // release memory
    }
  };
}

Per mixare Destroyablein a Component, dichiari il tuo componente in questo modo:

export class DashboardComponent extends Destroyable(MixinRoot) 
    implements OnInit, OnDestroy { ... }

Nota che MixinRootè necessario solo quando vuoi extenduna composizione Mixin. Puoi facilmente estendere più mixin ad es A extends B(C(D)). Questa è l'ovvia linearizzazione dei mixin di cui stavo parlando sopra, ad esempio stai componendo effettivamente una gerarchia di ereditarietà A -> B -> C -> D.

In altri casi, ad esempio quando si desidera comporre Mixin in una classe esistente, è possibile applicare il Mixin in questo modo:

const MyClassWithMixin = MyMixin(MyClass);

Tuttavia, ho trovato che il primo modo funziona meglio per Componentse Directives, dato che anche questi devono essere decorati @Componento @Directivecomunque.


Questo e spettacolare! grazie per il suggerimento. MixinRoot viene semplicemente utilizzato come segnaposto di classe vuoto qui? voglio solo assicurarmi che la mia comprensione sia corretta.
Alex Lockwood,

@AlexLockwood sì, il segnaposto di classe vuoto è esattamente quello per cui lo sto usando. Eviterei volentieri di usarlo, ma per ora non ho trovato un modo migliore per farlo.
Johannes Rudolph,

2
Ho finito per usare function Destroyable<T extends Constructor<{}>>(Base = class { } as T). In questo modo posso creare mixin usando extends Destroyable().
Alex Lockwood,

1
Questo sembra molto buono, tuttavia sembra che AoT build (Cli1.3) rimuova ngOnDestroy da DashBoardComponent come non viene mai chiamato. (lo stesso vale per ngOnInit)
dzolnjan il

grazie per questa soluzione. Tuttavia, dopo una generazione di prod con ionic o angular-cli, il mixin non funziona in qualche modo, come se non fosse stato esteso.
Han Che,

16

aggiornare

L'ereditarietà dei componenti è supportata dalla 2.3.0-rc.0

originale

Finora, il più conveniente per me è quello di mantenere template e stili in separata *htmle *.cssfile e specificare chi attraverso templateUrle styleUrls, quindi è facile riutilizzabile.

@Component {
    selector: 'my-panel',
    templateUrl: 'app/components/panel.html', 
    styleUrls: ['app/components/panel.css']
}
export class MyPanelComponent extends BasePanelComponent

2
Questo è esattamente ciò di cui ho bisogno. Come sarebbe il decoratore di @Component per BasePanelComponent? Potrebbe fare riferimento a diversi file HTML / CSS? Potrebbe fare riferimento agli stessi file html / css a cui fa riferimento MyPanelComponent?
ebhh2001,

1
Questo non eredita @Input()e @Output()decoratori, vero?
Leon Adler,

10

Per quanto ne so l'ereditarietà dei componenti non è stata ancora implementata in Angular 2 e non sono sicuro che abbiano dei piani, tuttavia poiché Angular 2 sta usando il dattiloscritto (se hai deciso di seguire quella strada) puoi usare l'ereditarietà delle classi facendo class MyClass extends OtherClass { ... }. Per l'ereditarietà dei componenti suggerirei di essere coinvolto nel progetto Angular 2 andando su https://github.com/angular/angular/issues e inviando una richiesta di funzionalità!


Capito, proverò nei prossimi giorni a ripetermi il progetto angular2 e verificherò che la funzione di richiesta non è più nei problemi del progetto in Git e, in caso contrario, elaborerò una richiesta per la risorsa, poiché mi sembra molto interessante caratteristica. Qualche idea in più per fare la richiesta più interessante?
Fernando Leal,

1
Per quanto riguarda il dattiloscritto della risorsa ereditaria che sto già utilizzando nella mia soluzione iniziale ( export class MyPanelComponent extends BasePanelComponent), il problema è solo nel caso di Annotazioni / Decoratori non ereditate.
Fernando Leal,

1
Sì, davvero non so cos'altro potresti aggiungere. Mi piace l'idea di avere un nuovo decoratore (qualcosa del genere @SubComponent()) che contrassegna una classe come sottocomponente o di avere un campo aggiuntivo sul @Componentdecoratore che ti consenta di fare riferimento a un componente padre da cui ereditare.
Watzon,

1
Richiesta di funzionalità angular2 aggiunta al progetto su GitHub: Estendi / eredita le annotazioni dei componenti angular2 # 7968
Fernando Leal

9

Cerchiamo di comprendere alcune limitazioni e funzionalità chiave sul sistema di ereditarietà dei componenti di Angular.

Il componente eredita solo la logica di classe:

  • Tutti i metadati nel decoratore @Component non vengono ereditati.
  • Le proprietà del componente @Input e le proprietà @Output vengono ereditate.
  • Il ciclo di vita dei componenti non viene ereditato.

Queste caratteristiche sono molto importanti da tenere a mente, quindi esaminiamole ognuna in modo indipendente.

Il componente eredita solo la logica di classe

Quando si eredita un componente, tutta la logica interna viene ugualmente ereditata. Vale la pena notare che solo i membri pubblici vengono ereditati poiché i membri privati ​​sono accessibili solo nella classe che li implementa.

Tutti i metadati nel decoratore @Component non vengono ereditati

Il fatto che non vengano ereditati metadati potrebbe inizialmente sembrare controintuitivo ma, se ci pensate, ha perfettamente senso. Se erediti da un componente dire (componenteA), non vorrai che il selettore del componente A, da cui stai ereditando, sostituisca il selettore del componente B che è la classe che sta ereditando. Lo stesso si può dire per template / templateUrl e style / styleUrls.

Le proprietà Component @Input e @Output sono ereditate

Questa è un'altra caratteristica che adoro davvero dell'ereditarietà dei componenti in angolare. In una frase semplice, ogni volta che si dispone di una proprietà @Input e @Output personalizzata, queste proprietà vengono ereditate.

Il ciclo di vita dei componenti non viene ereditato

Questa parte è quella che non è così ovvia soprattutto per le persone che non hanno ampiamente lavorato con i principi OOP. Ad esempio, supponiamo che tu abbia ComponentA che implementa uno dei tanti hook del ciclo di vita di Angular come OnInit. Se si crea ComponentB e si eredita ComponentA, il ciclo di vita di OnInit da ComponentA non si attiverà finché non lo si chiama esplicitamente anche se si dispone di questo ciclo di vita di OnInit per ComponentB.

Chiamata ai metodi dei componenti Super / Base

Per avere il metodo ngOnInit () dal fuoco ComponentA, dobbiamo usare la super parola chiave e quindi chiamare il metodo di cui abbiamo bisogno che in questo caso è ngOnInit. La super parola chiave si riferisce all'istanza del componente che viene ereditato da cui in questo caso sarà ComponentA.


5

se leggi le librerie CDK e le librerie dei materiali, usano l'ereditarietà ma non tanto per i componenti stessi, la proiezione dei contenuti è re IMO. vedi questo link https://blog.angular-university.io/angular-ng-content/ dove dice "il problema chiave con questo design"

So che questo non risponde alla tua domanda, ma penso davvero che l'ereditarietà / estensione dei componenti debba essere evitata . Ecco il mio ragionamento:

Se la classe astratta estesa da due o più componenti contiene una logica condivisa: utilizzare un servizio o persino creare una nuova classe dattiloscritta che può essere condivisa tra i due componenti.

Se la classe astratta ... contiene variabili condivise o funzioni onClicketc, ci sarà la duplicazione tra l'html delle due viste dei componenti estendibili. Questa è una cattiva pratica e che l'html condiviso deve essere suddiviso in Componenti. Questi componenti (parti) possono essere condivisi tra i due componenti.

Mi mancano altri motivi per avere una classe astratta per i componenti?

Un esempio che ho visto di recente sono stati i componenti che estendono AutoUnsubscribe:

import { Subscription } from 'rxjs';
import { OnDestroy } from '@angular/core';
export abstract class AutoUnsubscribeComponent implements OnDestroy {
  protected infiniteSubscriptions: Array<Subscription>;

  constructor() {
    this.infiniteSubscriptions = [];
  }

  ngOnDestroy() {
    this.infiniteSubscriptions.forEach((subscription) => {
      subscription.unsubscribe();
    });
  }
}

questo era bas perché in una grande base di codice infiniteSubscriptions.push()veniva usato solo 10 volte. Anche l'importazione e l'estensione AutoUnsubscriberichiede in realtà più codice rispetto alla semplice aggiunta mySubscription.unsubscribe()del ngOnDestroy()metodo del componente stesso, che ha richiesto comunque una logica aggiuntiva.


Ok, capisco la tua collocazione e concordo sul fatto che l'aggregazione risolva quasi tutti i problemi che sembrano necessitare di ereditarietà. Ed è sempre interessante pensare ai componenti come piccole parti dell'applicazione che possono essere ancorate in vari modi. Ma nel caso della domanda il problema è che non ho controllo / accesso alle modifiche nel componente che voglio ereditare (è un terzo componente), quindi l'aggregazione diventerebbe irrealizzabile e l'ereditarietà sarebbe la soluzione ideale.
Fernando Leal,

perché non puoi semplicemente creare un nuovo componente che incapsula quel componente di terze parti? Qual è il tuo componente di terze parti fuori interesse? ad es. <my-calendar [stuff] = stuff> <calendario di terze parti [stuff] = stuff> </ ..> </ ..>
robert king

@robertking ripetersi è un modello molto debole ... Ecco perché inizierai a odiare il tuo lavoro .. invece di godertelo.
Dariusz Filipiak,

Per quanto mi riguarda, è una buona idea estendere i componenti nel caso in cui si desideri avere gli stessi parametri Input / Output per un set di componenti, in modo che possano comportarsi come uno. Ad esempio, ho diversi passaggi di registrazione (credentialsStep, addressStep, selectBenefitsStep). Dovrebbero avere tutte le stesse opzioni di Input (stepName, actionButtons ...) e Output (complete, Annulla).
Sergey_T

@Sergey_T potresti prendere in considerazione un componente con selezione di ng e proiezione dei contenuti? Inoltre, ripetere alcuni input non sembra che tu stia davvero risparmiando su molte funzionalità TBH.
Robert King,

2

Se qualcuno è alla ricerca di una soluzione aggiornata, la risposta di Fernando è praticamente perfetta. Tranne che ComponentMetadataè stato deprecato. Usando Componentinvece ha funzionato per me.

Il CustomDecorator.tsfile completo di Decorator personalizzato è simile al seguente:

import 'zone.js';
import 'reflect-metadata';
import { Component } from '@angular/core';
import { isPresent } from "@angular/platform-browser/src/facade/lang";

export function CustomComponent(annotation: any) {
  return function (target: Function) {
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);

    var parentAnnotation = parentAnnotations[0];
    Object.keys(parentAnnotation).forEach(key => {
      if (isPresent(parentAnnotation[key])) {
        // verify is annotation typeof function
        if(typeof annotation[key] === 'function'){
          annotation[key] = annotation[key].call(this, parentAnnotation[key]);
        }else if(
          // force override in annotation base
          !isPresent(annotation[key])
        ){
          annotation[key] = parentAnnotation[key];
        }
      }
    });

    var metadata = new Component(annotation);

    Reflect.defineMetadata('annotations', [ metadata ], target);
  }
}

Quindi importalo nel tuo nuovo sub-component.component.tsfile componente e usa @CustomComponentinvece @Componentcosì:

import { CustomComponent } from './CustomDecorator';
import { AbstractComponent } from 'path/to/file';

...

@CustomComponent({
  selector: 'subcomponent'
})
export class SubComponent extends AbstractComponent {

  constructor() {
    super();
  }

  // Add new logic here!
}

I decoratori personalizzati non sono altamente scoraggiati? Da molti altri post / thread questa soluzione è stata contrassegnata come completamente sbagliata perché AOT non li supporterà?
TerNovi,

2

Puoi ereditare @Input, @Output, @ViewChild, ecc. Guarda l'esempio:

@Component({
    template: ''
})
export class BaseComponent {
    @Input() someInput: any = 'something';

    @Output() someOutput: EventEmitter<void> = new EventEmitter<void>();

}

@Component({
    selector: 'app-derived',
    template: '<div (click)="someOutput.emit()">{{someInput}}</div>',
    providers: [
        { provide: BaseComponent, useExisting: DerivedComponent }
    ]
})
export class DerivedComponent {

}

1

I componenti possono essere estesi come l'eredità di una classe dattiloscritta, solo che devi sovrascrivere il selettore con un nuovo nome. Tutte le proprietà Input () e Output () del componente padre funzionano normalmente

Aggiornare

@Component è un decoratore,

I decoratori vengono applicati durante la dichiarazione di classe non sugli oggetti.

Fondamentalmente, i decoratori aggiungono alcuni metadati all'oggetto classe e ai quali non è possibile accedere tramite ereditarietà.

Se si desidera ottenere l'ereditarietà del decoratore, suggerirei di scrivere un decoratore personalizzato. Qualcosa come sotto esempio.

export function CustomComponent(annotation: any) {
    return function (target: Function) {
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;

    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);
    var parentParamTypes = Reflect.getMetadata('design:paramtypes', parentTarget);
    var parentPropMetadata = Reflect.getMetadata('propMetadata', parentTarget);
    var parentParameters = Reflect.getMetadata('parameters', parentTarget);

    var parentAnnotation = parentAnnotations[0];

    Object.keys(parentAnnotation).forEach(key => {
    if (isPresent(parentAnnotation[key])) {
        if (!isPresent(annotation[key])) {
        annotation[key] = parentAnnotation[key];
        }
    }
    });
    // Same for the other metadata
    var metadata = new ComponentMetadata(annotation);

    Reflect.defineMetadata('annotations', [ metadata ], target);
    };
};

Consultare: https://medium.com/@ttemplier/angular2-decorators-and-class-inheritance-905921dbd1b7


Potresti esemplificare (usando l'esempio della domanda) come funzionerebbe? È possibile utilizzare stackblitz per sviluppare l'esempio e condividere il collegamento.
Fernando Leal,

@Component è un decoratore, i decoratori vengono applicati durante la dichiarazione di classe non sugli oggetti.
MAHESH VALIYA VEETIL

Hai ragione. I decoratori non fanno alcuna differenza. È necessario solo se il componente di base viene utilizzato come componente altrove
MAHESH VALIYA VEETIL

0
just use inheritance,Extend parent class in child class and declare constructor with parent class parameter and this parameter use in super().

1.parent class
@Component({
    selector: 'teams-players-box',
    templateUrl: '/maxweb/app/app/teams-players-box.component.html'
})
export class TeamsPlayersBoxComponent {
    public _userProfile:UserProfile;
    public _user_img:any;
    public _box_class:string="about-team teams-blockbox";
    public fullname:string;
    public _index:any;
    public _isView:string;
    indexnumber:number;
    constructor(
        public _userProfilesSvc: UserProfiles,
        public _router:Router,
    ){}
2.child class
@Component({

    selector: '[teams-players-eligibility]',
    templateUrl: '/maxweb/app/app/teams-players-eligibility.component.html'
})
export class TeamsPlayersEligibilityComponent extends TeamsPlayersBoxComponent{

    constructor (public _userProfilesSvc: UserProfiles,
            public _router:Router) {
            super(_userProfilesSvc,_router);
        }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.