Extracting Information from ASP.Net Web API using Angular 4

After experimenting with the well-known Visual Studio 2017 Angular 4 template, I successfully tested the functionality of side navbar buttons to retrieve in-memory data.

I then proceeded to enhance the project by adding a new ASP.Net Core 2.0 API controller that connects to a database using Entity Framework. The controller was set up to run smoothly, producing a 200 HTTP GET result.

Below is the code for the controller:

#region TodoController
namespace TodoAngularUI.Controllers
{
    [Route("api/[controller]")]
    public class TodoController : Controller
    {
        private readonly SchoolContext _context;
        #endregion

        public TodoController(SchoolContext DbContext)
        {
            _context = DbContext;

            if (_context.Todo.Count() == 0)
            {
                _context.Todo.Add(new Todo { TaskName = "Item1" });
                _context.SaveChanges();
            }
        }

        #region snippet_GetAll
        [HttpGet]
        public IEnumerable<Todo> GetAll()
        {
            return _context.Todo.ToList();
        }

        [HttpGet("{id}", Name = "GetTodo")]
        public IActionResult GetById(long id)
        {
            var item = _context.Todo.FirstOrDefault(t => t.Id == id);
            if (item == null)
            {
                return NotFound();
            }
            return new ObjectResult(item);
        }
        #endregion

Next, my goal was to display the data from the ASP.Net Core controller using Angular. I created a TypeScript component named "todo" as shown below:

import { Component, Inject } from '@angular/core';
import { Http } from '@angular/http';

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    public Todo: task[];

    constructor(http: Http, @Inject('BASE_URL') baseUrl: string) {
        http.get(baseUrl + '/api/todo').subscribe(result => {
            this.Todo = result.json() as task[];
        }, error => console.error(error));
    }
}

interface task {
    Id: number;
    TaskName: string;
    IsComplete: boolean;
}

The corresponding HTML component for the above TypeScript code is as follows:

<h1>Todo tasks</h1>

<p>This component demonstrates fetching Todo tasks from the server.</p>

<p *ngIf="!todo"><em>Loading...</em></p>

<table class='table' *ngIf="Todo">
    <thead>
        <tr>
            <th>Id</th>
            <th>Task Name</th>
            <th>Is complete</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let Task of todo">
            <td>{{ Task.Id }}</td>
            <td>{{ Task.TaskName }}</td>
            <td>{{ Task.Iscomplete }}</td>
        </tr>
    </tbody>
</table>

I also added routing for this in the Nav sidebar menu. Here is the TypeScript code for it:

import { Component } from '@angular/core';

@Component({
    selector: 'nav-menu',
    templateUrl: './navmenu.component.html',
    styleUrls: ['./navmenu.component.css']
})
export class NavMenuComponent {
}

And here is the HTML code for the Navbar:

<div class='main-nav'>
<div class='navbar navbar-inverse'>
    <div class='navbar-header'>
        <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
            <span class='sr-only'>Toggle navigation</span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
            <span class='icon-bar'></span>
        </button>
        <a class='navbar-brand' [routerLink]="['/home']">TodoAngularUI</a>
    </div>
    <div class='clearfix'></div>
    <div class='navbar-collapse collapse'>
        <ul class='nav navbar-nav'>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/home']">
                    <span class='glyphicon glyphicon-home'></span> Home
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/counter']">
                    <span class='glyphicon glyphicon-education'></span> Counter
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/fetch-data']">
                    <span class='glyphicon glyphicon-th-list'></span> Fetch data
                </a>
            </li>
            <li [routerLinkActive]="['link-active']">
                <a [routerLink]="['/api/todo']">
                    <span class='glyphicon glyphicon-apple'></span> Todo api
                </a>
            </li>
        </ul>
    </div>
</div>

Lastly, my app.component.ts file looks like this:

import { Component } from '@angular/core';

@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
}

The problem arises when attempting to fetch data by clicking on the respective menu button, as nothing happens. All other buttons are functional except this one, and the browser URL still shows the 200 result directly without triggering any actions.

No error messages are displayed, and despite searching online for solutions related to non-clickable buttons in Angular and passing data from ASP.Net to Angular, I have failed to find a resolution.

What am I missing?

Answer №1

(This answer is based on my previous comments)

I encountered a similar issue while working with Microsoft's Angular 4 template.


The Issue

In the Microsoft template, there is a BASE_URL string provided, which is extracted from the href attribute of the base tag in the index.cshtml file (this string is not intrinsic to the Angular framework).

The correct syntax for the base tag in index.cshtml should be <base href="~/" />.

This means that whenever you use the BASE_URL in your Angular 4 project, it already contains a trailing / character.

If you look at this component making a http.get call using that URL:

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    public Todo: task[];

    constructor(http: Http, @Inject('BASE_URL') baseUrl: string) {
        http.get(baseUrl + '/api/todo').subscribe(result => {
            this.Todo = result.json() as task[];
        }, error => console.error(error));
    }
}

It is essential to note that by calling http.get(baseUrl + '/api/todo'), you end up with an extra / before /api/todo, resulting in a URL like http://example.com//api/todo due to the existing trailing / in BASE_URL.


The Resolution

To address this, consider using http.get(baseUrl + 'api/todo') instead (without the leading / before api/todo) - assuming that the BASE_URL string already includes it unless modified elsewhere in the template.


Update 22-03-2018: Utilizing HTTP POST

Following the suggestion below, here is a brief example function for performing a POST request, assuming baseUrl and http are both injected into the constructor:

import { Observable } from 'rxjs/rx';
import { Http, Headers, RequestOptions } from '@angular/http';

@Component({
    selector: 'todo',
    templateUrl: './todo.component.html'
})
export class TodoComponent {
    constructor(private http: Http, 
        @Inject('BASE_URL') private baseUrl: string) {
    }

    post(todo: Todo) {    
        let fullUrl = this.baseUrl + 'api/todo';
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        
        this.http.post(fullUrl, JSON.stringify(todo), options)
            .subscribe(result => {
                console.log(result);
        }, error => console.error(error));
    }
}

On the ASP.NET WebAPI side, which automatically handles Content-Type as application/json in an HTTP POST request:

public class TodoController : Controller
{
    [HttpPost]
    public IActionResult Post([FromBody] Todo todo)
    {
        return Ok();
    }
}

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

Unable to access FireReader in Ionic 3, while Ionic 4 functions properly

After building my Ionic project on two different computers, I noticed that I received varying results. On the first computer: Ionic Info Ionic: ionic (Ionic CLI) : 4.2.1 (/usr/local/lib/node_modules/ionic) Ionic Framework : ionic-angular 3.9.2 ...

The yarn/npm package manager seems to be utilizing outdated code in an inexplicable way when paired with mocha and typescript

I recently encountered a strange issue that has left me scratching my head. When I manually run my test command, I receive two test results. However, when I execute the same command in a yarn/npm script, only one result is displayed. Has anyone else experi ...

Describing a function in Typescript that takes an array of functions as input, and outputs an array containing the return types of each function

Can the code snippet below be accurately typed? function determineElementTypes(...array: Array<(() => string) | (() => number) | (() => {prop: string}) | (() => number[])>) { /// .. do something /// .. and then return an array ...

Should we be worried about the security of the RxJS library?

Currently, I am in the midst of a project utilizing RxJS within the Angular framework. A recent security evaluation flagged the use of window.postMessage(‘’, ‘*’) in our application as a potential vulnerability. Further investigation pinpointed Imm ...

The loading of npm installed Firebase in Angular4 is not successful

I am currently facing an issue while trying to integrate npm installed Firebase with my Angular4 application. I have successfully installed the latest version of Firebase (version 4.1.1) using npm and verified that the installation was successful. Below is ...

What is the best way to transfer PHP form data to an Angular2 application?

As I am still getting familiar with angular2/4, please bear with me if I overlook something obvious. I am currently working on updating a booking process using angular2/4. Initially, the booking procedure commences on a php website, and once some basic in ...

Accessing the various types within a monorepo from a sibling directory located below the root folder

Seeking assistance in resolving a referencing types issue within a TypeScript monorepo project. Unsure if it is feasible given the current setup. The project structure is as follows: . ├── tsconfig.json ├── lib/ │ └── workers/ │ ...

Why aren't the child route components in Angular 6 loading properly?

I have encountered a small problem. I developed an app using Angular 6 with children routes implemented like this: { path:'pages', component:DatePagesComponent, children : [ {path:'404', component:Error404C ...

Need to transfer data from an Angular 5 application to a server-side file using PHP, but

I am experimenting with sending an encrypted variable from Angular to a PHP script for testing purposes. Below is the client-side script: ngOnInit(){ let user = "am"; let key = "pizza"; let enc = crypto.AES.encrypt(user, key); console.log(enc); let dec = ...

Issue updating Bootstrap in ASP.Net Core 2.1 with Angular Template

Every time I start a new ASP.Net Core 2.1 project with the angular template and try to update the bootstrap version from 3 to 4 in package.json, it ends up breaking my application. Despite numerous attempts such as deleting the node_modules folder and rein ...

Exploring Angular: Looping through an Array of Objects

How can I extract and display values from a JSON object in a loop without using the keyValue pipe? Specifically, I am trying to access the "student2" data and display the name associated with it. Any suggestions on how to achieve this? Thank you for any h ...

Utilizing ngModel with an uninitialized object

What is the most effective way to populate an empty instance of a class with values? For example, I have a User Class and need to create a new user. In my component, I initialize an empty User Object "user: User;". The constructor sets some properties, w ...

Update the class attributes to a JSON string encoding the new values

I have created a new class with the following properties: ''' import { Deserializable } from '../deserializable'; export class Outdoor implements Deserializable { ActualTemp: number; TargetTemp: number; Day: number; ...

Tips for associating an id with PrimeNg menu command

Within my table, I have a list of items that I would like to enhance using PrimeNg Menu for dropdown menu options. The goal is to enable navigation to other pages based on the selected item id. When a user clicks on a menu item, I want to bind the id of th ...

Utilize MaterialUI's Shadows Type by importing it into your project

In our project, we're using Typescript which is quite particular about the use of any. There's a line of code that goes like this: const shadowArray: any = Array(25).fill('none') which I think was taken from StackOverflow. Everything s ...

Confirm the existence of a non-null value

One of the functions I have implemented is designed to remove null values from an array that is passed as input. This function also provides an optional transform functionality, allowing the user to modify the elements of the array into a custom format if ...

Issues with Angular updating the *ngFor Loop

I'm looking to showcase a lineup of upcoming concerts in my HTML, sourced from a web API (which is functioning correctly). The API is encapsulated within a ConcertService: @Injectable({ providedIn: 'root' }) export class ConcertService { ...

Utilizing typed arrays within generic functions

Within a library, there exists a helper function designed to work with arrays of any type. The next step is to expand its functionality to also accommodate typed arrays. However, the challenge lies in the absence of a common base class for typed arrays or ...

A guide on dynamically displaying Primeng Components within Angular applications

My task involves dynamically rendering Primeng components along with regular HTML elements. The template for rendering is stored as a string, as shown below: const dynamicTemplate = `<div class="card flex flex-row gap-3 justify-content-cen ...

The file located at 'node_modules/minimatch/dist/cjs/index' does not contain an exported element called 'IMinimatch'. Perhaps you intended to reference 'Minimatch' instead?

I encountered an error while using rimraf as a devDependency (v5.0.0) in my project. The error message I received was: node_modules/@types/glob/index.d.ts:29:42 - error TS2694: Namespace '".../node_modules/minimatch/dist/cjs/index"' has ...