Modifying a specific object attribute within an array of objects without directly accessing the entire array during the modification process

I am currently working on a project that utilizes Angular with Ionic 3. In this application, I have an array of questions that can be edited by the user if any mistakes are made.

The issue arises when I attempt to edit a question from the array. Any changes made during editing immediately reflect in the array displayed using *ngFor.

Before delving into the code, let me provide some context:

Upon creating and saving a question, the questions and answers are displayed in a list using *ngFor. From there, users can choose to edit or delete a question.

https://i.sstatic.net/7KNAP.png

When I click "edit," the question data is sent back to the form for editing. As I make edits, the answer on the right side updates dynamically. However, upon canceling the editing process, the changed value persists, whereas I'd like it to revert only after clicking save again.

https://i.sstatic.net/dIE8g.png

Here's a snippet of my HTML code:

<ion-row>
<ion-col col-12 col-md-6>

  <!-- THE FORM USED FOR CREATION AND EDITING -->
  <form novalidate [formGroup]="novaQuestaoMultipla">
    <ion-item no-lines>
      <ion-label stacked>
        Title
        <p text-right [ngClass]="{'green': novaQuestaoMultipla.get('titulo').value.length <= 70, 'yellow': novaQuestaoMultipla.get('titulo').value.length > 70 && novaQuestaoMultipla.get('titulo').value.length <= 140, 'red': novaQuestaoMultipla.get('titulo').value.length > 140 }">({{novaQuestaoMultipla.get('titulo').value.length}}/160)</p>
      </ion-label>
      <ion-textarea rows="4" formControlName="titulo" maxlength="160" [ngClass]="{'campo-invalido': !novaQuestaoMultipla.get('titulo').valid && (novaQuestaoMultipla.get('titulo').dirty || multiplaInvalida)}"></ion-textarea>
    </ion-item>
    <div *ngIf="titulosQuestoesFiltradas.length > 0" class="titulos" text-center>
    </div>

    <p text-center class="texto-campo-invalido" *ngIf="!novaQuestaoMultipla.get('titulo').valid && (novaQuestaoMultipla.get('titulo').dirty || multiplaInvalida)">Invalid</p>
    <br />

    <ion-row class="campo-respostas">
      <ion-col text-right col-8>
        <h3>Answer Options</h3>
      </ion-col>
      <ion-col text-left col-4>
        <button ion-button round icon-only color="gold" [disabled]="quantidadeRespostasMultipla == 5" (click)="novaOpcao()">
          <ion-icon name="add"></ion-icon>
        </button>
      </ion-col>
    </ion-row>
    <ion-row *ngIf="quantidadeRespostasMultipla > 0">
      <ion-col text-left>
        <h3>Text</h3>
      </ion-col>
      <ion-col text-right>
        <h3>Correct</h3>
      </ion-col>
    </ion-row>

    <ion-row *ngFor="let r of respostasMultipla; let i = index" class="resposta-multipla" align-items-end>
      <ion-col col-12 col-md-8>
        <ion-item no-lines>
          <ion-label stacked>
            <p text-right [ngClass]="{'green': r.texto.length <= 20, 'yellow': r.texto.length > 20 && r.texto.length <= 35, 'red': r.texto.length > 35 }">({{r.texto.length}}/40)</p>
          </ion-label>
          <ion-input type="text" maxlength="40" [(ngModel)]="r.texto" [ngModelOptions]="{standalone: true}"></ion-input>
        </ion-item>
      </ion-col>
      <ion-col col-6 col-md-2 text-center>
        <button ion-button color="danger" round (click)="excluirOpcao(i)">
          <ion-icon name="ios-trash-outline"></ion-icon>
        </button>
      </ion-col>
      <ion-col col-6 col-md-2>
        <ion-item no-lines>
          <ion-toggle (ionChange)="escolheuCorreta(r, index)" [(ngModel)]="r.correto" [ngModelOptions]="{standalone: true}"></ion-toggle>
        </ion-item>
      </ion-col>
    </ion-row>
    <br />

    <div text-center>
      <button ion-button round color="danger" *ngIf="hasEditQuestao" (click)="cancelarEdicao()">Cancel Editing</button>
      <button ion-button round (click)="salvarMultipla()">Save Question</button>
    </div>
  </form>
</ion-col>

<!-- DISPLAY OF QUESTIONS WITH OPTIONS TO EDIT-->
<ion-col col-12 col-md-6 >
  <ion-item *ngFor="let q of questoesEscolhidasExibicao; let i = index" text-wrap>
    <h2>{{q.titulo}}</h2>
    <p *ngFor="let r of q.respostas">{{r.texto}}</p>
    <button ion-button outline color="gold" item-end icon-only (click)="editarQuestao(q, i)">
      <ion-icon name="create"></ion-icon>
    </button>
    <button ion-button outline color="danger" item-end icon-only (click)="removerQuestao(q, i)">
      <ion-icon name="trash"></ion-icon>
    </button>
  </ion-item>
</ion-col>

And below is my JS function for editing:

// EDIT QUESTION
editarQuestao(questao, index) {
    // Creating a temporary variable to store the question data without maintaining references
    const respostas = questao.respostas;

    // Saving the ID and index of the question
    this.hasEditQuestao = questao.id;
    this.editQuestaoIndex = index;
    this.questData = questao;

    // Updating the form with the question data
    this.novaQuestaoMultipla.get('titulo').setValue(questao.titulo);
    this.respostasMultipla = respostas;
    this.quantidadeRespostasMultipla = this.respostasMultipla.length;
}

Just to clarify, I'm using an object to push data once to Firebase and an array for display purposes. How can I ensure that edits made to a question do not automatically apply, allowing for updates only upon clicking save? Is there a way to remove references from the array?

Answer №1

Issues arising from using [(ngModel)] in a 2-way data binding context can be easily avoided by switching to Reactive forms.

By utilizing Angular reactive forms, you embrace a more proactive approach to programming where the data exchange between server-side non-UI data and client-side form elements is explicitly managed. This allows for better control over the states and values of HTML controls on the screen, while also offering benefits such as ease of implementation of reactive patterns, testing, and validation.

In Reactive forms, the manipulation and creation of form control objects take place within the component class itself. With direct access to both the underlying data model and the structure of form controls, it becomes possible to seamlessly push data model values into the form controls and extract user-altered values when needed. Additionally, the component can monitor changes in form control state and respond accordingly.

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

Discover the secret to creating an efficient TypeScript function that offers autocomplete with enum values

I'm attempting to achieve a scenario similar to the code below: enum Color { RED="RED", GREEN="GREEN", BLUE="BLUE" } function setColor(color:Color) { } However, when I attempt to call the function like this: setColor("RED"), I encounter the ...

Lint error: Expression detected when an assignment or function call was expected according to @typescript-eslint/no-unused-expressions rule

Encountering an error message while utilizing the code snippet countryMap.get(tradeId)?.map((companies) => {: The error states 'Expected an assignment or function call and instead saw an expression @typescript-eslint/no-unused-expressions' con ...

Validating forms in Angular 6 upon submission

In my current scenario, I am faced with the need to validate fields only upon submission. Due to the complexity of my form, which includes FormArrays and FormGroup, I have segmented it into multiple components such that each component represents a distinc ...

Utilizing variables for Protractor command line parameters

I am struggling to make variables work when passing parameters as a string in my code. Conf.ts params: { testEnvironment: TestEnvironment.Prod, }, env.ts export enum TestEnvironment { Dev = 'dev', QA = 'qa', Prod ...

Angular 4 application coming to a standstill whenever a network request is triggered in Internet Explorer

My app has the ability to make calls and update the screen based on incoming data. However, I have encountered a major issue - the app is extremely slow when using Internet Explorer (I.E), so much so that scrolling is nearly impossible. Surprisingly, the s ...

Fetch the JSON data and pass it to the ITest

Objective: Retrieve data from a JSON file and store it in the "test: ITest[]" array, then display it using console.log Challenge: The code is not functioning correctly. What element am I overlooking? Background: I am new to Angular Thank you! Ser ...

Why does Typescript's 'await' seem to not wait as expected?

Apologies for the rookie mistake, I am currently transitioning from a C# background to Ionic, which may be causing some confusion on my end. I'm working on retrieving a stored token from Ionic storage but I'm struggling with understanding promise ...

Error: An unexpected token < was caught in the TypeScript Express code

Using TypeScript to compile and run an Express server that simply serves an HTML file. However, encountering an error in the response under the network tab in Chrome for app.js: Uncaught SyntaxError: Unexpected token '<' Below is the server c ...

Activating external actions from the ngrx store through component interactions

I am working with a module that contains a modal where I perform some form tasks, and the modal has its own small feature store. After completing my work (specifically, when a save is successful), I need to trigger an output event in order for the parent ...

Unit testing in NodeJS using Mocha where global variables injected from WebPack are utilized through the DefinePlugin feature

When using the WebPack.DefinePlugin to inject global variables, I've encountered an issue with integrating them into my Mocha unit tests. The tests don't recognize the global variables, and the Mocha documentation doesn't provide clear guida ...

Angular retrieve - nested mapping

Having trouble with calling an API method inside a map function, as the second API call is not being made without any console errors appearing. //The following method is not getting called - The API method is not being triggered this.getUserPermissions(lo ...

Utilizing TypeScript namespaced classes as external modules in Node.js: A step-by-step guide

My current dilemma involves using namespaced TypeScript classes as external modules in Node.js. Many suggest that it simply can't be done and advise against using namespaces altogether. However, our extensive codebase is structured using namespaces, ...

I'm experiencing an issue with my website where it appears broken when I build it, but functions properly when I use npm run dev in Next

For my project, I have utilized NextJs, Tailwind, React, and Typescript. It is all set and ready to be hosted. After using "output: export" in next.config.js and running npm run build, the process was successful. However, when viewing my website locally, I ...

How to prevent duplicate database entries in Angular forms?

Currently, I am working on a project using Angular and TypeScript. The goal is to retrieve a list of users from an API and allow for the addition of new users. However, I am struggling with determining how to verify if a user with a specific name already e ...

Angular TypeScript DatePipe throwing a "invalidPipeArgument: 'Cannot convert "03:45:00" into a date'" error

My goal is to display just the hours and minutes of a Date object retrieved from an API on the frontend as 'shortTime' format. However, when I attempt to do so with the following code: <h2>{{data.scheduleTime | date:'shortTime'} ...

Modifying Angular 4 instance field in the code does not update the template as expected

One issue I am encountering is that changes in the instance variable in my component class are not automatically reflected in my template file unless I explicitly call ref.detectChanges The method signInWithGoogle in my auth service is called from the com ...

There is no definition for Angular 4 Material stepper tags

After thoroughly studying the Angular Material stepper documentation, I encountered an issue when attempting to integrate the HTML Material stepper tag into my Angular application. The error message stated that it's not a known element. HTML: <ma ...

Discover the myriad of possibilities created by combining arrays

I am working on a code snippet that aims to generate an array containing all possible combinations between two or more arrays. However, I am encountering a specific issue. getCombn(arr: string | any[], pre?: string | undefined) { pre = pre || ' &a ...

Angular: Enhancing URL links

Recently, I discovered a function in my code that allows me to cycle through different pictures and change the URL accordingly. The initial URL is obtained using angular routes, where the "domain" parameter consists of the domain.id and the domain.category ...

Tips for retrieving refreshed information following modifications via a POST request in Angular 2

I've scoured the web extensively, but I just can't seem to grasp how to retrieve updated data from the database. I'm currently learning Angular 2, and my predicament lies in fetching data from a table named 'branches' using PHP wit ...