79165085

Date: 2024-11-07 05:12:49
Score: 0.5
Natty:
Report link

Okay, I have the answer to your question and the explanation. After running into the same issue myself and digging deeper, I realized it was a "skill" issue and not an "angular" issue.

The "skill" issue here is using a boilerplate template (e.g., ng new) and not understanding how the different files are wired together, further exaggerated by different documentations and online articles assuming different versions of angular "template".

Issue in detail

Angular: 18.2.0 Material: 18.2.11

When using standalone components without module files, I could not utilize the provider to inject the default options into the matsnackbar in the following way.

import { MAT_SNACK_BAR_DEFAULT_OPTIONS, MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';

@Component({
  ......
  standalone: true,
  imports: [...., MatSnackBarModule, ....],
  providers: [
    {
      provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
      useValue: {
        duration: 2500,
        horizontalPosition: 'left',
      }
    }],
  ......
})

constructor(private _snackBar:MatSnackBar) {}

Solution 1: Defining providers in a component and inject it immediately for usage

Building on @christian-fuchs' comments and answers, the closest workaround I could settle on was the following.

import { MAT_SNACK_BAR_DEFAULT_OPTIONS, MatSnackBar, MatSnackBarConfig, MatSnackBarModule } from '@angular/material/snack-bar';

@Component({
  ......
  imports: [....,MatSnackBarModule, ....],
  standalone: true,
  providers: [
    {
      provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
      useValue: {
        duration: 2500,
        horizontalPosition: 'left',
      }
    }],
  ......
})

constructor(@Inject(MAT_SNACK_BAR_DEFAULT_OPTIONS) private _snackBarDefaultOptions: MatSnackBarConfig,
  private _snackBar: MatSnackBar) {}

triggerSnackbar() {
  this._snackBar.open('This snackbar will close in 5 seconds', null, _snackBarDefaultOptions)
}

Solution 2: Use ngModules along with standalone components (Keep it lightweight and use it for global configs)

To be honest, the above example is not really a workaround but the right way to do this when you're not using Angular module files (i.e., app.module.ts, etc.).

The reason is that the matsnackbar seems to be initialized before the provider can feed the default values in the standalone component. However, it would work if you took the same approach in the module file, say example.module.ts, and did something like below.

example.module.ts

import {
  MAT_SNACK_BAR_DEFAULT_OPTIONS,
  MatSnackBarModule,
} from '@angular/material/snack-bar';

@NgModule({
  providers: [
    { provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 50 } },
  ],
  exports: [MatSnackBarModule],

)
export class ExampleModule {}

example.component.ts

import { SnackBarComponentComponent } from './snack-bar-component/snack-bar-component.component';

@Component({
  ....
})
export class ExampleComponent {

  constructor(private snackBar: MatSnackBar) {}

  openSnackBar() {
    this.snackBar.open('This snack will close in 5 seconds');
  }
}

There may be some gotchas. For example, if this were app.module.ts and app.component.ts, we need to ensure that the app module is bootstrapped in main.ts or main.server.ts (server-side rendering, I believe) and not the AppComponent (as is the case when generating an Angular app with standalone templates).

Method 3: Understand the boilerplate generated by the latest ng command and define the provider in the correct location/file

If you are set on using angular standalone components without any modules, define the providers when bootstrapping. This ensures that the required information is loaded into the global config before these components are rendered.

For example

app.config.ts

import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
      useValue: {
        duration: 1000,
        horizontalPosition: 'right',
      }
    }]
};

main.ts

import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, appConfig)
  .catch((err) => console.error(err));
Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @christian-fuchs'
  • Low reputation (1):
Posted by: chandanbsd