Search code examples
angularangular-servicesangular-componentseventemitterhtml-input

How to update Array value in Service using Input from child component?


I am trying to update the value of an Array in my service from child component.

Here is the service at the top level of application

feature.service.ts

export class FeatureService {
    
  private myFeatures: MyFeature[] = [
    new Feature(1, '1st Empty String'),
    new Feature(2, '2nd Empty String'),
    new Feature(3, '3rd Empty String'),
  ];
        
  getFeatures() {
    return this.myFeatures.slice();
  }
}

printFeatureArray() {
  console.log(this.myFeatures); 
}

As you can see it, it initialises MyFeatures Array to hold the most up date version of my data,

I would like to change this data through a child component, and be able to call the printFeatureArray function to print the updated values.

Here are the child components constructing my feature items from the getDocFeatures function generating a reference for display, this component is used to create individual items per instance.

component.ts

export class Component {
  myFeatures: MyFeature[] | undefined;
        
  constructor(private docBuilderService: DocBuilderService) {}
    
  ngOnInit() {
    this.myFeatures = this.FeatureService.getDocFeatures();
  }
}

component.html

<div
  class="list"
  *ngFor="let myFeature of myFeatures; let i = index"
>
          
<app-my-item [docFeature]="docFeature"></app-my-item>
</div>

Finally from within the my-item component I have an input HTML element that I would like to record user input and populate the myFeatures Array on the very top level.

<input
  #inputTag
  (keyup)="getValue(inputTag.value)"
  [ngClass]="addClassTemplate()"
  (click)="onSelected()"
  type="text"
  placeholder="{{ docFeature?.inputValue }}"
/>

my-item.component.ts


export class MyItemComponent {
  value: string | undefined;
        
  constructor() {}
          
  getValue(value: string) {
    console.log(value);
  }
}

How do i update the original Array inside the service from the bottom level of this component tree?

Thanks!


Solution

  • Service

    For a cross communication a Subject in a Service is the best practice.

    myService

    private mySubject: Subject<string> = new Subject();
    
    getMySubject() {
      return this.mySubject.asObservable();
    }
    
    doSomething(text: string) {
      this.mySubject.next(text);
    }
    

    Component

    Code Behind

    ...
    private mySub!: Subscription;
    myTextData: string = "";
    
    constuctor(private myService: MyService) {}
    
    ngOnInit(): void {
      this.mySub = this.myService.getMySubject().subscribe(data => { this.myTextData = data; }
    }
    
    ngOnDestroy(): void {
      this.mySub.unsubscribe();
    }
    

    Components HTML

    <div>
      {{ myTextData }}
    </div>
    

    You can use an array of objects, too. And then use it with *ngFor. All the same. Every time the service calls the Subjects next method, all components they subscribe to it will be updated.

    Important We have the mySub object which we unsubscribe on ngOnDestroy this is very important! Otherwise the subscription is multiplied and fires multiple times. Thats not what we want :-). Second Important In the Service we'll never return the Subject directly. Instead we return its Observable. Looks a little strange but that is the correctly way to do this and not a workaround.

    All about this here.