What is the most straightforward way to make a property observable in terms of syntax?

There are countless tutorials out there demonstrating various ways to implement observables in Angular, but many of them are too complex for my needs. Some are outdated and no longer applicable.

Let's assume I have a service with a single property called numChickens, and I want components to be able to subscribe to that property. Do I really need to go through a million convoluted statements to make this work?

Below is the code for the service:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})

export class ChickenService {

  public chickens: number; 

  constructor() { }

}

...and here is the code for a component that will utilize the observable:

import { Component, OnInit } from '@angular/core';
import { ChickenService } from '../chicken.service';

@Component({
  selector: 'app-chickendisplay',
  templateUrl: './chickendisplay.component.html',
  styleUrls: ['./chickendisplay.component.scss']
})

export class ChickenDisplayComponent implements OnInit {

  constructor(public cs: ChickenService) {
  }

  ngOnInit() {
  }

}

In Angular 6, what is the simplest and most readable way to expose the chickens property in ChickenService so that a component class can access the value of that property as an observable stream? Or so that a component template can display the value using an async pipe?

I want to emphasize - please, no lengthy closures followed by claiming it's simple.

I am not just asking this question for myself, but also for others who are struggling to understand observables amidst the overwhelming and outdated tutorials. Simplifying this solution will benefit future developers. Thank you.

Answer №1

To simplify the process, consider creating a private Subject and utilizing it to generate your public Observable.

In the provided code snippet, I've established get and set functions for the chickens variable. This arrangement ensures that each update made, such as service.chickens = 10, will automatically initiate a new event on the Observable stream with the updated value.

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class ChickenService {

  private _chickens: number; // Keeping this private for exposure via get/set
  private chickenChange$ = new Subject<number>(); // Facilitating Observable creation
  public chickens$ = this.chickenChange$.asObservable(); // Definition of our Observable

  constructor() { }

  set chickens(val: number) {
    this._chickens = val; // Setting the new value
    this.chickenChange$.next(val); // Triggering the subject, thereby activating the Observable
  }

  get chickens() {
    return this._chickens;
  }

}

Answer №2

To turn it into a stream, all you need to do is add a single line of code:

import { Injectable } from '@angular/core';
import { of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ChickenService {

  public chickens: number; 
  public chickens$ = of(chickens);
  constructor() { }
}

Keep in mind that this won't allow you to update the value easily, so the benefits may be limited.

If you want the ability to update the value and have those changes reflected in the UI, you can use a Subject and subscribe to the stream:

import { Injectable } from '@angular/core';
import { of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ChickenService {

  public chickens: number;

  public chickens$ = chickensSub$.asObservable();
  private chickensSub$ = new BehaviorSubject();
  constructor() { }

  updateChickens(value: number) {
    this.chichensSub$.next(value);
  }
}

Remember: Try to avoid using Subjects unless necessary.

Answer №3

If you're looking to make a property observable, using a decorator might be a good option. The process of setting this up can be a bit tedious, but once it's done, you can easily implement it like so:

export class ChickenService {
  @Observablize()
  public chickens: number; 

  public chickens$: Observable<number>;
}

Now, let's dive into how the decorator would be implemented (note: this code is untested).

function Observablize() {
  return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
    const observableKey = propertyKey + "$";
    const privateKey = propertyKey + "_";

    Object.defineProperties(target.prototype, {
      [observableKey]: { value: new Subject<any>() },
      [propertyKey]: { 
        get() { return this[privateKey]; },
        set(v) { this[privateKey] = v; this[observableKey].next(v); }
      }
    });
  };
}

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

Typescript - Iterating through CSV columns with precision

I am currently facing a challenge in TypeScript where I need to read a CSV file column by column. Here is an example of the CSV data: Prefix,Suffix Mr,Jr Mrs,Sr After researching on various Stack Overflow questions and reading through TypeScript document ...

Having trouble with ngx-slick-carousel? Need help with managing the next and prev buttons?

I'm facing an issue where the next and prev buttons are not visible on my carousel component, even though they seem to be working properly. When I inspect the elements, I can see that the buttons are there. How can I make them visible? Here is my cod ...

Can Angular be used to dynamically filter a JSON object to display only the fields that match a specified filter text?

Sorry if this question has already been asked; I couldn't find the solution. Here is my issue: In my Angular app, I am retrieving a complex JSON object from a web service. I then present this JSON object to the user in tree format using ngx json vie ...

Utilize environment variables to access system information when constructing an Angular 2 application

In order to build my Angular application, I want to utilize a single system variable. System Variable server_url = http://google.com This is how my Environment.ts file looks: export const environment = { production: false, serveUrl: 'http://so ...

TypeScript - Minimize redundancy when defining types for a class and its constructor arguments

Here is a class structure I am currently using: class Person { id?: string = uuid(); name: string; constructor(data: Person) { _.merge(this, data); } } The 'uuid' function generates an id and '_' refers to loda ...

Error in Ionic Cordova Build prod: Module "." not found - Requires Typescript version >3

After updating my ionic project and all dependencies, I encountered an error when trying to build a --prod android apk: Uncaught Error: Cannot find module "." at vendor.js:1 at vendor.js:1 at Object.<anonymous> (vendor.js:1) at e (vendor.js:1) at Ob ...

Attempting to update Typescript on Linux Mint using npm

I am currently using typescript 2.1.4 and npm version 6.7.0. Realizing that 2.1.4 is quite outdated, I decided to try and update it. Here are the steps I took, which unfortunately did not work: $ sudo npm update -g typescript $ tsc --version Vesion 2.1. ...

"Utilizing the `useState` function within a `Pressable

Experiencing some unusual behavior that I can't quite figure out. I have a basic form with a submit button, and as I type into the input boxes, I can see the state updating correctly. However, when I click the button, it seems to come out as reset. Th ...

Utilizing the Composition Root concept in a TypeScript Express application

I am trying to grasp the concept of implementing a composition root in a project. Based on my research, improper usage of the composition root (such as referencing it in multiple places within your application code) can lead to the service locator antipat ...

How can I bind an event for changing innerHTML in Angular 2?

Is there a way to implement something similar to this: <div [innerHTML]="content" (innerHTMLchange)="contentInit()"></div> Currently, I have a variable content that is updated by a service fetching a string from my express server. The content ...

The inner workings of Angular 2: uncovering what occurs once we navigate to http://localhost:4200 on our browser

Could anyone provide a detailed explanation of the startup process for an Angular2 project? For example, after creating a sample project using Angular CLI: Run 'ng new my-test-app' Navigate to 'cd my-test-app' Start the server with & ...

Angular 5: How to Calculate the Sum of Two Numbers and Handle NaN Output

I have encountered an issue where I am trying to multiply two numbers and add another number, but the output is displaying as NaN. How can I troubleshoot and solve this problem? Below is the code snippet: medicines = [new Medicine()]; this.sum = 0;// su ...

Create an array that can contain a mix of nested arrays and objects

Working on my project using Angular and TypeScript includes defining an array that can contain arrays or objects. public arrangedFooterMenu: IMenuItemType[][] | IMenuItemType[] = []; typesOfData.forEach(type => { let filteredData: IMenuItemType | ...

angular2 ngif does not effectively conceal HTML elements when set to false

In the HTML file, I have the following code: <p *ngIf="!checklistsready"> not ready </p> <p *ngIf="checklistsready"> Ready </p> And in my TypeScript file, it looks like this: checklistsready: boolean = false; constructor( ...

Creating an ngFor loop with an ngIf condition for true and false bot conditions

I am attempting to troubleshoot an issue where if my condition is true, return true. If my condition is false, return false. However, currently, if only one condition is true, all conditions are being applied as true. Please help me resolve this problem. ...

The element is implicitly assigned the 'any' type due to the inability to use an expression of type to index the element

Check out my TS playground here // I have colours const colors = { Red: "Red", Blue: "Blue", Green: "Green" } type TColor = keyof typeof colors; // Some colours have moods associated with them const colorsToMood = { ...

Ensure that the dynamically inserted <title> tag remains intact in Angular even when the page is re

Can the dynamic title tag be preserved when the page is refreshed? When I refresh the page, the title tag reverts back to the original one specified in the index.html temporarily before switching back to the dynamically added one. I want the title tag to ...

Troubleshooting radio name input binding in Angular 2

In Angular2, there seems to be an issue with one-way binding to the name attribute for a group of radio inputs. For example: <div [ngFormModel]="form"> <input type="radio" [name]="varName" [id]="id1" ngControl="r1"> <input type="radio" ...

Trigger the ngOnInit() function of the app component for a second time by clicking on a link

Currently, I am in the process of restructuring an Angular project and came across the following functionality. Inside app.component.ts file ngOnInit() { this.portfolioID = Number(sessionStorage.getItem('portfolioID')); console.log(this.portfol ...

Encountering difficulties while configuring an Angular project on my local machine from GitLab (unable to install npm locally)

There are deprecated warnings for certain npm packages in the Node.js environment. It is advised to upgrade to specific versions to avoid ReDos regression issues: [email protected]: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 [email  ...