Modifying a group of Components in Angular using a Service

Managing a set of Components involves changing their properties using a Service. The Components have a minimal model and are meant to remain compact. They are being rendered with *ngFor.

The Service possesses a large Object and should possess the ability to access and modify the properties of all components.

I've explored BehaviorSubjects to some extent, but I find it off-putting that a small component would be listening to a large object:

class Service{
    _bigModel: BehaviorSubject<object>
    bigObject: Observable<object> 
}

class Component{
   constructor(){
      bigObject.subscribe((newBigObject)=>{
          let partNeeded = newBigObject.key1.keya.blabla; 
          //...do sth with partNeeded; 
      }); 
   }
}

The drawbacks here:

  • Components will subscribe events that do not pertain directly to them.

  • Potentially loads the service's model into every small component, resulting in DOM bloat. Unclear if the BehaviorSubject is managing variable references properly.

  • The requirement for components to understand the service's model to function correctly.

A more logical approach, in my opinion, would be for the Service to manipulate Components directly:

class Service{
    componentList: Component[]; //unknown type of a Component
    changeComponent(idOfComponent, propertyToChange, value); 
    changeAllComponents(propertyToChange){
       for(c of componentList){
          let val = computeSomeValue(); 
          changeComponent(c, propertyToChange, val); 
       }
    }; 
}

class Component {
     someProperty: someType; 
     someOtherProperty: someType; 
}
//Template: 
<div [ngClass]="someOtherProperty">{{someProperty}}</div>

Is this approach viable and feasible? If so, how can I manipulate a specific Component from a service? If not, what are better alternatives?

Answer №1

It is recommended to go with the initial approach, as it mirrors the functionality of NGRX and other redux-based state containers/ stores, proving to be a reliable concept.

Let's dissect your bullet points:

Components may subscribe to events that are not specifically targeting them.

This issue can be avoided by utilizing the appropriate rxjs operators. For instance:

Observable<string> stringProperty = this.service.dataChanges()
                                        .pipe(
                                          // select the required property
                                          map(data => ...),
                                          // only emit if a different value is received
                                          distinctUntilChanged()
                                        );

There could potentially be a scenario where every small component loads the service model, resulting in DOM overload. Uncertain whether BehaviorSubject manages variable references, etc.

To address the first part, it is essential to carefully manage how the service data object is rendered onto the DOM to prevent overload. Regarding the second part, each emitted data object from the service ideally should be a unique and immutable entity. Failure to maintain immutability could lead to complications whereby components miss updates on the shared data object caused by modifications from other parts of the application.

The component needing knowledge of the Service model to function properly.

Is there a specific concern regarding this requirement?

Considering the drawbacks associated with the alternative approach:

  • If a common component interface is lacking, working without type checking becomes inevitable (e.g., accessing object properties through object["someProperty"])
  • If a common component interface is defined, exposing the service data object for maximum usability might not resolve all issues highlighted in the third bullet point.
  • Maintaining a registry of components adds complexity, necessitating accurate registration/de-registration of instances.

Answer №2

It is not advisable to directly make changes to a Component from a Service. Services should primarily handle data and logic that are not tied to a specific view or Component.

Services should not be aware of all Components in your app, but rather offer access to necessary data through Observables. Components can then choose which Service to interact with and which data to subscribe to.

One approach is to have your Service provide different parts of a large model as separate Observables. Components can then select the relevant Observable they require. Remember, Components utilize Services, not the other way around.

If needed, consider breaking down your big model into smaller models or Subjects. Perhaps reevaluating your design could help determine if having all data within one object is essential.

class Service{
  _model1: BehaviorSubject<object>
  object1: Observable<object>

  _model2: BehaviorSubject<object>
  object2: Observable<object>

  _model3: BehaviorSubject<object>
  object3: Observable<object>   
}

Alternatively, you can expose certain sections of your large model as individual Observables:

class Service{
  _bigModel: BehaviorSubject<object>
  bigObject: Observable<object>

  object2 = _bigModel.pipe(
    map(newBigObject => newBigObject.key1.keya.blabla),
    distinctUntilChanged()
  )

  object3 = _bigModel.pipe(
    pluck('key2', 'keya', 'blabla'),
    distinctUntilChanged()
  )  
}

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

How can I silence the warnings about "defaultProps will be removed"?

I currently have a next.js codebase that is experiencing various bugs that require attention. The console is currently displaying the following warnings: Warning: ArrowLeftInline: Support for defaultProps will be removed from function components in a futur ...

Struggling to retrieve object values through useContext? Consider implementing useReducer in conjunction with useContext for more efficient state management

I'm facing an issue while trying to access my dispatch functions and states from the useContext. Strangely, when I attempt to destructure the context object in order to access them directly, I receive an error message stating that it does not exist (E ...

Unable to establish a cookie on an Angular client hosted anywhere except for 'localhost'

Utilizing an express server as the backend and an angular client as the frontend. The express server is hosted on port 3001, while angular is on port 4200. Everything works perfectly when running on localhost. However, when attempting to host Angular on a ...

Is it possible to have Angular and Node.JS Express running on the same port?

It seems like I may have a duplicated question, but I'm struggling to figure out how to properly configure and run the frontend and backend together. I've looked into solutions on this and this questions, but I'm still confused. Currently, ...

Instructions on setting a photo as a background image using a text-based model

I'm a beginner with Angular so I may have a simple question. I am using an image from the Google API, which is not a URL. How can I set this image as the background-image in a CSS tag that only accepts URIs? Thank you! ...

Debate surrounding the use of .next() in conjunction with takeUntil

Recently, I've observed a change in behavior after updating my rxjs version. It seems that the .next() method this.ngUnsubscribe$.next(); is no longer functioning as it used to: export class TakeUntilComponent implements OnDestroy { // Our magical o ...

To handle the input field efficiently in Angular, consider utilizing two <input type="radio"> elements

I am facing a challenge in my Angular 5 project where I have two radio buttons and need to dynamically update the value of formControlName and placeholder for an input field based on the selected radio button. How can I tackle this issue? <div clas ...

How to use Typescript to find the length of an array consisting of either strings or

I am trying to determine the length of a string or array, stored in a variable with the data type var stepData : string | string[]. Sometimes I receive a single string value, and other times I may receive an array of strings. I need the length of the array ...

Challenges with using async await alongside synchronous functions

I'm currently navigating through a library that utilizes async functions and feeling a bit overwhelmed. I'm attempting to call a function that should return a string, but I'm hitting some roadblocks. As I understand it, the ZeroEx library fu ...

Swagger Issue Resolved: Restriction on Number of Params Set

After setting up this option for my route, I noticed that when accessing the first parameter (page), it correctly returns the value entered in Swagger UI. However, when trying to access the second parameter (genre), it seems to interpret it as a string &ap ...

Unable to incorporate angular2-datatable into VS2015 project using npm as a package manager

I'm currently working on developing a web application using Angular 2 and ASP.NET 5. In order to create a table, I decided to install the angular2-datatable package using npm. I added the dependency below to my package.json file: "angular2-datatable" ...

Instructions for obtaining the most recent event using the `fromEvent` function

I'm having trouble capturing the final event generated by the keyup event using rxjs. Every time, my console is filled with multiple log messages. Here's the Angular directive I'm working with: import { Directive, AfterContentChecked, Eleme ...

Preserving ES6 syntax while transpiling using Typescript

I have a question regarding keeping ES6 syntax when transpiling to JavaScript. For example, if I write the following code: class Person { public name: string; constructor(name: string) { this.name = name; } } let person = new Person('John D ...

"Utilizing the Ionic side menu to activate a function in the view.ts file

In my app, there is a menu located in the main app.component.html <ion-app> <ion-split-pane contentId="main-content"> <ion-menu contentId="main-content" type="overlay"> <ion-content> ...

Vue: Defining typed props interface including optional properties

I created a method that I want to be accessible on all my Vue instances, so I can use it in case of an error and display a specific error component. Similar to the functionality provided by vue-error-page. Since I am working with typescript, I now want to ...

What could be causing NgModel to fail with mat-checkbox and radio buttons in Angular?

I am working with an array of booleans representing week days to determine which day is selected: selectedWeekDays: boolean[] = [true,true,true,true,true,true]; In my HTML file: <section> <h4>Choose your days:</h4> <mat-che ...

Troubleshoot an Office add-in using VS Code

Looking for guidance on Office add-ins and VS code... I recently went through the steps outlined in this tutorial by Microsoft to create an Excel custom functions add-in. To debug it using VS code, I had to select TypeScript as the script type while creat ...

A guide on passing an ngFor object variable to a function

I am trying to display subcategories for each category in my *ngFor list. The subcategory data is retrieved from Firebase using the category_id, but I am struggling to pass the category_id from the HTML to the function for every member of the category. ho ...

Trying to access @PreAuthorize with the role 'ROLE_ADMIN' is resulting in a Forbidden error

When restricting access to a method for only admins, one can make use of @PreAuthorize("hasRole('ROLE_ADMIN')"). Below is an example implementation: @CrossOrigin(origins="http://localhost:4200") @RestController @RequestMapping("/api/v1") public ...

Utilizing arrayUnion function in Firestore with Angular

My journey to learn Firestore has hit a roadblock. The documentation on AngularFire and FieldValue hasn't been much help. Every time I try to use FieldValue, it throws an error saying "FieldValue does not exist." const userRef = this.firestore.collect ...