0

I'm working on an Angular application which uses a .NET Core back end with EF to learn Angular. The solution was generated by selecting the Angular and ASP.NET Core project template in Visual Studio 2022. My controllers were scaffolded by selecting the option API Controller with actions, using Entity Framework.

There're two models. Employee and Salary. Each one has a controller. Namely EmployeesController and SalariesController. There's a service class which accesses the controllers. When the user clicks a button on the first page the employees page loads. This works correctly. The user can click a link next to each employee to view their salary. The code for the Salaries page is identical to that of the employees page. But when I click the link the page loads without any data. When I debug the action method on the controller doesn't get hit.

Here's my Employees controller.

namespace EmployeesAngular.Server.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class EmployeesController : ControllerBase
    { 
        private readonly EmployeesAngularServerContext _context;

        public EmployeesController(EmployeesAngularServerContext context)
        {
            _context = context;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<Employee>>> GetEmployee()
        {
            return await _context.Employee.ToListAsync();
        }
    }
}

This is the Salaries controller

namespace EmployeesAngular.Server.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class SalariesController : ControllerBase
    {
        private readonly EmployeesAngularServerContext _context;

        public SalariesController(EmployeesAngularServerContext context)
        {
            _context = context;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<Salary>>> GetSalary()
        {
            return await _context.Salary.ToListAsync();
        }
    }
}

This is the app.component.html file.

<div class="container">
  <h3 class="d-flex justify-content-center">Employee Details</h3>
  <nav class="navbar navbar-expand-sm bg-light navbar-dark">
    <ul class="navbar-nav">
      <li class="nav-item">
        <button routerLink="employees" class="m-1 btn btn-light btn-outline-primary" Button>Employees</button>
      </li>
    </ul>
  </nav>
  <router-outlet></router-outlet>
</div>

Here's the employees.component.html file.

<table class="table table-striped">
  <thead>
    <tr>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Department</th>
      <th>Date Of Joining</th>
      <th>Total Salary</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let dataItem of EmployeeList">
      <td>{{dataItem.firstName}}</td>
      <td>{{dataItem.lastName}}</td>
      <td>{{dataItem.department}}</td>
      <td>{{dataItem.joinDate}}</td>
      <td>{{dataItem.totalSalary}}</td>
      <td><a routerLink="/salaries" routerLinkActive="active" ariaCurrentWhenActive="page">View Salary Components</a></td>
      <td><a routerLink="/editEmployee" routerLinkActive="active" ariaCurrentWhenActive="page">Edit</a></td>
    </tr>
  </tbody>
</table>

This is the code for the salaries.component.html file.

<table class="table table-striped">
  <thead>
    <tr>
      <th>Basic</th>
      <th>Allowance</th>
      <th>Bonus</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let dataItem of SalaryList">
      <td>{{dataItem.basic}}</td>
      <td>{{dataItem.allowance}}</td>
      <td>{{dataItem.bonus}}</td>
    </tr>
  </tbody>
</table>

Here's the employees.component.ts file.

import { Component, OnInit } from '@angular/core';
import { ApiserviceService } from '../apiservice.service';

@Component({
  selector: 'app-employees',
  templateUrl: './employees.component.html',
  styleUrls: ['./employees.component.css']
 })
export class EmployeesComponent implements OnInit {

  constructor(private service: ApiserviceService) { }

  EmployeeList: any = [];

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

  refreshEmpList() {
    this.service.getEmployeeList().subscribe(data => {
      this.EmployeeList = data;
    });
  }
}

Here's the salaries.component.ts file.

import { Component, OnInit } from '@angular/core';
import { ApiserviceService } from '../apiservice.service';
import { ActivatedRoute } from '@angular/router';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-salaries',
  templateUrl: './salaries.component.html',
  styleUrl: './salaries.component.css'
})
export class SalariesComponent implements OnInit {
  constructor(private service: ApiserviceService) {
}

  SalaryList: any = [];

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

  refreshSalary() {
    this.service.getSalaryList().subscribe(data => {
      this.SalaryList = data;
    });
  }
}

This is my service called the apiservice.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
 
export class ApiserviceService {
  constructor(private http: HttpClient) { }

  getEmployeeList(): Observable<any[]> {
    return this.http.get<any[]>('employees');
  }

  getSalaryList(): Observable<any[]> {
    return this.http.get<any[]>('salaries');
  }
}

This is how routing is done in the app-routing-module.ts file.

const routes: Routes = [
  { path: 'employees', component: EmployeesComponent },
  { path: 'salaries', component: SalariesComponent },
  { path: 'editEmployee', component: EditEmployeeComponent },
];

When I click the salary link and observe the console under developer tools in the browser I see an error saying

SyntaxError: Unexpected token '<', "<!doctype "... is not valid

I searched for this error and tried to implement the suggested fixes. As mentioned in this page I modified the code to look like this.

getSalaryList(): Observable<any[]> {
  return this.http.get<any[]>('salaries', { responseType: 'text' });
}

This gives me a compiler error saying

Type 'Observable' is not assignable to type 'Observable<any[]>'

In the responseType property it also says that

No overload matches this call

As suggested in this page I tried to change base href in the Index.html page like this but that didn't work either.

<base href="/">

to

<base href=".">

Based on another answer I modified the code in the service class like this.

getSalaryList(): Observable<any[]> {
    const httpOptions: Object = {
      headers: new HttpHeaders({
        'Accept': 'text/html',
        'Content-Type': 'text/plain; charset=utf-8'
      }),
      responseType: 'text'
    };

    return this.http.get<any[]>('salaries', httpOptions);
}

This also doesn't hit the controller action method. And gives me an error in the browser console saying

NG0900: Error trying to diff...Only arrays and iterables are allowed.

I looked at several other answers to similar questions. But the responses seems to be incomplete. Most questions don't have a selected answer and the suggestions on the answers do not seem to work for this problem. So I'm posting this question.

Although I tried various suggestions because they're given in answers I'm mystified as to why the code doesn't work for Salaries while it works for Employees.

Help is much appreciated.

12
  • What do you get when you hit the controller explicitly with some other tool, for example postman? Commented Feb 6, 2024 at 7:44
  • @Adrian Kokot, I tried with Postman. For the employees URL I'm getting the data correctly as the response. But for the salaries URL it gives the Index.html page. Commented Feb 6, 2024 at 7:53
  • So the problem is not in the client but in the server. Are you sure the controller is loaded correctly? What route are you trying to hit exactly? Are you using some kind of automatic controller loading or manual? Which version of .net are you using? Commented Feb 6, 2024 at 8:01
  • @Adrian Kokot, I don't get what you mean by controller loading. So far in .net core applications I've created the project in Visual Studio, added my code if necessary, and run it. I hadn't changed any default settings. The route I'm trying to hit are given in the service class. The controller class defines the route with the Route attribute as [Route("[controller]")] so in the service class I'm giving the URL as 'salaries' (controller name) to the http get call. I'm using .net core 8. Things work with the employees controller. So I don't know why they don't work with the salaries controller. Commented Feb 6, 2024 at 8:14
  • well, if you use the default setup of everyhing then there's no reason it should not work. Maybe weird question, but did you rebuild the project after adding the new controller? Commented Feb 6, 2024 at 8:31

1 Answer 1

0

What is the problem?

your angular and asp.net are running on different ports and when you write this line

this.http.get<any[]>('employees');

your request will try to get employees through angular port https://localhost:4200/employees and angular will resolve this as EmployeesComponent and not to call the backend

Fix the problem

First, let's distinguish Between your API routes and the component routes. Make your API run on /api/Employees and /api/salaries by changing the following line in controller files

Route("api/[controller]")]

Second, we need to tell angular any route is start with /api/** forward it to the backend, this Can be done by open the file employeesangular.client > src > proxy.conf.js and remove /weatherForecast and add /api/**

const PROXY_CONFIG = [
  {
    context: [
      "/api/**"
    ],
    target,
    secure: false
  }
]

Finally, change your service to use the correct route

export class ApiserviceService {
    constructor(private http: HttpClient) { }

  getEmployeeList(): Observable<any[]> {
    return this.http.get<any[]>('/api/Employees');
  }

  getSalaryList(): Observable<any[]> {
    return this.http.get<any[]>('/api/salaries');
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

what is the target in this case?

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.