What is the best way to kickstart a Reactive Angular 2 form by utilizing an Observable?

My current strategy involves storing the form values in my ngrx store so that users can easily navigate around the site and return to the form if needed. The concept is to have the form values repopulate from the store using an observable.

This is how I am currently implementing it:

constructor(private store: Store<AppState>, private fb: FormBuilder) {
    this.images = images;
    this.recipe$ = store.select(recipeBuilderSelector);
    this.recipe$.subscribe(recipe => this.recipe = recipe); // console.log() => undefined
    this.recipeForm = fb.group({
      foodName: [this.recipe.name], // also tried with an OR: ( this.recipe.name || '')
      description: [this.recipe.description]
    })
  }

The store receives an initial value, which seems to be correctly passing through my selector function. However, by the time the form is being created, it appears that the value has not returned yet. As a result, this.recipe remains undefined.

Is this approach incorrect, or is there a way to ensure that the observable is returned before creating the form?

Answer №1

When it comes to handling observables, it may seem like adding another layer will complicate things. However, breaking down a single component into a container component and a presentational component actually makes it easier.

The container component solely deals with observables without concerning itself with presentation. Data from observables is passed to the presentational component through @Input properties while utilizing the async pipe:

@Component({
  selector: "recipe-container",
  template: `<recipe-component [recipe]="recipe$ | async"></recipe-component>`
})
export class RecipeContainer {

  public recipe$: Observable<any>;

  constructor(private store: Store<AppState>) {
    this.recipe$ = store.select(recipeBuilderSelector);
  }
}

The presentational component only receives simple properties and does not interact directly with observables:

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "recipe-component",
  template: `...`
})
export class RecipeComponent {

  public recipeForm: FormGroup;

  constructor(private formBuilder: FormBuilder) {
    this.recipeForm = this.formBuilder.group({
      foodName: [""],
      description: [""]
    });
  }

  @Input() set recipe(value: any) {
    this.recipeForm.patchValue({
      foodName: value.name,
      description: value.description
    });
  }
}

The concept of using container and presentational components follows the general Redux idea, as detailed in Presentational and Container Components.

Answer №2

When faced with this situation, there are two effective options to consider...

Choice 1:

To show or hide the form in the HTML, you can utilize the *ngIf directive like so:

<form *ngIf="this.recipe">...</form>

Choice 2: Implement the async pipe in your template and structure your model as follows:

component

model: Observable<FormGroup>;    
...
this.model = store.select(recipeBuilderSelector)
    .startWith(someDefaultValue)
    .map((recipe: Recipe) => {
        return fb.group({
            foodName: [recipe.name],
            description: [recipe.description]
        })
    })

template

<app-my-form [model]="(model | async)"></app-my-form>

It is important to ensure proper handling of updates to both the store and the current model.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Retrieve data from FileReader's onload event asynchronously in Javascript

Although this question has been asked numerous times before, I have reviewed the answers provided and still cannot get my code to work. I am working with Angular 2. I have input tags for file insertion. The file successfully reaches the component, but when ...

Create a Jest test environment with MongoDB and TypeScript, following the guidance provided in the Jest documentation

While attempting to set up a simple test file following the jest documentation, I encountered several linter errors: connection: The type 'Promise<MongoClient> & void' is missing properties such as '{ db: (arg0: any) => any; cl ...

Webpack does not support d3-tip in its current configuration

I'm having some trouble getting d3-tip to work with webpack while using TypeScript. Whenever I try to trigger mouseover events, I get an error saying "Uncaught TypeError: Cannot read property 'target' of null". This issue arises because th ...

What is the best way to loop through an object while keeping track of its value types

I have a JSON file containing UI adjustments sourced from the API: interface UIAdjustmentsJSON { logoSize: number; themeColor: string; isFullScreen: boolean; } To simplify things, let's utilize a static object: const adjustments: UIAdjust ...

Typescript error message TS2314: One type argument is required for the generic type 'Array<T>'

I recently started my journey in learning typescript and have written some basic code. class Learning { subjects: Array[string]; hoursPerDay: number; constructor(subj: Array[string], hrs: number) { this.subjects = subj; thi ...

"Experience the seamless navigation features of React Navigation V6 in conjunction with

Hello there, I've been experimenting with react-navigation 6 in an attempt to show a modal with presentation: "modal" as instructed on the documentation. However, I'm facing an issue where the modal is not displaying correctly and appears like a ...

Conceal or eliminate webpack from Angular 2 framework

Currently immersed in an Angular 2 project with TypeScript. Desiring to conceal or eliminate webpack from the developer tool. Came across information about uglify but it remains somewhat puzzling. See the image below for a glimpse of the Chrome Developer ...

The NestJS framework encountered an error due to a method being undefined in the

Encountering Error with NestJS Function create123: TypeError - Cannot read properties of undefined (reading 'create123') The constructor is displayed below \`export class AuthenticationService { constructor( private readonly usersServ ...

Encountered an error while trying to load a resource on ionic emulate

After successful testing in the browser, my app encounters an issue when emulating on an android emulator. It seems to be unable to load resources from the json-server hosted in my localhost, resulting in a "Failed to load resource" error message. To addr ...

What is the best way to format a string into a specific pattern within an Angular application

In my Angular component, I have 4 fields: customerName, startDate, and startTime. Additionally, I have a fourth field that is a textarea where the user can see the message that will be sent via email or SMS. Within my component, I have defined a string as ...

Can I integrate @types/pkg into my custom library to automatically have types included?

Imagine I am creating a library that utilizes types from the Definitely Typed repository (such as @types/pkg). Would it be feasible for my package to automatically include these types when installed, eliminating the need for consumers to separately instal ...

What is the TypeScript term for assigning multiple parameters an alias?

Imagine having a piece of code structured like this: export async function execute(conf: Record<string, string>, path: string, params: Array<string>) { const cmd = params[1]; const commandOption = params.slice(2) switch(cmd){ ...

The issue with the demo variable not functioning properly within the ngOnChanges method of the child component

I am facing an issue in child.component.ts where I am using the demo variable with input(), but it is not showing up in the developer console. app.html <input type="text" placeholder="text" #val> <br> <button (click)="submitValue(val)"> ...

Tips for removing data from documents that include automatically generated IDs

In my Angular project, I am utilizing google-cloud-firestore as the database. To import Firestore, I used the code import { AngularFirestore } from '@angular/fire/firestore';. The following function is used to add data to the database: changeLev ...

A guide on transforming JSON data to an array format where nested arrays are encapsulated within objects using curly braces instead of square brackets in TypeScript

Retrieve data from a REST API in the following format: { "ProductID":1, "Category":[ { "CategoryID":1, "SubCategory":[ { "SubCategoryID":1, } ] } ] } I need to t ...

Creating a submodule in Angular is a great way to organize and modular

Currently, I am developing an angular 2 app using CLI. The size of my app module is becoming too large and complex, so I have decided to create submodules. The submodule structure is as follows: // Project Submodule import { NgModule } from & ...

Error encountered when attempting to retrieve token from firebase for messaging

I am currently working on implementing web push notifications using Firebase. Unfortunately, when attempting to access messaging.getToken(), I encounter an error stating "messaging is undefined." Below is the code snippet I am utilizing: private messaging ...

Dealing with name conflicts in Typescript when using multiple interface inheritance

Is it possible to securely implement two interfaces in Typescript that both have a member of the same name? Can this be achieved? For example: interface IFace1 { name: string; } interface IFace2 { name: string; } class SomeClass implements IFace ...

connect the validation of forms to different components

I am currently working on components to facilitate the user addition process. Below is an example of my form component: createForm(): void { this.courseAddForm = this.formBuilder.group({ name: ['', [ Validators.required, ...

How can I personalize a Leaflet popup with image thumbnails and additional customization options?

I've been researching and trying out different solutions, but so far none of them have worked for me. My goal is to dynamically add a title, image, address, and name to popups on my leaflet map as data comes in. However, I'm experiencing some cha ...