Tips on using services for storing and fetching list data in Angular

I currently have two components, the "add-expense" component and the "view-list" component. The "add-expense" component collects expense details from a form and stores them as an object. My goal is to add this object to an empty list within the "expense-list" service. When I later load my "view-list" component, all the values in the list maintained by the "expense-list" service should be displayed.

I've read about using rxjs BehaviorSubject for this purpose. However, most examples demonstrate how to store and update strings using .next(). In my case, I need to work with a list.

import { BehaviorSubject } from "rxjs";
export class ExpenseListService{
    expenseList:BehaviorSubject<any[]> = new BehaviorSubject([]); //error
    
}

How do I define a BehaviorSubject that takes a list as its type? How can I add/push elements to this list maintained by the BehaviorSubject? And how do I subscribe to changes?

Answer №1

It's strange that you're encountering an error with that code because it runs smoothly on my end. Check out the code here

If you have a behavior subject containing an array and you need to add a new value to it, you can utilize the spread operator to create a new array with the additional item.

export class ExpenseListService{
  expenseList:BehaviorSubject<any[]> = new BehaviorSubject([]);
    
  add(item) {
    this.expenseList.next([...this.expenseList.value, item]);
  }
}

This action will trigger the behavior subject to emit a fresh array inclusive of the newly added value.

Answer №2

When you initialize a variable with an assertion, there is no need to explicitly set the type as it will be determined by the value you provide.

For example, you can simply write:

expenseList = new BehaviorSubject([]);

However, it is considered best practice to specify the type of the array like so:

expenseList = new BehaviorSubject<ListModel[]>([]);

If you want to append data to the existing array, you should do it as follows:

const newValue = 'new value';
const newContent = [...this.expenseList.getValue(), newValue];

this.expenseList.next(newContent)

To subscribe to the observable:

 expenseList.asObservable().subscribe(() => ....) 

Answer №3

Edit: AdrianBrand's Solution is more concise and appropriate, and I recommend using it

If my understanding is correct, you are looking to:

  1. add a single object from the "add-expense" component to an array
  2. subscribe to an observable in your "view-list" and retrieve the array containing all previously added values?

Using a BehaviourSubject as you suggested may not be effective, as it would result in emitting a new array each time and losing the previously added values.

I believe a straightforward store approach would be most suitable: (Caution: This method only appends values to the list if the observable is subscribed to.)

export class ListService {
  // No initial value:
  private expenseListSubject: Subject<any> = new Subject<any>();
  // The empty initial array:
  private initialList = [];

  // Pass the emitted subject values through scan to accumulate them:
  public expenseList$: Observable<any[]> = this.expenseListSubject.pipe(
    scan((acc: any[], currentValue: any) => ([...acc, currentValue]), this.initialList)
  );
  
  public addToList(value: any) {
    this.expenseListSubject.next(value)
  }
}

To subscribe in your component:

export class ViewList {
  constructor(private listService: ListService) {}
  
  this.listService.expenseList$.subscribe((currentArray) => console.log(currentArray))
  }

To append a new value:

export class AddExpense {
  constructor(private listService: ListService) {}

  this.listService.addToList(value)
  }

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

The callback function `(err: any, data: any) => void` does not share any properties with the type `RequestInit`

Inspired by the tutorial at , I am working on a time-based visualization. I am currently using version "d3": "^5.4.0". Here is the code snippet: d3.json('http://127.0.0.1:5000', function (err, data) { if (err) throw err; // Cre ...

Example of Using Bluebird in a Chain of Catch and Then Functions

I am struggling to understand how promises work in my code flow setup. I want to create a register function with multiple steps, but I'm unsure of what is achievable and what is not. Imagine I have a register function where I need to: register a u ...

Navigate back to the main page using a tab

Is there a way to navigate back to the rootPage that is defined in the appComponent when using tabs? I have found that the setRoot method does not function as I anticipated. When used on a Tab page, the navigation stack is not cleared. Instead of the navig ...

"Implementing a retry feature for Angular http requests triggered by a button

Imagine having a situation where a component has a method that triggers an http request defined in a service. The observable is subscribed to within the component: Component: fetchData() { this.apiService.fetchDataFromServer().subscribe( respo ...

Create a function that accepts a class as a parameter and outputs an object of that class

I am working on an instantiator function that generates an instance of a provided class: declare type ClassType = { new (): any }; // known as "ParameterlessConstructor" function createInstance(constructor: ClassType): any { return new constructor(); ...

Error: 'ngForOf' is not recognized as a valid property of the 'tr' element

Since this afternoon, I've been facing a challenge that I can't seem to grasp. The issue lies within a service I created; in this file, there is an object from which I aim to showcase the data in a loop. An error message is displayed: NG0303: C ...

Using RxJS switchMap in combination with toArray allows for seamless transformation

I'm encountering an issue with rxjs. I have a function that is supposed to: Take a list of group IDs, such as: of(['1', '2']) Fetch the list of chats for each ID Return a merged list of chats However, when it reaches the toArray ...

Angular - Is there a specific type for the @HostListener event that listens for scrolling on the window?

Encountering certain errors here: 'e.target' is possibly 'null'. Property 'scrollingElement' does not exist on type 'EventTarget'. What should be the designated type for the event parameter in the function onWindow ...

Error: Virtual script not located; possibly absent <script lang="ts" / "allowJs": true / within the jsconfig.json.volar

https://i.sstatic.net/dFaVQ.png I noticed an error in my footer component in VueJs (TypeScript template) as depicted by the image showing blue squiggly lines. ...

Aurelia's navigation feature adds "?id=5" to the URL instead of "/5"

I have set up my Aurelia Router in app.ts using the configureRouter function like this: configureRouter(config, router: Router) { config.map([ { route: ['users', 'users/:userId?'], na ...

Is your Express router failing to handle post requests properly?

I've set up my development environment using angular-cli and webpack, with the following configuration in package.json: "dependencies": { "@angular/common": "^4.0.0", "@angular/compiler": "^4.0.0", "@angular/core": "^4.0.0", "@angular ...

Error: Unable to modify a property that is marked as read-only on object '#<Object>' in Redux Toolkit slice for Firebase Storage in React Native

Hey there! I've been working on setting my downloadUrl after uploading to firebase storage using Redux Toolkit, but I'm facing some challenges. While I have a workaround, I'd prefer to do it the right way. Unfortunately, I can't seem to ...

The connection could not be established due to an error, and there was also a failure in loading the resource with the error message "net::ERR

I'm attempting to implement this particular example utilizing SignalR with .NET Core and Angular while incorporating the ABP Framework, However, upon running the example, I continuously encounter the following Errors in the browser console: https:// ...

Steps for generating an instance of a concrete class using a static method within an abstract class

Trying to instantiate a concrete class from a static method of an abstract class is resulting in the following error: Uncaught TypeError: Object prototype may only be an Object or null: undefined This error occurs on this line in ConcreteClass.js: re ...

React textarea trigger function on blur event

https://codesandbox.io/s/react-textarea-callback-on-blur-yoh8n?file=/src/App.tsx When working with a textarea in React, I have two main objectives: To remove focus and reset certain states when the user presses "Escape" To trigger a callback function (sa ...

What is the best way to share type definitions between a frontend and a Golang backend application?

I utilized typescript for both the frontend (Angular) and backend (Express). To ensure type definitions are shared, I created a file called shared-type-file.ts. interface Kid{ name: string; age: number; } By then running npm install in both the front ...

Ways to expand a TypeScript interface and make it complete

I'm striving to achieve the following: interface Partials { readonly start?: number; readonly end?: number; } interface NotPartials extends Partials /* integrate Unpartialing in some way */ { readonly somewhere: number; } In this case, NotPar ...

Optimal Method for Inserting Lengthy Data using Sequelize

I have a table containing 20 elements. Is there a more concise way to input data into Sequelize without listing each element individually like this: Sequelize.create({ elem1: req.body.eleme1, elem2: req.body.eleme2, elem3: req.body.eleme3, elem4: ...

VS Code using Vue is displaying an error message stating: The property '' does not exist on type '{}'.ts(2339)

While working in Visual Studio Code, I came across the following code snippet: <script lang="ts" setup> const parseCSV = () => { // Code omitted for brevity } } </script> <template> <button @click="parseCSV ...

Automatically identify the appropriate data type using a type hint mechanism

Can data be interpreted differently based on a 'type-field'? I am currently loading data from the same file with known type definitions. The current approach displays all fields, but I would like to automatically determine which type is applicab ...