Using Reactive Forms to Modify an Object in Angular Firebase

As I dive into creating an edit form using the Reactive form approach, I encounter the challenge of binding the object's value with a FormControl only to get null instead of the expected true value.

In my Angular and Firebase project, the issue arises when attempting to initialize the object in the edit component by invoking a service method (getSpecificNews) with an ID parameter, but it fails to work as intended.

This being my inaugural foray into Angular and Firebase, I seek assistance from anyone who can aid me in resolving this predicament.

news.service.ts

import { map } from 'rxjs/operators';
import { AngularFireDatabase, AngularFireList } from '@angular/fire/database';
import { News } from './news.model';
import { Injectable } from '@angular/core';

@Injectable()
export class NewsService {
    news: AngularFireList<News[]>;
    constructor(private db: AngularFireDatabase) {
    }
    getNews() {
        this.news = this.db.list('news');
        return this.news.valueChanges();
    }

    getSingleNews(id: string) {
        return this.db.object('news/' + id);
    }

    updateNews(news: News) {
        return this.db.database.ref().update(news);
    }

    deleteItem(id: string) {
        this.getSingleNews(id).remove();
    }
}

edit.component.ts

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { NewsService } from '../news.service';
import { News } from '../news.model';
import { AngularFireDatabase } from '@angular/fire/database';
import { Router, ActivatedRoute, Params } from '@angular/router';

@Component({
  selector: 'app-news-edit',
  templateUrl: './edit.component.html',
  styleUrls: ['./edit.component.css']
})
export class NewsEditComponent implements OnInit {
  @ViewChild('closeBtn') closeBtn: ElementRef;
  editMode = false;
  id;
  news: any;
  newsForm: FormGroup;
  constructor(private newsService: NewsService, private db: AngularFireDatabase, private router: Router, private route: ActivatedRoute) { }

  ngOnInit() {
    this.getRouteID();
    this.news = this.newsService.getSingleNews(this.id).snapshotChanges().subscribe(
      action => {
        this.news = action.payload.val();
      }
    );
    console.log(this.news.title);
    this.initForm();
  }

  private initForm() {
    let theTitle = '';
    let theSubtitle = '';
    let theArticle = '';
    let thePicture = '';

    if (this.editMode) {
      theTitle = this.news.title;
      theSubtitle = this.news.subtitle;
      theArticle = this.news.article;
      thePicture = this.news.picture;
    }

    this.newsForm = new FormGroup({
      'title': new FormControl(theTitle),
      'subtitle': new FormControl(theSubtitle),
      'article': new FormControl(theArticle),
      'picture': new FormControl(thePicture)
    });
  }

  getRouteID() {
    this.route.params.subscribe(
      (param: Params) => {
        this.id = param['id'];
        this.editMode = param['id'] != null;
      }
    );
  }

  onCreateNews() {
    const id = '';
    const title = this.newsForm.value.title;
    const subtitle = this.newsForm.value.subtitle;
    const article = this.newsForm.value.article;
    const picture = this.newsForm.value.picture;

    if (this.editMode) {
      const iD = this.id;
      this.newsService.getSingleNews(this.id).update({title, subtitle, article, picture})
        .then(() => console.log(id, title, subtitle, picture, article))
        .catch((e) => console.log(e));
    } else {
      console.log('entou no else do create news ');
      const newNew = new News(id, title, subtitle, picture, article);
      this.db.database.ref('news/' + newNew.getID()).set(newNew,
        (e) => {
          if (e) {
            console.log(e);
          } else {
            console.log('Data saved');
          }
        });
    }
    this.newsForm.reset();
    this.router.navigate(['/news']);
    this.closeModal();
  }

  closeModal() {
    this.closeBtn.nativeElement.click();
  }

}

edit.component.html

<div class="modal-dialog modal-dialog-centered" role="document">
  <form (ngSubmit)="onCreateNews()" [formGroup]="newsForm">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalCenterTitle" *ngIf="!editMode">Adicionar Noticia</h5>
        <h5 class="modal-title" id="exampleModalCenterTitle" *ngIf="editMode">Editar Noticia</h5>
        <button type="button" class="close" data-dismiss="modal" #closeBtn aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <div class="container">

          <div class="row">
            <div class="col form-group">
              <label for="newsTitle">Titulo</label>
              <input type="text" class="form-control" name="title" id="title" formControlName="title">
              <p>Text: {{ news.title }}</p>
            </div>
          </div>

          <div class="row">
              <div class="col form-group">
                <label for="subtitle">Sub Titulo</label>
                <input type="text" class="form-control" name="subtitle" id="subtitle" formControlName="subtitle">
              </div>
            </div>

          <div class="row">
            <div class="col form-group">
              <label for="article">Noticia</label>
              <textarea name="article" id="article" cols="30" rows="10" class="form-control" formControlName="article"></textarea>
            </div>
          </div>

          <div class="row">
            <div class="col form-group">
              <label for="picture">Picture</label>
              <input type="text" class="form-control" name="picture" id="picture" formControlName="picture">
            </div>
          </div>

        </div>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
        <button type="submit" class="btn orange-btn" *ngIf="editMode">Salvar Mudancas</button>
        <button type="submit" class="btn orange-btn" *ngIf="!editMode">Adicionar Noticia</button>
      </div>
    </div>
  </form>
</div>

news.model.ts

export class News {
    private id: string;
    private title: string;
    private subtitle: string;
    private picture: string;
    private article: string;

    constructor(id: string, title: string, subtitle: string, picture: string, article: string) {
        this.id = this.setID();
        this.title = this.setTitle(title);
        this.subtitle = this.setSubtitle(subtitle);
        this.picture = this.setPicture(picture);
        this.article = this.setArticle(article);
    }

    private setID() {
        function s4() {
            return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
        }
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    }

    getID() {
        return this.id;
    }

    setTitle(title: string) {
        return this.title = title;
    }

    getTitle() {
        return this.title;
    }

    setSubtitle(sub: string) {
        return this.subtitle = sub;
    }

    getSubtitle() {
        return this.subtitle;
    }

    setPicture(pic: string) {
        return this.picture = pic;
    }

    getPicture() {
        return this.picture;
    }

    setArticle(art: string) {
        return this.article = art;
    }

    getArticle() {
        return this.article;
    }
}

Answer №1

Check this out:

ngOnInit() {
   this.fetchRouteID(); // <- this method is synchronous
   this.article = this.newsService.retrieveSingleArticle(this.id) // <- so the id cannot be guaranteed
   ...
}

In my opinion, it would be better like this:

public currentArticle = null;
ngOnInit() {
   this.articleForm = new FormGroup({
      'title': new FormControl([]),
      'subtitle': new FormControl([]),
      'content': new FormControl([]),
      'image': new FormControl([])
   });
   this.initialize();
}
initialize() {
    this.route.params.subscribe(
      (params: Params) => {
      this.id = params['id'];
      this.editMode = !!this.id;
      this.article = this.newsService.retrieveSingleArticle(this.id).snapshotChanges().subscribe(
          snapshot => {
          this.currentArticle = snapshot.payload.val();
          this.articleForm.patchValue(this.currentArticle);
      });
    });
}

I hope you find this helpful.

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

Issue with Angular Filters: Difficulty Arises When Search Box is Cleared

I have a list of objects that I am displaying, and I added a search box to filter a column. When I enter a value, the data is filtered correctly. However, when I clear the search box, I do not get all the data back; I remain stuck with the initially search ...

A guide on utilizing the useEffect hook to dynamically update a button icon when hovering over it in a React application

Is it possible to change the icon on a button when hovering using useEffect? <Button style={{ backgroundColor: "transparent" }} type="primary" icon={<img src={plusCart} />} onCl ...

Trouble encountered during installation of Angular CLI: module.js - code 549

I recently encountered issues with Angular-CLI, so I decided to update it using the command $ npm install -g @angular/cli. However, after doing so, I am facing a new error message. Can anyone provide assistance with this problem? module.js:549 throw err ...

Angular error: "No directive was found with exportAs 'ngModel'." Ensure that FomsModule has already been imported

I'm encountering an issue where I am being advised to import "FomsModule", but it is already imported in my code. I attempted to include "ReactiveFormsModule" as well, but the problem persists. Here is the complete error message: src/app/components/ ...

Empty initial value of a number type input element in React using TSX

In the process of developing a react POS app using Typescript, I encountered an issue with calculating change when entering the amount of money received from a buyer. The problem arises when the first value passed to the change calculation logic is empty, ...

Error TS 2322 - The property 'id' is not present in the object of type '{ id: number'

Just starting out with Angular and TypeScript. I created a model with the same properties but encountered an error and am struggling to find a solution: TS2322: Type '{ id: number; model: string; plate: string; deliveryDate: string; deadline: st ...

Is it possible to interpret this in TypeScript?

I need assistance in understanding the following Code snippet. Can someone guide me through it? [Symbol.iterator](): IterableIterator<IPv4> { return this; } ...

Effective ways to display Typescript Arrays in Visual Studio popups consistently

Can Visual Studio consistently display Array<> types in TypeScript by encapsulating the type in Array rather than adding [] to the end? This would make long types much more readable and easier to follow, as it reads from left to right. https://i.sst ...

Manipulating the distinct look of the final element in an *ngFor loop

I am trying to enhance the appearance of the last line of controls generated by an iterator by making it disabled and somewhat invisible. Currently, my code is functioning well, as shown below. <div *ngFor="let item of data; let last = last;"> &l ...

Applying CSS styles to a shadow DOM element will not produce the desired visual

I'm encountering an issue while attempting to apply CSS to an element within a shadow-root by adding a class to it. In my Angular component, I have the following code: CSS .test { border: 1px solid red; } TS document.getElementById('my-div&a ...

Is it possible to include a component in an HTML file when the constructor of the component needs a parameter?

Recently delving into the world of AngularJs has been a captivating experience for me. I am aiming to develop a component with a constructor that accepts a parameter of type string. However, when I try to declare the selector on the HTML file, the componen ...

Error: Undefined object property 'name' TypeError: Undefined object property 'name'

Currently, I am working on a project that involves AngularJS and .net core. The main concept is to use Angular for presenting data and calling WebAPI from .net core. Presenting the data is not an issue, but I have encountered a problem with posting data as ...

Having difficulty implementing interval to a maximum of 2 minutes or until a certain condition is fulfilled in Angular

In my current scenario, I am working with two APIs - apiOne and apiTwo. When I call apiOne, it should return a response. If the response is successful, then I need to pass this response as a parameter to apiTwo. ApiTwo will then provide another response wh ...

Downloading videos from WebRTC getDisplayMedia in Angular 8 is not supported

Currently utilizing the NPM package in conjunction with Angular 8 found here: [ https://www.npmjs.com/package/webrtc-adapter ] to mimic the WebRTC getDisplayMedia functionality showcased here: [ ] I have successfully managed to initiate and terminate a r ...

Upgrading the subscription structure to fetch multiple details from a single initial request containing IDs

In my project, I am making multiple calls to two different backend services. The first call is to retrieve the IDs of "big" items, and then subsequent calls are made to get the details of each "big" item using its ID. I have explored options like concatMa ...

AngularFire-seed: Exploring the Effects of Function Arguments

Within the AngularFire-seed, a process unfolds in the run-definition: $rootScope.auth = loginService.init('/login'); Next, in the loginService-definition: init: function() { return auth = $firebaseSimpleLogin(firebaseRef()); }, The firebase ...

Encountering issues while trying to execute npm and node commands within Visual Studio Code

When I attempt to execute node commands in a command window, such as ng serve -o, everything works fine. However, when I try to do the same in VS Code, I encounter the following error message: ng : The term 'ng' is not recognized as the name of ...

What is the method for extracting children from a singular object in json-server rather than an array?

I am currently utilizing json-server as a mock-backend to fetch child data from a single object. The main table is called sentinel and the secondary table is named sensor https://i.sstatic.net/1BrRq.png https://i.sstatic.net/3lOVD.png It can be observ ...

Exploring the ckeditor5-typing plugin within CKEditor

Currently, I am in the process of developing a soft keyboard using CKEditor. One part of this involves transforming text upon input (which I have completed) and occasionally needing to delete a key (where I am currently facing challenges). Below is the sni ...

What are the steps to switch out the 'subscribe' method with 'mergeMap' or

I am trying to understand how I can replace the subscribe method with switchMap in my code. I have been searching for an example online but haven't found one yet. dialogRef.afterClosed().pipe(filter(result => !!result)).switchMap(result => { ...