¿Cómo paso datos a componentes enrutados Angular?

290
dragonmnl 2016-04-25 22:10.

En una de mis plantillas de rutas Angular 2 ( FirstComponent ) tengo un botón

first.component.html

<div class="button" click="routeWithData()">Pass data and route</div>

Mi objetivo es lograr:

Haga clic en el botón -> ruta a otro componente conservando los datos y sin utilizar el otro componente como directiva.

Esto es lo que intenté ...

1er ENFOQUE

En la misma vista, estoy almacenando la recopilación de los mismos datos en función de la interacción del usuario.

first.component.ts

export class FirstComponent {
     constructor(private _router: Router) { }

     property1: number;
     property2: string;
     property3: TypeXY; // this a class, not a primitive type

    // here some class methods set the properties above

    // DOM events
    routeWithData(){
         // here route
    }
}

Normalmente me encaminaría a SecondComponent por

 this._router.navigate(['SecondComponent']);

eventualmente pasando los datos por

 this._router.navigate(['SecondComponent', {p1: this.property1, p2: property2 }]);

mientras que la definición del enlace con parámetros sería

@RouteConfig([
      // ...
      { path: '/SecondComponent/:p1:p2', name: 'SecondComponent', component: SecondComponent} 
)]

El problema con este enfoque es que supongo que no puedo pasar datos complejos (por ejemplo, un objeto como property3) en la url;

2do ENFOQUE

Una alternativa sería incluir SecondComponent como directiva en FirstComponent.

  <SecondComponent [p3]="property3"></SecondComponent>

Sin embargo, quiero dirigirme a ese componente, ¡no incluirlo!

3er ENFOQUE

La solución más viable que veo aquí sería utilizar un servicio (por ejemplo, FirstComponentService) para

  • almacenar los datos (_firstComponentService.storeData ()) en routeWithData () en FirstComponent
  • recuperar los datos (_firstComponentService.retrieveData ()) en ngOnInit () en SecondComponent

Si bien este enfoque parece perfectamente viable, me pregunto si esta es la forma más fácil / elegante de lograr el objetivo.

En general, me gustaría saber si me faltan otros enfoques potenciales para pasar los datos entre componentes, particularmente con la menor cantidad posible de código.

16 answers

210
Günter Zöchbauer 2016-04-25 22:12.

actualización 4.0.0

Consulte los documentos de Angular para obtener más detalles https://angular.io/guide/router#fetch-data-before-navigating

original

Usar un servicio es el camino a seguir. En los parámetros de ruta, solo debe pasar los datos que desea que se reflejen en la barra de URL del navegador.

Consulte también https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

El enrutador enviado con RC.4 vuelve a introducir data

constructor(private route: ActivatedRoute) {}
const routes: RouterConfig = [
  {path: '', redirectTo: '/heroes', pathMatch : 'full'},
  {path : 'heroes', component : HeroDetailComponent, data : {some_data : 'some value'}}
];
class HeroDetailComponent {
  ngOnInit() {
    this.sub = this.route
      .data
      .subscribe(v => console.log(v));
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}

Consulte también Plunker en https://github.com/angular/angular/issues/9757#issuecomment-229847781

70
Utpal Kumar Das 2016-09-19 23:39.

Creo que dado que no tenemos el tipo de cosa $ rootScope en angular 2 como en angular 1.x. Podemos usar el servicio / clase compartido angular 2 mientras que en ngOnDestroy pasar datos al servicio y después del enrutamiento tomar los datos del servicio en la función ngOnInit :

Aquí estoy usando DataService para compartir el objeto héroe:

import { Hero } from './hero';
export class DataService {
  public hero: Hero;
}

Pasar objeto del componente de la primera página:

 ngOnDestroy() {
    this.dataService.hero = this.hero; 
 }

Tomar objeto del componente de la segunda página:

 ngOnInit() {
    this.hero = this.dataService.hero; 
 }

Aquí hay un ejemplo: plunker

35
Washington Soares Braga 2019-06-28 04:29.

Angular 7.2.0 introdujo una nueva forma de pasar los datos al navegar entre componentes enrutados:

@Component({
  template: `<a (click)="navigateWithState()">Go</a>`,
})
export class AppComponent  {
  constructor(public router: Router) {}
  navigateWithState() {
    this.router.navigateByUrl('/123', { state: { hello: 'world' } });
  }
}

O:

@Component({
  selector: 'my-app',
  template: `
  <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>`,
})
export class AppComponent  {}

Para leer el estado, puede acceder a la window.history.statepropiedad una vez finalizada la navegación:

export class PageComponent implements OnInit {
  state$: Observable<object>;

  constructor(public activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.state$ = this.activatedRoute.paramMap
      .pipe(map(() => window.history.state))
  }
}
33
Daniel Charles Mwangila 2018-09-07 04:02.
<div class="button" click="routeWithData()">Pass data and route</div>

bueno, la forma más fácil de hacerlo en angular 6 u otras versiones, espero es simplemente definir su ruta con la cantidad de datos que desea pasar

{path: 'detailView/:id', component: DetailedViewComponent}

como puede ver en la definición de mis rutas, agregué el /:idto stand a los datos que quiero pasar al componente a través de la navegación del enrutador. Por lo tanto, su código se verá como

<a class="btn btn-white-view" [routerLink]="[ '/detailView',list.id]">view</a>

para leer idel componente, solo importe ActivatedRoute como

import { ActivatedRoute } from '@angular/router'

y en el ngOnInites donde recuperas los datos

ngOnInit() {
       this.sub = this.route.params.subscribe(params => {
        this.id = params['id'];
        });
        console.log(this.id);
      }

puede leer más en este artículo https://www.tektutorialshub.com/angular-passing-parameters-to-route/

17
PeS 2019-11-12 01:29.

Estamos en 2019 y muchas de las respuestas aquí funcionarían, dependiendo de lo que quieras hacer. Si desea pasar algún estado interno no visible en la URL (parámetros, consulta), puede usar statedesde 7.2 (como aprendí hoy :)).

Desde el blog (créditos de Tomasz Kula): navegas hasta la ruta ...

... de ts: this.router.navigateByUrl('/details', { state: { hello: 'world' } });

... desde plantilla HTML: <a routerLink="/details" [state]="{ hello: 'world' }">Go</a>

Y para recogerlo en el componente de destino:

constructor(public activatedRoute: ActivatedRoute) {}

  ngOnInit() {
    this.state$ = this.activatedRoute.paramMap
      .pipe(map(() => window.history.state))
  }

Tarde, pero espero que esto ayude a alguien con Angular.

16
terary 2019-01-14 07:44.

Alguna persona súper inteligente (tmburnell) que no soy yo sugiere reescribir los datos de la ruta:

let route = this.router.config.find(r => r.path === '/path');
route.data = { entity: 'entity' };
this.router.navigateByUrl('/path');

Como se ve aquí en los comentarios.

Espero que alguien encuentre esto útil

12
AmirReza-Farahlagha 2019-01-07 22:11.

Este es el otro enfoque que no es bueno para este problema. Creo que el mejor enfoque es el parámetro de consulta por Routerangular que tiene 2 vías:

Pasando el parámetro de consulta directamente

Con este código se puede navegar a urlpor paramssu código html:

<a [routerLink]="['customer-service']" [queryParams]="{ serviceId: 99 }"></a>

Pasando el parámetro de consulta por Router

Tienes que inyectar el enrutador dentro de tu me constructorgusta:

constructor(private router:Router){

}

Ahora usa eso como:

goToPage(pageNum) {
    this.router.navigate(['/product-list'], { queryParams: { serviceId: serviceId} });
}

Ahora, si quieres leer desde Routerotro Component, tienes que usar ActivatedRoutelike:

constructor(private activateRouter:ActivatedRouter){

}

y subscribeque:

  ngOnInit() {
    this.sub = this.route
      .queryParams
      .subscribe(params => {
        // Defaults to 0 if no query param provided.
        this.page = +params['serviceId'] || 0;
      });
  }
7
scorpion 2018-09-21 10:02.

Solución con ActiveRoute (si desea pasar el objeto por ruta, use JSON.stringfy / JSON.parse):

Prepare el objeto antes de enviarlo:

export class AdminUserListComponent {

  users : User[];

  constructor( private router : Router) { }

  modifyUser(i) {

    let navigationExtras: NavigationExtras = {
      queryParams: {
          "user": JSON.stringify(this.users[i])
      }
    };

    this.router.navigate(["admin/user/edit"],  navigationExtras);
  }

}

Reciba su objeto en el componente de destino:

export class AdminUserEditComponent  {

  userWithRole: UserWithRole;      

  constructor( private route: ActivatedRoute) {}

  ngOnInit(): void {
    super.ngOnInit();

      this.route.queryParams.subscribe(params => {
        this.userWithRole.user = JSON.parse(params["user"]);
      });
  }

}
6
Shashank Agrawal 2020-08-10 18:32.

Miré todas las soluciones (y probé algunas) de esta página, pero no estaba convencido de que tuviéramos que implementar una forma de pirateo para lograr la transferencia de datos entre rutas.

Otro problema con simple history.statees que si está pasando una instancia de una clase en particular en el stateobjeto, no será la instancia mientras la recibe. Pero será un objeto JavaScript simple y llano.

Entonces, en mi aplicación Angular v10 (Ionic v5), hice esto:

this.router.navigateByUrl('/authenticate/username', {
    state: {user: new User(), foo: 'bar'}
});

Y en el componente de navegación ( '/authenticate/username'), en el ngOnInit()método, imprimí los datos con this.router.getCurrentNavigation().extras.state-

ngOnInit() {
    console.log('>>authenticate-username:41:',
        this.router.getCurrentNavigation().extras.state);
}

Y obtuve los datos deseados que se pasaron

5
ahankendi 2016-10-31 09:38.

El tercer enfoque es la forma más común de compartir datos entre componentes. puede inyectar el servicio del artículo que desea utilizar en el componente relacionado.

import { Injectable } from '@angular/core';
import { Predicate } from '../interfaces'

import * as _ from 'lodash';

@Injectable()
export class ItemsService {

    constructor() { }


    removeItemFromArray<T>(array: Array<T>, item: any) {
        _.remove(array, function (current) {
            //console.log(current);
            return JSON.stringify(current) === JSON.stringify(item);
        });
    }

    removeItems<T>(array: Array<T>, predicate: Predicate<T>) {
        _.remove(array, predicate);
    }

    setItem<T>(array: Array<T>, predicate: Predicate<T>, item: T) {
        var _oldItem = _.find(array, predicate);
        if(_oldItem){
            var index = _.indexOf(array, _oldItem);
            array.splice(index, 1, item);
        } else {
            array.push(item);
        }
    }


    addItemToStart<T>(array: Array<T>, item: any) {
        array.splice(0, 0, item);
    }


    getPropertyValues<T, R>(array: Array<T>, property : string) : R
    {
        var result = _.map(array, property);
        return <R><any>result;
    }

    getSerialized<T>(arg: any): T {
        return <T>JSON.parse(JSON.stringify(arg));
    }
}



export interface Predicate<T> {
    (item: T): boolean
}
3
Noname 2018-06-19 14:06.

Pasar usando JSON

  <a routerLink = "/link"
   [queryParams] = "{parameterName: objectToPass| json }">
         sample Link                   
  </a>
3
Amin Adel 2018-12-25 00:46.

utilice un servicio compartido para almacenar datos con un índice personalizado. luego envíe ese índice personalizado con queryParam. este enfoque es más flexible .

// component-a : typeScript :
constructor( private DataCollector: DataCollectorService ) {}

ngOnInit() {
    this.DataCollector['someDataIndex'] = data;
}

// component-a : html :
<a routerLink="/target-page" 
   [queryParams]="{index: 'someDataIndex'}"></a>

.

// component-b : typeScript :
public data;

constructor( private DataCollector: DataCollectorService ) {}

ngOnInit() {
    this.route.queryParams.subscribe(
        (queryParams: Params) => {
            this.data = this.DataCollector[queryParams['index']];
        }
    );
}
1
Nelson Bwogora 2019-05-14 08:31.

di que tienes

  1. componente1.ts
  2. componente1.html

y desea pasar datos a component2.ts .

  • en component1.ts es una variable con datos dicen

      //component1.ts
      item={name:"Nelson", bankAccount:"1 million dollars"}
    
      //component1.html
       //the line routerLink="/meter-readings/{{item.meterReadingId}}" has nothing to 
      //do with this , replace that with the url you are navigating to
      <a
        mat-button
        [queryParams]="{ params: item | json}"
        routerLink="/meter-readings/{{item.meterReadingId}}"
        routerLinkActive="router-link-active">
        View
      </a>
    
      //component2.ts
      import { ActivatedRoute} from "@angular/router";
      import 'rxjs/add/operator/filter';
    
      /*class name etc and class boiler plate */
      data:any //will hold our final object that we passed 
      constructor(
      private route: ActivatedRoute,
      ) {}
    
     ngOnInit() {
    
     this.route.queryParams
      .filter(params => params.reading)
      .subscribe(params => {
      console.log(params); // DATA WILL BE A JSON STRING- WE PARSE TO GET BACK OUR 
                           //OBJECT
    
      this.data = JSON.parse(params.item) ;
    
      console.log(this.data,'PASSED DATA'); //Gives {name:"Nelson", bankAccount:"1 
                                            //million dollars"}
       });
      }
    
1
sushil suthar 2020-03-20 08:07.

Puede utilizar BehaviorSubject para compartir datos entre componentes enrutados. Un BehaviorSubject tiene un valor. Cuando se suscribe emite el valor inmediatamente. Un sujeto no tiene valor.

En el servicio.

@Injectable({
  providedIn: 'root'
})
export class CustomerReportService extends BaseService {
  reportFilter = new BehaviorSubject<ReportFilterVM>(null);
  constructor(private httpClient: HttpClient) { super(); }

  getCustomerBalanceDetails(reportFilter: ReportFilterVM): Observable<Array<CustomerBalanceDetailVM>> {
    return this.httpClient.post<Array<CustomerBalanceDetailVM>>(this.apiBaseURL + 'CustomerReport/CustomerBalanceDetail', reportFilter);
  }
}

En el componente, puede suscribirse a este BehaviorSubject.

this.reportService.reportFilter.subscribe(f => {
      if (f) {
        this.reportFilter = f;
      }
    });

Nota: El tema no funcionará aquí, solo es necesario usar el tema de comportamiento.

0
Jeff G. 2020-09-30 12:29.

Una buena solución es implementar un método Guard con canActivate. En este escenario, puede obtener datos de una api determinada y permitir que el usuario acceda al componente descrito en el archivo de enrutamiento. Mientras tanto, se puede establecer la propiedad de datos del objeto de ruta y recuperarla en el componente.

Digamos que tiene esta configuración de enrutamiento:

const routes: Routes = [
    { path: "/:projectName", component: ProjectComponent, canActivate: [ProjectGuard] }
]`

en su archivo de guardia puede tener:

canActivate(next: ActivatedRouteSnapshot,state: RouterStateSnapshot)
: Observable<boolean> | Promise<boolean> | boolean {
return this.myProjectService.getProject(projectNameFoundElsewhere).pipe(
  map((project) => {
    if (project) {
      next.data = project;
    }
    return !!project;
  }),
);

} `

Entonces en tu componente

constructor(private route: ActivatedRoute) {
    this.route.data.subscribe((value) => (this.project = value));
}

Esta forma es un poco diferente a pasar a través de un servicio, ya que el servicio mantiene el valor en un sujeto de comportamiento siempre que no esté desarmado. Al pasar por el guardia, los datos están disponibles para la ruta actual. No he comprobado si las rutas de los niños guardan los datos o no.

0
Ralf Hannuschka 2020-11-18 01:02.

Por defecto, no usaré un guardia para este para mí, es más, puedo ingresar a la ruta o puedo dejarla. No es para compartir datos entre ellos.

Si desea cargar datos antes de que ingresemos una ruta, simplemente agregue un resolutor a este, esto también es parte del enrutador.

Como ejemplo muy básico:

Resolver

import { Resolve, ActivatedRoute } from "@angular/router";
import { Observable } from "rxjs";
import { Injectable } from "@angular/core";
import { take } from "rxjs/operators";

@Injectable()
export class UserResolver implements Resolve<User> {

    constructor(
        private userService: UserService,
        private route: ActivatedRoute
    ) {}

    resolve(): Observable<firebase.User> {
        return this.route.params.pipe(
            switchMap((params) => this.userService.fetchUser(params.user_id)),
            take(1)
        );
    }
}

poner en el enrutador:

RouterModule.forChild([
{
    path: "user/:user_id",
    component: MyUserDetailPage,
    resolve: {
        user: UserResolver
    }
  }
}]

obtener los datos en nuestro componente

ngOnInit() {
    const user: firebase.User = this.activatedRoute.snapshot.data.user;
}

La desventaja de este enfoque es que ingresará la ruta primero si no obtiene los datos del usuario antes, esto asegura que los datos para el usuario se hayan cargado y estén listos al inicio del componente, pero usted permanecerá en la página anterior como durante mucho tiempo que se han cargado los datos (Cargando animación)

MORE COOL STUFF

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que le diagnosticaron envenenamiento por mercurio y plomo, probablemente debido a su trabajo como manipuladora de casas.

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

Recientemente salió a la luz un informe policial que acusa a la estrella de 'Love Is Blind', Brennon, de violencia doméstica. Ahora, Brennon ha respondido a los reclamos.

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Conozca cómo Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia mientras organizaba la primera celebración de Acción de Gracias desde que murió su madre, Naomi Judd.

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Descubra por qué un destacado experto en lenguaje corporal cree que es fácil trazar "tales paralelismos" entre la princesa Kate Middleton y la princesa Diana.

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los inodoros arrojan columnas de aerosol invisibles con cada descarga. ¿Como sabemos? La prueba fue capturada por láseres de alta potencia.

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

¿Caduca el repelente de insectos?

¿Caduca el repelente de insectos?

¿Sigue siendo efectivo ese lote de repelente de insectos que te quedó del verano pasado? Si es así, ¿por cuánto tiempo?

Estados Unidos podría evitarse el horror del nuevo gran horneado británico

Estados Unidos podría evitarse el horror del nuevo gran horneado británico

Si este tráiler de pesadilla de la temporada más reciente de Great British Bake Off te asustó y te hizo no volver a ver el programa, es posible que tengas suerte: PBS no ha decidido si se transmitirá o no la última temporada en los Estados Unidos. actualización, para aquellos que no siguen sin aliento este tipo de drama de nicho: el presentador Paul Hollywood y la hermosa carpa llena de batidoras de colores pastel y cuadros se trasladaron de la BBC al Canal 4; Mary Berry, Sue Perkins y Mel Giedroyc renunciaron.

Atún como Oh, Hello llega a Netflix

Atún como Oh, Hello llega a Netflix

Foto: Netflix Oh, Hello On Broadway (Netflix): Después de llegar a Broadway el año pasado, los dos locos del Upper West Side interpretados por John Mulaney y Nick Kroll finalmente llegaron a Netflix. El especial consta del espectáculo en el escenario, algunos momentos entre bastidores y un invitado muy especial de “Too Much Tuna”.

Actualice a un Sonicare por tan solo $ 30

Actualice a un Sonicare por tan solo $ 30

¿Quiere probar un cepillo de dientes Sonicare sin gastar mucho dinero en uno de sus modelos favoritos de gama alta? Puede comprar un kit de la Serie 2 o Serie 3 por tan solo $ 30 hoy en Amazon. Haga clic aquí para ver la lista completa de modelos elegibles y tenga en cuenta que se descontarán $ 10 adicionales en su carrito.

Ponle una tapa. En realidad, ponle una tapa a todo. Consigue 12 tapas de cocina elásticas de silicona por $14. [Exclusivo]

Ponle una tapa. En realidad, ponle una tapa a todo. Consigue 12 tapas de cocina elásticas de silicona por $14. [Exclusivo]

Tapas elásticas de silicona de Tomorrow's Kitchen, paquete de 12 | $14 | Amazonas | Código promocional 20OFFKINJALids son básicamente los calcetines de la cocina; siempre perdiéndose, dejando contenedores huérfanos que nunca podrán volver a cerrarse. Pero, ¿y si sus tapas pudieran estirarse y adaptarse a todos los recipientes, ollas, sartenes e incluso frutas en rodajas grandes que sobran? Nunca más tendrás que preocuparte por perder esa tapa tan específica.

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

El equipo está a la espera de las medallas que ganó en los Juegos Olímpicos de Invierno de 2022 en Beijing, ya que se está resolviendo un caso de dopaje que involucra a la patinadora artística rusa Kamila Valieva.

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Miles de compradores de Amazon recomiendan la funda de almohada de seda Mulberry, y está a la venta en este momento. La funda de almohada de seda viene en varios colores y ayuda a mantener el cabello suave y la piel clara. Compre las fundas de almohada de seda mientras tienen hasta un 46 por ciento de descuento en Amazon

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

El jueves se presentó una denuncia de delito menor amenazante agravado contra Joe Mixon.

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

El Departamento de Policía de Lafayette comenzó a investigar a un profesor de la Universidad de Purdue en diciembre después de recibir varias denuncias de un "hombre sospechoso que se acercaba a una mujer".

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Al igual que el mundo que nos rodea, el lenguaje siempre está cambiando. Mientras que en eras anteriores los cambios en el idioma ocurrían durante años o incluso décadas, ahora pueden ocurrir en cuestión de días o incluso horas.

India me está pateando el culo

India me está pateando el culo

Estoy de vuelta por primera vez en seis años. No puedo decirte cuánto tiempo he estado esperando esto.

ℝ

“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.

¿Merrick Garland le ha fallado a Estados Unidos?

Es más de la mitad de la presidencia de Biden. ¿Qué está esperando Merrick Garland?

¿Merrick Garland le ha fallado a Estados Unidos?

Creo, un poco tarde en la vida, en dar oportunidades a la gente. Generosamente.

Language