0

I'm trying to use Angular and Spring Boot to show a list of Rule objects. I am not using a JPA Repository to respond to the GET request, so I think I have to 'manually' convert my list of Rule objects on the Spring Boot side to a JSON so that httpClient.get<Rule[]>() can convert it to a list of Rule objects on the Angular side. However, this is currently not working, and my rule objects are not showing up on my webapp.

I'm not using a repository because I'm querying the database on the Spring Boot side, and doing some business logic to only display rules that fall under a certain criteria, and adding some information about the rules that is not in the database.

How do I correctly convert the list of rules to a JSON? Is this even the right approach?

Thank you!

Rule.java:

@Entity
@Table(name = "MSG_DOM") 
public class Rule implements Serializable{
    private static final long serialVersionUID = 1L;
    private String fireType;
    private boolean multipleDrivers;

    @Id
    @Column(name = "MSG_CD", unique = true, nullable = false)
        private String messageCode;

    @Column(name = "MSG_TYP_CD", unique = false, nullable = false)
        private String messageTypeCode;

    @Column(name = "BUS_RUL_CD", unique = false, nullable = false)
        private String busRuleCode;

    @Column(name = "MSG_EXTR_USER_TXT", unique = false, nullable = false)
        private String externalMessageText;

    @Column(name = "MSG_INTRL_USER_TXT", unique = false, nullable = false)
        private String internalMessageText;

    public Rule(){}

    public String getMessageCode(){
        return messageCode;
    }
    
    public void setMessageCode(String messageCode){
        this.messageCode = messageCode;
    }

    ...

RuleController.java:

@RestController
@RequestMapping("/api/v1")
public class RuleController {

    //get all rules
    @CrossOrigin(origins = "http://localhost:4200")
    @RequestMapping(value = "/rules", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
    @ResponseBody
    public Rule[] getAllRules() {
        RuleListService ruleListService = new RuleListService();
        List<Rule> ruleList = ruleListService.listRules();
        Rule[] rules = new Rule[ruleList.size()];
        for (int i = 0; i < ruleList.size(); ++i) rules[i] = ruleList.get(i);
        return rules;
    }
}

RuleListService.java:

public class RuleListService {

    ApplicationContext context =  new AnnotationConfigApplicationContext(BeanConfig.class);
    
    public List<Rule> listRules() {
        RuleList lister = context.getBean(RuleList.class);
        ArrayList<HashMap<String, Rule>> ruleMaps = lister.loadRuleMap();
        ArrayList<Rule> ruleList = new ArrayList<Rule>();
        for(HashMap<String, Rule> ruleMap: ruleMaps) { 
            ruleList.addAll(ruleMap.values());
        }
        return ruleList;
    }

}

rule.ts:

export class Rule {

    constructor(
                private _serialVersionUID : number,
                private _fireType: string,
                private _multipleDrivers: boolean,
                private _messageCode: string,
                private _messageTypeCode: string,
                private _busRuleCode: string,
                private _externalMessageText: string,
                private _internalMessageText: string,) {
                }

    getSerialVersionUID() : number {
        return this._serialVersionUID;
    }

    setSerialVersionUID(value: number) {
        this._serialVersionUID = value;
    }

    ...

rule.service.ts:

@Injectable({
  providedIn: 'root'
})
export class RuleService {
  private baseUrl = "http://localhost:8080/api/v1/rules"
  
  constructor(private httpClient: HttpClient) {}

  getRuleList(): Observable<Rule[]> {
    return this.httpClient.get<Rule[]>(`${this.baseUrl}`).pipe(
        catchError(this.handleError)
    );
  }

  private handleError = (error: Response) => {
    if (error.status === 400) {
      return throwError(() => new Error("Bad Input"));
    }
    if (error.status === 404) {
      return throwError(() => new Error("Not Found"));
    }

    return throwError(() => new Error("App Error"));
  }
}

rule-list.component.ts:

export class RuleListComponent implements OnInit {

  rules: Rule[];
  ruleTest: Rule;

  constructor(private ruleService: RuleService) { }

  ngOnInit(): void {
    this.getRules();
  }

  private getRules() {
    this.ruleService.getRuleList().pipe(
        map((actions: Rule[]) =>
          actions.map(action => { return action as Rule;} )
      )).subscribe(rules => this.rules = rules);
  }
}

rule-list.component.html:

<h2> Rule List </h2>
<table class = "table table-striped">
    <thead>
        <tr>
            <th> MSG_CD </th>
            <th> MSG_TYP_CD </th>
            <th> BUS_RUL_CD </th>
            <th> MSG_EXTR_USER_TXT </th>
            <th> MSG_INTRL_USER_TXT </th>
            <th> Fire Type </th>
            <th> Multiple Drivers </th> 
        </tr>
    </thead>
    <tbody>
        <tr *ngFor = "let rule of rules" >
            <td> {{rule.getMessageCode()}} </td>
            <td> {{rule.getMessageTypeCode()}} </td>
            <td> {{rule.getBusRuleCode()}} </td>
            <td> {{rule.getExternalMessageText()}}</td>
            <td> {{rule.getInternalMessageText()}} </td>
            <td> {{rule.getFireType()}} </td>
            <td> {{rule.getMultipleDrivers()}} </td>
        </tr>
    </tbody>
</table>

UPDATE: I'm still not seeing the data in the table, and have gotten this error from the dev console on my browser:

ERROR TypeError: rule_r1.getMessageCode is not a function
    at RuleListComponent_tr_20_Template (rule-list.component.html:16:18)

I tried changing the variables to public in the Rule.ts constructor, and accessing the variables directly instead of calling get() methods like so, {{rule._messageCode}}, for all table fields, and while it (obviously) got rid of the error, I still did not see data in the table.

The table has been populated with rows indicating there is actually a Rule[] being sent and received correctly, but there is no data in any row.

I've also updated all included code, since many changes have been made.

Table Screenshot

5
  • Spring Boot apps usually start on port 8080 and your error is pointing to an address with port 4200 which is the default angular dev server port. Maybe this is the problem. Commented Jun 27, 2022 at 12:53
  • Instead of return gson.toJson(ruleListService.listRules()); just return ruleListService.listRules() ditch the GSON stuff. You also have to make sure that your fields match (in the DTO on the java side and the DTO on the JS side, currently they don't so no binding/conversion on the JS side will occur). Commented Jun 27, 2022 at 14:03
  • @M.Deinum, I've updated the DTO on both sides (and made the changes in the post as well), do you think this is still an issue? Commented Jun 27, 2022 at 14:55
  • Can you add the complete frontend Rule code please? The error you see means that your frontend Rule object doesn't have a function getMessageCode() Commented Jun 27, 2022 at 15:58
  • And what you also should keep in mind is that your Java code isn't prepending an underline before the json keys and thus it's probably not mapping the values to the fields prepended with underlines in the frontend. Commented Jun 27, 2022 at 16:09

2 Answers 2

1

There's no need to manually convert your Java Object to Json. Spring Rest controllers do that by default. You can also specify it in your RestController like:

@RequestMapping(value = "/rules", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})

Then in your Angular service it should automatically convert the json to your typescript object:

@Injectable({
  providedIn: 'root'
})
export class RuleService {
  private baseUrl = "http://localhost:4200/api/v1/rules"
  
  constructor(private httpClient: HttpClient) {}

  getRuleList(): Observable<Rule[]> {
    return this.httpClient.get<Rule[]>(this.baseUrl).pipe(
      catchError(this.handleError)
    );
  }

 private handleError = (error: Response) => {
    if (error.status === 400) {
      return throwError(new BadInput(error));
    }
    if (error.status === 404) {
      return throwError(new NotFoundError());
    }

    return throwError(new AppError(error));
  }
}

Just make sure that your typescript model has getters and setters (not 100% it is needed but try it out) and that the fields in the json match with your model's.

export class Rule {

  constructor(private _messageCode?: string,
              private _messageTypeCode?: string,
              private _busRuleCode?: string,
              private _externalMessageText?: string,
              private _internalMessageText?: string) {
  }


  get messageCode(): string {
    return this._messageCode;
  }

  set messageCode(value: string) {
    this._messageCode= value;
  }

  .......
}

Last but not least. Try to change your getRules() method in rule-list.component.ts like:

private getRules() {
    this.ruleService.getRuleList().pipe(
        map(actions =>
          actions.map(action => { return action as Rule;} )
      )).subscribe(rules => this.rules = rules);
}
Sign up to request clarification or add additional context in comments.

3 Comments

I added everything you suggested, and my data is still not showing up in the table.
updated my answer. Should be working now. If not, it would be helpful if you could post the errors you get in your console.
I updated my program to match your updated answer, but I'm still not seeing any data in the table. There are however rows indicating the list is being transferred correctly.
0

Posting as an answer not an update in order to help anyone who has the same issue.

  1. Moved attributes of Rule object out of constructor in Rule.ts
  2. Per @BeWu's suggestion, got rid of the "_" prepending the attribute names in Rule.ts
  3. Changed getRules() in rule-list.component.ts to:
  private getRules() {
    this.ruleService.getRuleList().subscribe(
      res => {
        this.rules = res.map(x => Object.assign(new Rule(), x)); 

My table is now displaying all values correctly. I think the issue was stemming from the getRules() method not properly converting the data from ruleService.getRuleList() into Rule objects. I found a similar question with a great answer that had lots of useful information, linked here.

Thank you to everyone who took the time to post suggestions and guidance!

1 Comment

What version of Angular are you using @DomRom123?

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.