What's the best way to signal to an Observable that my array has reached its end?

Quick Summary: Can someone help me figure out how to get an Observed array to complete? Check out this jsBin example for reference.

I'm a beginner with Observables so I might be approaching this problem the wrong way. With the code provided below, or in this jsBin link, what changes should I make to ensure the User's sites array completes?

let firstUser = {
  name: 'Susan',
  sites: [
    {company: 'ABC Co', role: 'admin'},
    {company: 'XYZ Co', role: 'reader'}
  ]
};

user = new Rx.BehaviorSubject(firstUser);

function authorized(authRoles) {
    // check if the current user has one of the authorizedRoles roles
    return this.user
      .do(user => console.log("Checking user: ",JSON.stringify(user)))
      .flatMap( user => user.sites )
      .do(res => console.log("Mapped user roles: ",res))
      .first( site => authRoles.indexOf(site.role) !== -1 ) // only return when a role matches
      .do( res => console.log('First: ',res))
      .map( res => true)
}

// This one finds a match and completes
authorized(['writer','admin','reader']).subscribe(res =>{
  console.log("1: isAuthorized?: ",res);
}, err => {
  console.log("1: ERROR: User is not authorized!");
}, () => {
  console.log("1: Authorized check completed!");
});

// This one never completes
authorized(['writer','foo']).subscribe(res =>{
  console.log("2: isAuthorized?: ",res);
}, err => {
  console.log("2: ERROR: User is not authorized!");
}, () => {
  console.log("2: Authorized check completed!");
});

Please note that if (first)[http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-first] doesn't find a match it will throw an error, but only if the Observable completes.

The code above works correctly and completes if there's a match, however, it will neither complete nor throw an error without a match because the array of user.sites never completes.

Is there a way to force the array to complete? I managed to make it work by fetching/subscribing to the user first:

//
// Pretend that fetchedUser is fetched in a safer/more sane way

// fetch the user and set it
let fetchedUser;
user.subscribe(user => fetchedUser = user);

function authorized2(authRoles) {
    // check if the current user has one of the authorizedRoles roles
    return Rx.Observable.of(this.fetchedUser.sites)
      .do(sites => console.log("auth2: Checking users sites: ",sites))
      .flatMap( sites => sites )
      .do(res => console.log("Mapped user roles: ",res))
      .first( site => authRoles.indexOf(site.role) !== -1 ) // only return when a role matches
      .do( res => console.log('First: ',res))
      .map( res => true)
}

I feel like there's a simple step I'm missing to make this work with pure Observables. Thank you in advance for your assistance!

Answer №1

Rx.Observable.of(this.fetchedUser.sites)
transforms its arguments into an observable sequence and then ends. This causes the .first operator to throw an error. The issue here is that you are not completing the this.user subject. To address this, you could modify it to this.user.take(1) or use Rx.Observable.of(fetchedUser) without turning it into a behavior subject if completion is desired. Alternatively, you can switch from using .first to .map to return false when the user lacks authorization. Below is an illustration of how the take operator can be used.

const user = Rx.Observable.create((o) => {
  window.setTimeout(() => {
    o.next({
      name: 'Susan',
      sites: [
        {company: 'ABC Co', role: 'admin'},
        {company: 'XYZ Co', role: 'reader'}
      ]
    });
  }, 1000);
});

function authorized(authRoles) {
    return user
      .take(1)
      .flatMap( user => user.sites )
      .first( site => authRoles.indexOf(site.role) !== -1 )
      .map( res => true);
}

authorized(['writer','admin','reader']).subscribe(res =>{
  console.log("1: isAuthorized?: ",res);
}, err => {
  console.log("1: ERROR: User is not authorized!");
}, () => {
  console.log("1: Authorized check completed!");
});

authorized(['writer','foo']).subscribe(res =>{
  console.log("2: isAuthorized?: ",res);
}, err => {
  console.log("2: ERROR: User is not authorized!");
}, () => {
  console.log("2: Authorized check completed!");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.2/Rx.min.js"></script>

Upon reviewing your example, I couldn't help but wonder why you're employing RxJs for this particular problem. RxJs is designed for working with streams of data, yet your scenario involves a static value. Simplifying your approach without RxJs in a synchronous manner could achieve the same outcome much more straightforwardly. Here's an alternative:

function authorized(authRoles) {
    return firstUser.sites.some(x => authRoles.includes(x));
}

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

Recreate body content with JQuery Mobile

I've been attempting to duplicate the entire HTML content within the tags. However, even though the appearance on screen is correct, none of the links are functioning.</p> <p>To view the fiddle, click here: [Here is a fiddle][1]</p> ...

Are there any built-in methods in JQuery specifically designed to identify validatable elements?

Is there a way to identify and validate all the different types of form elements in a form? My attempt is not working as expected. Here is my code: var FormValidator = function (form) { this.form = form, this.elements = this.form.find(' ...

The function replace does not exist in t(…)trim

I encountered an error in my code that breaks the functionality when checked using console.log. var map = L.map('map').setView([0, 0], 2); <?php $classesForCountries = []; if (have_posts()) : while (have_posts()) : the_post(); ...

Using RxJS to send a series of distinct AJAX requests on an event

Suppose I have an event type, such as a click event. I need to initiate 3 separate ajax requests through this event, but I want to listen for the final result of all 3 requests. What would be the most suitable design pattern for implementing this sequence ...

The functionality of elements such as buttons and textboxes has been compromised since they have been placed within a CSS tabbed table

I successfully implemented small tables containing input elements and buttons, but I decided to enhance the user interface by placing them inside CSS tabs. The new layout looked better and saved space on the page. After configuring the CSS tabs using divs ...

Launching a Node.js Express application on Heroku

I'm facing an issue while trying to deploy my app on Heroku, as I keep encountering the following error: 2022-08-11T12:49:12.131468+00:00 app[web.1]: Error: connect ECONNREFUSED 127.0.0.1:3306 2022-08-11T12:49:12.131469+00:00 app[web.1]: at TCPConnect ...

Tips for avoiding the babel/cli accessibility issue during Google Cloud deployment

I've encountered an issue while attempting to deploy my modularized Node.js Google cloud function to Google Cloud Platform. The function works smoothly on my local machine, but I'm getting an error during deployment that reads: ERROR: (gcloud.fu ...

JavaScript can dynamically attach EventListeners to elements, allowing for versatile and customized event

I am currently populating a table using data from an XML file. One of the columns in the table contains links to more details. Due to the unique requirements of my web page setup (Chrome extension), I need to dynamically add an event handler when the table ...

Adjusting Image Size According to Window Resize?

My current issue involves having four images displayed side by side. When the window size is adjusted or viewed on a smaller device, the layout shifts to a line jump (with three images in a row and the fourth one below). What I actually want is for all fou ...

Retrieving data from the database using getStaticProps in Next.js

As I was following a tutorial on Next.js, the instructor did something that deviated from what I had learned in school and left me pondering. Here is what he did: interface FaqProps { faq: FaqModel[]; } export default function Faq({ faq }: FaqProps) { ...

Leveraging the power of express, employing the await keyword, utilizing catch blocks, and utilizing the next

I am currently developing an express JS application that follows this routing style: router.post('/account/create', async function(req, res, next) { var account = await db.query(`query to check if account exists`).catch(next); if (accoun ...

Vue Dynamic Table Title

Is it possible to add labels to a pivot-table in Vue without affecting array indexes and drag-and-drop functionality as shown in the screenshot below? https://i.stack.imgur.com/5JTSM.png Are there alternative methods for implementing this feature? You c ...

Issue with retrieving query results using node_redis client

I've been struggling with a particular issue that I just can't seem to solve. The problem revolves around not being able to retrieve output values from a Redis query. My setup involves using the node_redis client as the Redis driver for my Node.J ...

The behavior of JavaScript may vary when running in the IE deployment environment compared to running in debugging tools like Visual

UPDATE: After debugging in IE, it seems that the "setSelectionRange" function is not supported. It's strange that it works in Visual Studio but not outside of it. The JavaScript fails at that line when running in IE, which raises the question: how can ...

Shorten a data point in JSON code

I'm currently in the process of developing a stock widget that utilizes JSON to retrieve data from the Yahoo API/YQL console. I am specifically working with values extracted from the key ChangePercentRealtime, however, the values are longer than what ...

Retrieving nested array elements in MongoDB using parent ID element (JavaScript)

Suppose I have a MongoDB collection structured as follows: _id: ObjectId(" <a objectid> ") id: " <a userid> " __v: 0 subscribedTo: Object Regardless of whether or not Mongoose is being used, how can I execute a query to acc ...

Utilizing the power of Node.js with Oracle seamlessly, without the need for the Oracle Instant

Currently, I am working on testing the connectivity to our Oracle databases. Recently, I came across node-oracledb, a tool released by Oracle that aims to simplify this process. However, one major hurdle is the requirement of having the Oracle Instant Clie ...

A guide on embedding the flag status within the image tag

I would like to determine the status of the image within the img tag using a flag called "imagestatus" in the provided code: echo '<a href="#" class="swap-menu"><img id="menu_image" src="images/collapsed.gif" hspace = "2"/>'.$B-> ...

Creating crisp and clear text within a three.js texture

I am currently incorporating a 512x512 SVG onto a circular plane in my project using the following code snippet: const texture = new THREE.TextureLoader().load('img/plane.svg'); ​ const material = new THREE.MeshBasicMaterial({ ...

How can we deliver pure JS, HTML, and CSS content without relying on static HTML pages?

Looking to create a fast app prototype without using React or Vue? I'd like to avoid simply making an html and js file imported within it. Can npm packages, SCSS be used while programming vanilla Javascript minus a framework? ...