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

Tips for assigning dynamic #id(*ngFor) and retrieving its value in Angular2

In my HTML file, I have the following code snippet: <tr *ngFor="let package of packages"> <td *ngFor="let coverage of coverages"> <input type="hidden" #dynamicID [value]="coverage.id"> <-- Include an identifier with the vari ...

The issue is that only the first checkbox within ngFor is being toggled on and off each time a different checkbox is clicked

When I have checkboxes inside a ngFor loop and click on any one of them, only the first one gets checked and unchecked, updating only the status of the first checkbox. Here is the HTML template: <div *ngFor="let num of count" class="m-b-20"> & ...

What is the method to verify that an Action was sent from one Action to another in NGXS?

I've been utilizing NGXS extensively throughout an application, but there are certain tasks that I still struggle to accomplish in a satisfactory manner. The documentation and other questions on this topic haven't provided the answers I seek. One ...

Is it feasible to utilize a single parent component with multiple unique child components in Angular 2?

Is it possible to create a parent component without specifying the child component? Typically, I would define a parent component and include the selector of the child component in the HTML file parent-component-1.html: //some logic <child-component-1 ...

The canActivate: [AuthGuard] feature on the external router is not functioning as expected

I'm encountering an issue with my routing. I attempted to use the following code: const routes: Routes = [ { path: 'home', component: HomeComponent, canActivate: [AuthGuard], children: [ { path: 'events', component: Ev ...

What is the best way to implement multiple HTTP subscriptions in a loop using Angular?

I find myself in a predicament where I need to iterate through an array of strings passed as parameters to a service, which then responds through the HTTP subscribe method. After that, some operations are performed on the response. The issue arises when t ...

What is the best way to utilize a reduce function to eliminate the need for looping through an object in JavaScript?

Currently, my code looks like this: const weekdays = eachDay.map((day) => format(day, 'EEE')); let ret = { Su: '', Mo: '', Tu: '', We: '', Th: '', Fr: '', Sa: '' }; w ...

Executing asynchronous methods in a Playwright implementation: A guide on constructor assignment

My current implementation uses a Page Object Model to handle browser specification. However, I encountered an issue where assigning a new context from the browser and then assigning it to the page does not work as expected due to the asynchronous nature of ...

An error occurred because the property 'coSearchCriteria' is being read from an undefined source in Angular Karma/Jasmine

I am attempting to test an Angular component using Karma and Jasmine. However, I am facing an error that says "Uncaught TypeError: Cannot read property 'coSearchCriteria' of undefined" while testing, even though the normal functionality of the co ...

Having trouble establishing a connection with Db2 while using protractor

Encountering an issue when attempting to establish a connection with a remote DB2 database, resulting in the following exception: SQL30081N A communication error has been detected. The communication protocol in use is 'TCP/IP'. The com ...

The values of nested objects are not being passed as parameters from the AJAX call to the action method

How can I ensure that the nested object values in the parameter are passed correctly to the action method in the controller along with the full object? When calling the AJAX method, the values in the object inside the red ring on the pictures do not get p ...

Trigger events across various tabs

I am currently developing a web application where tab1 triggers the opening of tab2 and needs to refresh when tab2 is closed. However, I have encountered an issue where the next() value is only executed once during component initialization. I suspect that ...

Requesting CORs on Web API version 2.0

After enabling CORs on a Web API, I encountered an issue where GET and POST requests were failing on Chrome. Interestingly, the GET request worked fine on MS Edge, but the POST request threw two error messages in the console: The request could not be proc ...

Creating a HMAC-SHA-256 signature in JavaScript for datatrans: A step-by-step guide

I'm currently working on an Angular project that involves implementing the Datatrans payment system. Unfortunately, I have been facing difficulties in generating a sign for the payment. I have been following the process outlined in this link (enter l ...

What could be causing Typescript Compile Errors to occur during runtime?

In the Visual Studio React + Redux template project, I have created a react component with the following "render()" method: public render() { return ( <React.Fragment> <h1>Welcome to the Adventure Company {th ...

When using Asp.net core, Selenium is unable to locate the geckodriver in the specified directory

Why can't my web API Core 2.0 project find geckodriver.exe and how do I solve it? In my ASP.NET Core 2.0 web API project, I included a geckodriver.exe in the project directory and changed "Copy to output directory" to "Copy always". This is the cont ...

Access and retrieve real-time JavaScript variables directly from a website

Question: I am curious about the possibility of accessing JavaScript variables on a website from C#. Since the scripts are on the client side, is it possible to read their values live through C#? Any assistance or guidance on this matter would be greatly ...

Struggling with the compilation of this Typescript code

Encountering a compile error: error TS2339: Property 'waitForElementVisible' does not exist on type 'signinPage' SigninPage code snippet: export class signinPage{ constructor(){ emailInput: { selector: 'input[type ...

Struggling to deploy a Typescript React / NestJS application on Heroku due to the error message "error TS2307: Cannot find module"?

Switching from a Typescript React/Express app to using Nest.JS has caused complications when deploying to Heroku. The app runs smoothly locally, but encounters build failures on Heroku. Despite efforts to troubleshoot, it remains unclear whether the issues ...

How to utilize Enzyme to call a React prop in TypeScript

I'm currently in the process of converting my Jest tests from Enzyme to TypeScript, and I've come across a specific case that I'm unsure how to resolve. Essentially, I'm attempting to invoke a function passed as a prop to a sub-componen ...