Guide on subscribing to an object from a service in Angular 2/5

I am facing an issue where I need to update my property component with data received from the server.

In the Service, I have implemented something like this:

private events: Event[] = [];
eventChanged = new Subject<any>(); // Edit: added an observable

constructor(private http: HttpClient) { 
    this.http.get<Event[]>(this.baseUrl)
    .subscribe(events => this.events = events);
  this.eventChanged.next(this.events.slice()); //Edit: added an information to subscribers that events list changed
}
getEvents(): Observable<Event[]> {
    return this.eventChanged.asObservable();
}  // Edit: now I use this method to enables subscribers to observable

        /* I don't use that method after Edit
        showAllEvents(): Event[] {
            return [...this.events];
        }
        */

Next, I used the showAllEvents() method in my component like so:

private events: Event[] = [];
private calendarEvents: CalendarEvent[] = [];
subscription: Subscription; // Edit: Added a Subscription

    getInterestedEvents() {
       // this.events = this.el.showAllEvents(); <-- I changed it into observable
   this.subscription = this.el.getEvents()
    .subscribe(
    (events) => {
      this.events = events;
    });
    this.events.forEach(eachEvent => {
        let calendarEvent: CalendarEvent = {
            start: subDays(startOfDay(new Date()), 1),
            end: addDays(new Date(), 1),
            title: eachEvent.name,
            color: colors.red
        }
        this.calendarEvents.push(calendarEvent);
    })
}

However, I am unsure how to make this.events wait for the data from the service. Any suggestions? Each page presents a different challenge, and I am feeling quite puzzled.

Edit

Despite implementing subscribe and observable, this.el.getEvents().subscribe... still does not return any data in my component.

Answer №1

To stay informed about any changes in the event list, you can establish a subscription within your service.

eventChanged = new Subject<Event[]>();

In your component, make sure to subscribe to the eventChanged observable to react to any modifications in the event list.

this.subscription = this.eventService.eventChanged
  .subscribe(
    (events: Event[]) => {
      this.events = events;
    }
  );

Additionally, include a next method in your service that triggers a notification whenever any component updates the event list. This method should also send out the updated list of events or perform any desired actions.

addEvent(event: Event) {
    this.events.push(event);
    this.eventChanged.next(this.events.slice());
}

Update: Illustrated with an Example

Consider having event viewer, event editor components, and event service.

Configure the event service to have a subject for event changes, methods to access events, and add events.

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

@Injectable()
export class EventService {

  eventChanged = new Subject<string[]>();

  events: string[] = [
    'Pizza Party',
    'Hackathon',
    'Movie Night'
  ]

  constructor() { }

  addEvent(event: string) {
    this.events.push(event);
    this.eventChanged.next(this.events.slice());
  }

  getEvents() {
    return this.events.slice();
}

Then, the EventViewer component retrieves the event list and subscribes to changes.</p>

<p><strong>event-viewer.component.ts</strong></p>

<pre><code>import { Component, OnInit } from '@angular/core';
import {EventService} from '../event.service';
import {Subscription} from 'rxjs/Subscription';

@Component({
  selector: 'app-event-viewer',
  templateUrl: './event-viewer.component.html',
  styleUrls: ['./event-viewer.component.css']
})
export class EventViewerComponent implements OnInit {

  subscription: Subscription;
  events: string[] = this.eventService.getEvents();

  constructor(private eventService: EventService) { }

  ngOnInit() {
    this.subscription = this.eventService.eventChanged
    .subscribe(
      (events: string[]) => {
        this.events = events;
      }
    )
  }

}

The updated content is then displayed.

event-viewer.component.html

<ul>
  <li *ngFor="let event of events">{{event}}</li>
</ul>

Similarly, create an event editor component.

event-editor.component.ts

import { Component, OnInit } from '@angular/core';
import {EventService} from '../event.service';

@Component({
  selector: 'app-event-edit',
  templateUrl: './event-edit.component.html',
  styleUrls: ['./event-edit.component.css']
})
export class EventEditComponent implements OnInit {

  eventNumber: number = 1;

  constructor(private eventService: EventService) { }

  ngOnInit() {
  }

  addEvent()
  {
    this.eventService.addEvent(this.eventNumber.toString())
    this.eventNumber++;
  }

}

Provide a button for users to interact with.

event-editor.component.html

<button (click)="addEvent()">Add event {{eventNumber}}</button>

In the chosen module, remember to declare these components and specify the provider.

app.module.ts

@NgModule({
  declarations: [
    EventViewerComponent,
    EventEditComponent
  ],
  imports: [
    CommonModule
  ],
  providers: [EventService]
})

Now, both components will reflect any changes made when interacting with one another.

Answer №2

For instance:

class MyService {
    private dataSubject: Subject<boolean> = new Subject();
    public dataStream: Observable<boolean> = this.dataSubject.asObservable();

    endpointUrl = "https://<your_backend>/api/";

    constructor(private httpClient: HttpClient) {}

    // retrieve collection
    getData(queryParams: any): Promise<Data[]> {
         return this.httpClient.get<Data[]>(this.endpointUrl + 'data').toPromise();
    }

    // create single object
    postData(dataItem: Data): Promise<Data> {
        const result = this.httpClient.post(
            this.endpointUrl + 'data',
            JSON.stringify(dataItem),
            this.requestParams);

        return result.toPromise().then((response) => {
            dataItem.id = response['id'];
            this.dataSubject.next(true);
            return dataItem as Data;
        });
}



class MyComponent implements OnDestroy {
    subscriptions: Subscription[] = [];
    success:boolean;
    failure:boolean;
    loading:boolean;

    constructor(private ms: MyService) {
        this.subscriptions.push(this.ms.dataStream.subscribe(() => this.loadData()));
    }


    async loadData() {
        this.myData = await this.ms.getData();
    }

    async submitData() {
        this.loading = true;
        try {
            const response = this.ms.postData(this.dataItem)
            this.success = true;   
        } catch (error) {
            console.log('error', error);
            this.failure = true;
        } finally {
            this.loading = false;
        }
    }

    ngOnDestroy() {
        this.subscriptions.forEach(subscription => subscription.unsubscribe());

}

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

Merge the chosen values from the drop-down menu into one string

Any suggestions would be greatly appreciated! I am currently developing an application using ASP.NET web forms that consists of a dropdown list and two list boxes. I want these elements to be cloned whenever a specific button is clicked. However, my issue ...

Modify JSON from using single quotes to double quotes using JavaScript

Received a JSON from the backend to the front end that uses single quotes throughout, causing issues with a Magento 2 widget. The JSON structure is as follows: { 'mood': 'happy', 'reason': 'why shouldn't I?'} T ...

Froala text area is unexpectedly visible when I attempt to cover it with a partially see-through mask

The website I'm currently developing features a semi-transparent overlay that dims the screen with a light-colored message displayed on top. This overlay and message are shown whenever there is a background process running. Everything was running smoo ...

How can async/await help in retrieving the value of a CORS request?

Trying to make a CORS request and utilize async/await to extract the value from it (using jquery). The functions createCORSRequest and executeCORSRequest seem to be functioning correctly, so the implementation details are not my main concern. The function ...

Retrieving information within a loop through the utilization of a left join操作。

Presently, I am utilizing a while loop to fetch user comments from a MySQL table and applying a conditional class to the buttons within the comment div. Each comment contains two buttons: thumbsup button thumbsdown button I aim to assign the class name ...

What could be the reason for my Angular website displaying a directory instead of the expected content when deployed on I

My current challenge involves publishing an Angular application to a Windows server through IIS. Upon opening the site, instead of displaying the actual content, it shows a directory. However, when I manually click on index.html, the site appears as intend ...

Angular's HttpClient makes sure to wait for the HTTP requests to complete

Initializing arrays with the call this.Reload.All() is causing confusion and breaking the service due to multiple asynchronous calls. I am looking for a synchronous solution where each call waits for its response before proceeding to the next one. How can ...

I am puzzled as to why my text and div boxes are displaying in my navbar/hamburger menu instead of at the bottom of the page

Greetings, everyone! This is my debut post here so bear with me if it's not presented in the correct format. Currently, I am deep into creating a website using HTML, CSS, and just a hint of JavaScript. My primary focus right now is on building the ho ...

Add a class to alternate elements when mapping over data in React

Check out the code snippet below: <div className="grid md:grid-cols-2 sm:grid-cols-2 grid-cols-1 gap-16 mt-24 px-4"> {status === "success" && delve(data, "restaurants") && data.r ...

The statement ""x=x || 4" will result in a `ReferenceError: x is not defined` because the

What is the reason behind receiving a ReferenceError: x is not defined error when using x = x || 4 or even x=(x||5), while var x = x || 4 operates as intended? ...

Modifying the name of a key in ng-multiselect-dropdown

this is the example data I am working with id: 5 isAchievementEnabled: false isTargetFormEnabled: true name: "NFSM - Pulse" odiyaName: "Pulse or" when using ng-multiselect-dropdown, it currently displays the "name" key. However, I want ...

Obtain the data from a nested array

I'm facing a situation where I have the following code: var obj = { level1 : { level2 : 'value' } }; I also have another object: var returnData = { value: "level1.level2", anotherThing: "level1" }; The goal is to ...

Resolve the issue of text overlapping on an image when zooming in

There seems to be an issue with overlapping text and images when zooming the page. I have included a screenshot for reference, please take a look and provide a solution. Thank you in advance.https://i.sstatic.net/oVlGN.png Here is the CSS code causing the ...

Missing Directory Issue Upon Deploying Node.js App on Google App Engine

I have a Node.js web application built using TypeScript and Koa.js that I am looking to deploy on Google App Engine. The code has already been transpiled into JavaScript and is stored locally in the ./dist/src/ directory. Essentially, I only need to depl ...

Mocha: A Unique Perspective on Testing the express.Router Instance

As I was developing a JavaScript controller file, I came across the need to test if my controller instance contains an instance of the express method called Router(). import {assert} from 'chai'; import {UF_Controller} from '../../controlle ...

Issue with running Angular Application through docker-compose.yml file is stopping the execution

Below is the docker file I have created for my angular application: Dockerfile: # base image FROM node:10.16.0-alpine AS build-step # set working directory WORKDIR /app COPY package.json ./ RUN npm install COPY . . RUN npm run build FROM nginx:1.16.1-alp ...

Problem with Typescript: The type '{ x;y }' is required to have a '[Symbol.iterator]()' method

Just starting out with Typescript and tackling the task of converting a React project from JavaScript to TypeScript. I've been diving into various posts for guidance, but I feel like I'm going in circles. Any assistance would be greatly appreci ...

Guide on transmitting data from a child component to a parent object in Vue.js?

When I attempt to utilize a child component with custom form inputs and emit those values to the parent component, I encounter an issue where one input value disappears when another input value is entered. Let me showcase some code: Child Component <tem ...

Why is "undefined" being used to alert an ajax call response?

I am encountering an issue with a returned value from an AJAX request. The web method being referenced returns a boolean value of true or false. However, when attempting to access this value outside the AJAX method, I am receiving an "undefined" message :? ...

Struggling to remove an image while using the onmouseover event with a button?

I am encountering an issue with hiding an image using the onmouseover event not applied directly to it, but rather to a button element. The desired functionality is for the image to appear when the mouse hovers over and disappear when it moves away. Here&a ...