0
votes

I use Subject in my app in order to make CRUD operations. I subscribe to it in a couple of components, but it doesn't seem to change my values in real time. I have to resort to reloading the page to get the most recent values. This is probably due to my lack of understanding of how everything should be organised, but I haven't found any simmilar issues here. Could you please help me fix what I do wrong? (using BehaviourSubject yields the same results). Here is the code for my service:

    @Injectable({ providedIn: 'root' })
export class AppBookshelfService {
  filesAndFolders: ItemFile[] = [];
  filteredArrOfFolders = [];
  filesSubj = new Subject();
  curentParent = 0;
 
  getFiles() {
     this.http.get<any>('http://localhost:3000/api/items').subscribe(
       (data: any) => {
         this.filesAndFolders = data.items;
        this.filesAndFolders = data.items.filter(item => item.isDeleted === 0);  
        this.filesSubj.next(data.items.filter(item => item.isDeleted === 0)); 
         data.items.forEach(item => {
           if(item.parentId !== 0 && item.isFolder === 1) {
            this.filteredArrOfFolders.push(item);
           }
         });     }
     )
  }

  getFile(id: number): Observable<ItemFile> {
    return this.http.get<ItemFile>("http://localhost:3000/api/items/" + id);
  }

  emitIdForFile(parentId: number) {
    this.curentParent = parentId;
  }

  getCurentParent() {
    return this.curentParent;
  }

  postFile(item: ItemFile) {
    return this.http.post<ItemFile>("http://localhost:3000/api/items/", item).subscribe(result => {})
  }

Here is the code of one of my key components, that does the subscribing:

 ngOnInit(): void {
    this.bookshelfService.getFiles();
    this.sub = this.bookshelfService.filesSubj.subscribe(data => {
      this.files = data;
      console.log(this.files)
      let groupOfCheckboxes = {};
      this.files.forEach(element => {
        groupOfCheckboxes[element.id.toString()] = new FormControl("");
      });
      this.checkboxForm = new FormGroup(groupOfCheckboxes);
    });

    // this.getFolders();

  }

  ngOnDestroy(): void {
    this.sub.unsubscribe();
  }

  startCreatingFolder() {
    this.isFolderBeingCreated = !this.isFolderBeingCreated;
  }

  createFile(id: number) {
    this.bookshelfService.emitIdForFile(id);
    this.router.navigate(['create']);
  }

  createFolder(event, id) {
    const folder = {
      name: this.folderNameInput.nativeElement.value,
      description: "",
      imageLink: "",
      isDeleted: 0,
      parentId: id,
      isFolder: 1
    }
    this.bookshelfService.filesAndFolders.push(folder);
    this.bookshelfService.getFiles();
    this.bookshelfService.postFile(folder);
    this.isFolderBeingCreated = false;
  }

Here is a link to the full repository: https://github.com/Not-a-whale/BookshelfApp

Here is the app working on Heroku with the said flaw: https://bookshelf-app-nikita.herokuapp.com/

1

1 Answers

1
votes

The subject is Hot(Multicast), You Should subscribe to it before emit occurs:

 ngOnInit(): void {
    
    this.sub = this.bookshelfService.filesSubj.subscribe(data => {
      this.files = data;
      console.log(this.files)
      let groupOfCheckboxes = {};
      this.files.forEach(element => {
        groupOfCheckboxes[element.id.toString()] = new FormControl("");
      });
      this.checkboxForm = new FormGroup(groupOfCheckboxes);
    });

     this.bookshelfService.getFiles();
     // this line must be after subscribe

    // this.getFolders();

  }

An Observable is cold when data is produced inside the Observable and the Observable is hot when the data is produced outside the Observable. As we just saw the hot Observable is able to share data between multiple subscribers. We call this behaviour “multicasting”. Generating a random number is not a good real life usecase. A good usecase would be DOM events. Let’s say we’re tracking clicking behaviour and have multiple subscribers do something with the coordinates:

The data is produced outside of the Observable itself. Which makes it hot, because the data is being created regardless of if there is a subscriber or not. If there is no subscriber when the data is being produced, the data is simply lost.

More Info

If you want to save data_list in memory and then use it multiple times use a behaviorsubject to hold it, subject has no memory to hold the events.

behaviorsubject needs a default value so you must initialize it with an empty list.

When you call service just next the behaviorsubject with new data, then subscribe to it before service call to receive the changes events

// RxJS v6+
import { BehaviorSubject } from 'rxjs';
​
const subject = new BehaviorSubject(123);
​
// two new subscribers will get initial value => output: 123, 123
subject.subscribe(console.log);
subject.subscribe(console.log);
​
// two subscribers will get new value => output: 456, 456
subject.next(456);
​
// new subscriber will get latest value (456) => output: 456
subject.subscribe(console.log);
​
// all three subscribers will get new value => output: 789, 789, 789
subject.next(789);
​
// output: 123, 123, 456, 456, 456, 789, 789, 789