ManBearPig
ManBearPig

Reputation: 93

Rxjs find operator not working on values in Observable array

I have a service in which Im casting my http payload to an Observable array, I would like to be able to call a method within that service to act on that observable. In my case I would like to confirm an ID exists in the array of Observable ID's. Ive done some research on stackoverflow and I think its because im mishandling the return of the find operator( as I understand it returns an observable ) .

Service :

@Injectable({
  providedIn: 'root'
})
export class RuleFieldsService {
  baseUrl: string;
  relativeUrl: string;
  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  };
  private rulefieldIdSub = new BehaviorSubject<any[]>([]);
  private rulefieldIdStore: { ruleFieldIds: any } = { ruleFieldIds: [] };
  rulefieldIds$ = this.rulefieldIdSub.asObservable();


  constructor(private readonly http: HttpClient, @Inject(APP_BASE_HREF) 
              private readonly baseHref: string) 
              {
                this.baseUrl = this.baseHref + environment.baseAPIUrl;
               };

  getRuleFields() {
    this.relativeUrl = 'rulefields';
    return this.http.get(this.baseUrl + this.relativeUrl);
  }


  getList(){
    
    this.relativeUrl = 'rulefields';

    this.http.get(this.baseUrl + this.relativeUrl + '/enabledropdown').subscribe(data => {
      this.rulefieldIdStore.ruleFieldIds = data;
      this.rulefieldIdSub.next(Object.assign({}, this.rulefieldIdStore).ruleFieldIds);
    });
  }

  checkRuleFieldType(ruleFieldId) {
    console.log('check method')
    this.rulefieldIds$.pipe(find((id: any) => id === ruleFieldId)).subscribe(items => {
        console.log("ID is in the list")
    });
  }      
}

Component :

@Component({
  selector: 'app-rules-tab-wrapper',
  templateUrl: './rules-tab-wrapper.component.html',
  styleUrls: ['./rules-tab-wrapper.component.scss']
})
export class RulesTabWrapperComponent implements OnInit {
public selectedIndex;
public ruleFieldIds$: Observable<any[]>;
  constructor(private rulesetService: RulesetService,
              private rulefieldService: RuleFieldsService) { }

  ngOnInit() {

    this.rulefieldService.getList();
     this.ruleFieldIds$ = this.rulefieldService.rulefieldIds$;
     this.rulefieldService.checkRuleFieldType(15);
     console.log(this.ruleFieldIds$ )
  }
}

Ive looked into the Observable to try and get a better idea of where the error may be, but I cant make actionable sense of the information :

source: BehaviorSubject
 closed: false
 hasError: false
 isStopped: false
 observers: Array(1)
  0: FindValueSubscriber
   closed: false
   destination: Subscriber {closed: false, _parentOrParents: null, _subscriptions: Array(1), syncErrorValue: null, syncErrorThrown: false, …}
   index: 2
   isStopped: false
   predicate: (id) => id === ruleFieldId
   source: Observable
   source: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
   _isScalar: false
   __proto__: Object
syncErrorThrowable: true
syncErrorThrown: false
syncErrorValue: null
thisArg: undefined
yieldIndex: false
  _parentOrParents: Subscriber
  closed: false
  destination: SafeSubscriber {closed: false, _parentOrParents: null, _subscriptions: null, syncErrorValue: null, syncErrorThrown: false, …}
  isStopped: false
  syncErrorThrowable: true
  syncErrorThrown: false
  syncErrorValue: null
  _parentOrParents: null
  _subscriptions: [FindValueSubscriber]
  __proto__: Subscription
_subscriptions: [SubjectSubscription]
__proto__: Subscriber
length: 1
__proto__: Array(0)
thrownError: null
_isScalar: false
_value:
 enableDropdownFor: (6) [15, 42, 55, 65, 67, 69]

Thank you for your insight in advance !

Upvotes: 0

Views: 1577

Answers (1)

cjd82187
cjd82187

Reputation: 3593

You're understanding on how the "find" operator is a bit off. It is a filter and is meant to filter a stream of data that emits multiple values, but not emit anything till the value matches the predicate and then stop emitting.

https://rxjs-dev.firebaseapp.com/api/operators/find

Your example observable emits a single value, that happens to be an array. What you want to do in your function is use the rxjs map operator, and inside that you will have the array of values, and use the standard array's find (or findIndex), and return a boolean.

I've put together a little stack blitz demoing it, with some comments. https://stackblitz.com/edit/angular-dra5wu?file=src%2Fapp%2Fapp.component.html

here are some of the concepts and operators I was mentioning:

export class ExampleService {
  // start with "null" so we can filter that out, this way we know our get list has ran at least once if that's important
  private rulefieldIdSub = new BehaviorSubject<any[]>(null);
  
  // we skip the intial "null" value, this makes sure "getList" has populated the rules
  rulefieldIds$ = this.rulefieldIdSub.asObservable().pipe(filter(ids => ids !== null));

  getList(){
    // making a fake list for example purposes, running in a set timeout so it takes a second
    setTimeout(() => {
      this.rulefieldIdSub.next([1,3,5,7,9])
    }, 1000);
  }

  checkRuleFieldType(ruleFieldId): Observable<boolean> {
    return this.rulefieldIds$.pipe(
      // we're using the "map" operator to transform the result from the observable into something else
      // https://rxjs-dev.firebaseapp.com/api/operators/map
      map(ruleFieldIds => {
        // map gets the full array of your ruleField ids, now we return if we found that id or not
        return ruleFieldIds.findIndex(id => id === ruleFieldId) !== -1;
      })
    )
      
  }    
 
}

And an updated component that uses it

export class AppComponent  {
  name = 'Angular';
  rulefieldIds$: Observable<any[]>;

  constructor(public exampleService: ExampleService) {
    this.exampleService.getList();
    this.rulefieldIds$ = this.exampleService.rulefieldIds$;

    this.exampleService.checkRuleFieldType(15).subscribe(result => {
      console.log("Do we have 15?", result)
    });

    this.exampleService.checkRuleFieldType(5).subscribe(result => {
      console.log("Do we have 5?", result)
    });
  }
}

Upvotes: 1

Related Questions