1

I have angular App with State management which is work in where i load data from database in observable like

let first give you interface

interface service {
  id: number,
  name:string,
  category_id: number,
}

interface category{
  id:number,
  name:string,
}

As you can see there is relation between service and category which link by id which is number.

Let give you what is observable look like

services : Observable<Service[]>

categories : Observable<Category[]>

How can i merge Category name into Services into something differnt like this:

interface ServiceWithCategory{
  id: number,
  name:string,
  category_id: number,
  category:string,
}

I try to to some async pipe in angular but it don't update if category updated

const serviceswithcategory = services.pipe(
      map(i => i.map((service) => {
        return {
          ...service,
          Category: `${this.async.transform(categories.pipe(map((i) => { 
           return i.find((a: Category) => a.id == service.category_id) }))?.name}
           }`
        }
      })));

Now serviceswithcategory don't do anything when category get udpated. It is not reactive to category.

2
  • While the answer is very nice, but if you code also the backend, consider retrieving the data in the structure you want. Commented Jun 4, 2023 at 7:12
  • it totally front end using ngxs Commented Jun 17, 2023 at 12:37

1 Answer 1

2

Here's the code you're after:

forkJoin({ services: services$, categories: categories$ })
  .pipe(
    map(({ services, categories }) => {
      const categoriesById = categories.reduce((acc, category) => {
        acc[category.id] = category;
        return acc;
      }, {});

      return services.map((service) => ({
        id: service.id,
        name: service.name,
        category: categoriesById[service.category_id],
      }));
    })
  )
  • forkJoin let us get the result of the 2 observables
  • Using a reduce, we transform the categories into a dictionary of categories to be able to access them by ID instead of having a loop in a loop (to find a category for each service) which wouldn't be efficient
  • We then map onto the services and get the corresponding category

Result:

[
    {
        "id": 1,
        "name": "Service 1",
        "category": {
            "id": 100,
            "name": "Category 100"
        }
    },
    {
        "id": 2,
        "name": "Service 2",
        "category": {
            "id": 200,
            "name": "Category 200"
        }
    },
    {
        "id": 3,
        "name": "Service 3",
        "category": {
            "id": 100,
            "name": "Category 100"
        }
    }
]

Here's a live example (look at the console for the output).

Sign up to request clarification or add additional context in comments.

10 Comments

sorry it don't work in angular Element implicitly has an 'any' type because expression of type 'number' can't be used to index type '{}'. No index signature with a parameter of type 'number' was found on type '{}'.ts(7053)
it return null on angular sharing angular live example soon
strange it work there
You probably just need to cast the accumulator of the reduce defined as {} and instead put {} as Record<number, TheOtherTypeThatHasBothNameAndCategory>
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.