Ensure that Angular FormGroup POST is correctly de-marshaled in conjunction with associated objects

Summary

In a complex Angular 2.x + Spring Data REST + Spring Boot 1.4 project, the challenge lies in defining JSON object references that can be successfully de-marshaled by Spring into the domain model.

Key Points

Consider a scenario where a recipe book contains both labels and recipes, all created simultaneously. Each recipe has connections to one or more labels, leading to a representation like:

book: {
  title: 'Cook book',
  labels:[{            
    name: 'fruit',
    description: 'Recipe with fruit.'
  },{
    name: 'vegetable',
    description: 'Recipe with vegetables.'
  },{
    name: 'fish',
    description: 'Recipe with fish.'
  }],
  recipes:[{
    title: 'Sweet corn and onion salad',
    description: 'Simple, quick, and refreshing corn salad',
    labels: [{
      labelRef: 1      
    }]
  }]
}

The challenge here is using array indices as client-side label identifiers when no persistent IDs exist for either label or recipe. This requires maintaining a guaranteed order in the array of labels.

To tackle this dynamically in an Angular application, a form is implemented allowing users to add labels and recipes at will:

let book = this._fb.group({
      title: this._fb.control(''),
      labels: this._fb.array([this.buildLabel()]),
      recipes: this._fb.array([this.buildRecipe()])
});

buildLabel(): FormGroup {
      return this._fb.group({
        name: this._fb.control(''),
        description: this._fb.control('')
      });
}

buildRecipe(): FormGroup {
      return this._fb.group({
        title: this._fb.control(''),
        description: this._fb.control(''),
        labels: this._fb.array([])
      });
}

buildLabelReference(index: number): FormGroup {
      return this._fb.group({
        labelIndex: this._fb.control(index)
      });
}

Moving on to the mid-tier representation in Spring Data JPA + Lombok, we have Label:

@Data
@Entity
@Table(name = "LABEL")
public class Label {    
    @Id
    @Column(nullable = false)
    private Long id;

    private String name = "";
    private String description = "";

    @ManyToOne(optional=false)
    @JoinColumn(name="book_id")
    private Book book;
}

And then comes Recipe:

@Data
@Entity
@Table(name = "RECIPE")
public class Recipe {
    @Id
    @Column(nullable = false)
    private Long id;

    private String title = "";
    private String description = "";

    @OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
    @Column(nullable = false)
    private List<Label> labels = new ArrayList<>();

    @ManyToOne(optional=false)
    @JoinColumn(name="book_id")
    private Book book;
}

Finally, there's Book:

@Data
@Entity
@Table(name = "BOOK")
public class Book {
    @Id
    @Column(nullable = false)
    private Long id;

    private String title = "";

    @OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
    @Column(nullable = false)
    private Set<Label> labels = new HashSet<>();

    @OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
    @Column(nullable = false)
    private List<Recipe> recipes = new ArrayList<>();

}

While the Book de-marshalls correctly in the @RepositoryRestController,

@Slf4j
@RepositoryRestController
public class BookRespositoryRestController {

    private final BookRepository bookRepository;
    private final LabelRepository labelRepository;

    @RequestMapping(method = RequestMethod.POST,value = "/books")
    public @ResponseBody PersistentEntityResource post(@RequestBody Book book,
            PersistentEntityResourceAssembler assembler) {

        Book entity = processPost(book);

        return assembler.toResource(entity);
    }

the stumbling block appears when dealing with recipe labels as the labelRef poses challenges unknown to the domain model.

Hence, the primary query remains how to construct a solid labelRef structure in the Angular TypeScript setup so that the Book can accurately de-marshal the label links embedded within each recipe object.

Answer â„–1

In short;

Unfortunately, there is currently no built-in support for this feature. However, you can implement custom code to enable it.

Insights

There are two main challenges that need to be addressed: defining JSON object references and managing encoding/decoding of these references.

Resolving JSON Object References

While there have been various proposals for standardizing JSON references, a universally accepted standard has yet to emerge as of 2021.

Handling Multiple Requests

To work around the lack of direct support, consider splitting requests by submitting referenced objects separately and using unique server-side paths as references:

/categories/1: {
  name: 'fruits',
  description: 'Recipes involving fruits.'
}

/categories/2: {
  name: 'vegetables',
  description: 'Recipes with vegetables.'
}
...

Subsequent requests can then utilize these server-side paths for referencing objects.

Spring Data REST facilitates this approach without requiring additional custom coding, though more intricate models may necessitate adjustments for bidirectional associations.

Managing Single Requests

If consolidating into a single request is necessary, adopt a JSON reference model and encode references accordingly, implementing custom deserialization logic on the server side when processing the data.

Spring Data REST offers options for custom (de)serialization with Jackson's ObjectMapper, or alternatively, employ a data transfer object for deserialization purposes.

An example incorporating JSONPath and de-serialization in a data transfer object:

book: {
  title: 'Recipe Collection',
  categories:[{
    name: 'fruits',
    description: 'Recipes involving fruits.'
  },{
    name: 'vegetables',
    description: 'Recipes with vegetables.'
  },
  ...
],
recipes:[{
  title: 'Summer Berry Smoothie',
  description: 'Refreshing and nutritious berry smoothie',
  categories: [{
    category: '$..categories[0]'      <--- referenced using JSONPath
  }]
}]
}

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

Is it possible to create a click event for a mat-icon within a mat form field (either in the matprefix or matsuffix)?

I am working with a mat-form-field and have incorporated a custom mat-icon within it as a matprefix. However, I am now looking to create a click method for that matprefix icon. Despite my attempts to write a click method, it does not seem to be functioning ...

It's strange that I can easily access jQuery from the developer console, but for some reason I can't access it from

My script bundle contains jquery along with other elements, but when I try to use it from a component, I encounter an error message saying ERROR ReferenceError: $ is not defined. Strangely, when I type $('body') in the console, it works perfectly ...

"Utilizing *ngFor to Loop Through Nested JSON Data in an Angular Ionic HTML Component

I am currently utilizing the Ionic segment feature along with nested JSON data structures. I have recently switched to using Ionic within the Angular framework, specifically version 5. However, I am encountering difficulty when attempting to display the da ...

Is it possible for Angular's `HttpClient` to use complex property types in the `.get()` generics aside from just `string` or `number`?

After spending an entire day researching the topic, I've hit a dead end. All my efforts have only led me to discover one thing—omission. None of the information I've come across mentions whether you can utilize non-simple types (such as string ...

Is it feasible to pre-render an Angular2 view invisibly when hovering?

Can a view be preloaded and prerendered invisibly upon hover? I have an application that displays a list of items. Each item is its own component, and when you click on any of these items, it is replaced by a detailed view (another component) of the same ...

Having issues with my *ngFor loop in Angular 11. Any assistance in resolving this problem would be greatly appreciated

Utilizing *ngFor, I am sending an array from typescript to the HTML page. kitUser: any=[]; constructor(private service: AdminService) { } ngOnInit() { this.service.getKSignUps().subscribe(res=>{ this.kitUser=res; console.log(this. ...

Exploring the World of JSON Mapping Libraries

In my project, I am dealing with objects (like a ball) that have specific data members and functions. These objects are being retrieved from various servers, each with its own hierarchy. For example: class Ball { int size; string color; } An instance ...

Surprising Outcome when Dealing with Instance Type - Allowing extra properties that are not explicitly stated in the type

I've come across an issue with the InstanceType Utility Type. I am trying to create a function that takes a class constructor and an instance of that class. For instance: export type Class<T=any> = { new(...args: any[]): T; }; function acceptC ...

Converting a TypeScript class to a plain JavaScript object using class-transformer

I have a few instances of TypeScript classes in my Angular app that I need to save to Firebase. However, Firebase does not support custom classes, so I stumbled upon this library: https://github.com/typestack/class-transformer which seems to be a good fit ...

Mastering the usage of Higher Order Components (HOC) with both types of props is

I am facing a challenge in implementing HOCs for this specific scenario. I aim to enclose existing components since they share similar functionalities. Below is an abridged version of my current structure: function CreateComponentHere(props: BaseProps): J ...

The angular [hidden] directive fails to function properly within a loop in a production environment

I am facing an unusual problem. In my code, I have a loop that goes through a collection and sets the [hidden] attribute based on a property value in each item of the collection. Initially, all these values are set to false. Here is how it looks: <ng-co ...

Looking to grab a single value from a JSON file and utilize it in component code within Angular 2 (8.2.8)?

My JSON file contains information about various pages: { "pagesList": [ { "pageUrl": "index", "imgUrl": "homepage", "imgNumber": 17 }, { "pageUrl": "second", "imgUrl": "secondimage", ...

Angular 2 Error: Unable to access the `subscribe` property of an undefined value

In my Angular 2 project, I have a single form that serves the purpose of adding a new event or editing an existing one. The logic checks the URL parameter to determine whether an ID is present. If an ID is found, it subscribes to the editEvent method from ...

The category 'Moment' cannot be assigned to the category 'Date'. The characteristic 'toDateString' is not present in the category 'Moment'

I recently integrated moment into my Angular2 application, and encountered an issue when attempting to assign the date of this week's Saturday to a variable of type date, case "weekend": this.fromDate = moment().startOf('week ...

How to Trigger an Angular 9 Function Using jQuery

Is there a way to trigger a function within an Angular 9 component using jQuery? I have an Angular 9 application that allows the integration of small jQuery plugins. I now need to make a basic function in the Angular app accessible to these jQuery plugins ...

Utilize functional JS code within a TypeScript environment

Attempting to integrate this code into a TypeScript project, it is a modified version of the react-custom-scrollbars. I am struggling with TypeScript in regards to declaring types for style and props. In this particular case, I prefer to bypass type check ...

Dependency injection selectively loads only the required parameters

I am currently in the process of developing a custom XHRBackend for my angular2 application. My goal is to intercept all http responses with a 401 status code (unauthorized) and redirect the user to the login page. However, I am facing an issue where the D ...

Issue with Rxjs switchMap function not correctly executing the provided function

Currently utilizing the Subject observable provided by rxjs. In my component, I have implemented two methods. Search search(terms: Observable<string>) { return terms.pipe( debounceTime(400), distinctUntilChanged(), switchMap(term => ...

Is it possible to utilize ReturnType and Parameters in a generic function declaration on TypeScript?

While attempting to create a generic wrapper function, I initially thought the following code would suffice, but unfortunately, it is not functioning as expected: function wrapFunctionWithLogging<T extends Function>(f: (...args: Parameters<T>) ...

Refresh an Angular page automatically

Having a small issue in my angular application. The problem arises on the first page where I display a table listing all employees along with a "Create New Employee" button that opens a form for adding a new employee. However, after submitting the form and ...