DomRom123
DomRom123

Reputation: 1

How To Convert Spring Boot Entity to Angular Object

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

Upvotes: 0

Views: 1099

Answers (2)

DomRom123
DomRom123

Reputation: 1

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!

Upvotes: 0

aglamp
aglamp

Reputation: 69

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);
}

Upvotes: 1

Related Questions