5

Using the famous Visual Studio 2017 Angular 4 template, I tested the side navbar buttons and could fetch the in-memory data.

Then I added to the project, a new ASP.Net Core 2.0 API controller connected to a database using Entity Framework, and got it to run with the 200 HTTP GET result.

The controller code:

#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

Now, I wanted to display the resulting ASP.Net Core controller data using Angular, so I created a TypeScript component named “todo” as 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;
}

And created its HTML component as below:

<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>

Then went to add its routing in Nav side bar menu, here is TypeScript code:

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

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

And here is Navbar HTML code:

<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>

And my app.component.ts:

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

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

The issue here is that when I click the menu button to get data nothing happens, all other buttons are working but not this one, and 200 result is still showing when use directly the browser URL.

No error message, and I failed to find solution searching on the net for issues related to Non clickable buttons in Angular, and related to passing data from ASP.Net to Angular.

What am I doing wrong?

16
  • can you show your api code, also what is your controller? it is missing in the http.get(baseUrl + '/api/todo') Commented Oct 15, 2017 at 14:28
  • @Haris, Updated the question. Commented Oct 15, 2017 at 14:39
  • which action are you invoking? GetAll? Commented Oct 15, 2017 at 14:45
  • Yes, the default one, just GetAll. Commented Oct 15, 2017 at 15:26
  • 1
    If I recall correctly, the template includes an example on how to do this - have you compared your 'Todo' solution with the one in the template? Also, try http.get(baseUrl + 'api/todo') instead (note the absence of / in front of api) - the BASE_URL string should already include that. Commented Oct 15, 2017 at 16:56

1 Answer 1

2

(Answer derived from my comments above)

I have had a similar problem to this before using Microsoft's Angular 4 template.


The Problem

Microsoft provides the BASE_URL string as part of their template - it's obtained by extracting the href attribute from the base tag in index.cshtml (the BASE_URL string isn't part of the Angular framework).

The base tag in index.cshtml should look like <base href="~/" />

Which means that anywhere using BASE_URL in your Angular 4 project already has the BASE_URL suffixed with a / character.

So looking at this component calling http.get 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));
    }
}

Note that your call to http.get(baseUrl + '/api/todo') has a / in front of /api/todo - so the parameter passed into http.get will look like http://example.com//api/todo due to the extra / from BASE_URL.


The Solution

Try http.get(baseUrl + 'api/todo') instead (note the absence of / in front of api/todo) - the BASE_URL string should already include that, if nothing else in the template has been changed.


Update 22-03-2018: Using HTTP POST

As per the comment below, here's a quick example function for POST, assuming that baseUrl and http have both been 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));
    }
}

And on the ASP.NET WebAPI side (which implicitly knows how to handle Content-Type of application/json in an HTTP POST request):

public class TodoController : Controller
{
    [HttpPost]
    public IActionResult Post([FromBody] Todo todo)
    {
        return Ok();
    }
}
Sign up to request clarification or add additional context in comments.

8 Comments

And I was previously confused by this video: youtube.com/watch?v=gjqcVugzX8U&t=2222s at 57:40 it was showing clearly http.get('/api/todo'), but this constructor was not including baseUrl.
How did you handle code related to post for the sample example?
@sam The code for POST is very similar - using http.post albeit with a JSON body (using 'Content-Type' as application/json). Have a look at RequestOptions and Headers. The WebAPI controller will implicitly understand how to handle a POST request with Content-Type = application/json. (don't forget to use the [HttpPost] attribute on the controller action)
I'm doing the same at the moment, but don't know why it's not hitting my controller. Here's the brief code segment [Route("api/[controller]")] public class BankController : Controller { [HttpPost] public int SaveBankDetails([FromBody] Bank bank) { } What do you think will be the correct url to hit this?
@sam It depends, are you receiving a 404 in your client? I would think the URL for that would be either be baseUrl + 'api/Bank/SaveBankDetails' or baseUrl + 'api/Bank'. I'd suggest you try testing your API using Postman, because it sounds like a Route issue with your controller - getpostman.com . You could also try a [Route("Bank/SaveBankDetails")] attribute on your POST method too
|

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.