0

I am using Visual Studio 2015 community edition with AngularJS 1.4.7 in this project.

ProductAPIController.cs

public class ProductAPIController : ApiController
{
    [HttpGet]
    public IQueryable<Product> MyProducts()
    {
        List<Product> p = new List<Product>() { new Product {ProductName ="abc", Id = 1, Description = "Desc 1" },
                                                new Product {ProductName ="def", Id = 2, Description = "Desc 2" },
                                                new Product {ProductName ="ghi", Id = 3, Description = "Desc 3" } };
        return p.AsQueryable();
    }
}

Product.cs

public class Product
{
    public int Id { get; set; }
    public string ProductName { get; set; }
    public string Description { get; set; }
}

app.module.js I used a separate file for app module as suggested in best practice here

(function () {
    'use strict';
    angular.module('app', ['ngCookies', 'ngRoute'])
})();

homeCtrl.js

here I used both service and the controller in the same file. I even tried it using in a separate file, but still it didn't work.

(function () {
   'use strict';
   angular.module('app')
   .controller('homeCtrl', ['$scope', '$http', 'ProductService', '$location', function homeCtrl($scope,  $http, ProductService, $location) {
    $scope.products = [];

    $scope.getProducts = function () {
        ProductService.getProducts().then(function (response) {
            $scope.products = response.data;
        }, function (err) {
            alert(err);
        });
      };
   }])
   .service('ProductService', ['$http', function ($http) {
       this.getProducts = function () {
          return $http.get('/api/ProductAPI');
       };
    }]);
})();

index.cshtml

Here I use data-ng-repeat="item in products"

<div class="row" ng-app="app">
   <div class="col-md-12" ng-controller="homeCtrl">
      <div class="row">
          <div class="col-md-12">
              <form id="test">
                  <div data-ng-repeat="item in products">
                      <div class="col-sm-4 column productbox">
                          <div class="producttitle">{{item.ProductName}}</div> <div class="action"></div>
                          <div class="productprice">
                              <div class="pull-right">
                                  <div >{{item.Id}}</div> 

                                  <a href="" class="btn btn-danger btn-sm" role="button"><span class="glyphicon glyphicon-arrow-right"></span> Get Quote</a>
                              </div>
                              <div class="pricetext">{{item.Description}}</div>
                          </div>
                      </div>
                  </div>
              </form>
          </div>
      </div>
  </div>
</div>
@section scripts
{
    @Scripts.Render("~/Scripts/angular.js")
    @Scripts.Render("~/Scripts/angular-cookies.min.js")
    @Scripts.Render("~/Scripts/angular-route.js")
    @Scripts.Render("~/Ng/app.module.js")
    @Scripts.Render("~/Ng/homeCtrl.js")
}

I have tried several different approaches but all failed. What am I doing wrong here?

Edit

I have uploaded the sample project to GitHub here . After downloading you may need to rebuild the project to sync related packages.

Edit: Answer

for future reference, in case if an AngularJS beginner like me is looking at this question, the answer as follows. Based on accepted answer given by @Miyuru Ratnayake, just changed the

$scope.getProducts = function () {
 ......
}

to

function getProducts() { 
   ...
}

and called it before the function declaration. Separate function activate() is not necessary.

so the controller is like this:

.controller('homeCtrl', ['$scope', '$cookies', '$http', 'ProductService', '$location', function homeCtrl($scope, $cookies, $http, ProductService, $location) {
    $scope.products = [];
    getProducts();
    function getProducts() { 
        ProductService.getProducts().then(function (response) {
            $scope.products = response.data;
        }, function (err) {
            alert(err);
        });
    };
}])
9
  • 1
    Do you see any error? What happens when you access yoursite/api/productapi from browser, do you get data? Commented Oct 12, 2015 at 2:34
  • 1
    Why return IQueryable? Just return the list. Commented Oct 12, 2015 at 2:35
  • 1
    See the browser network console for errors. Most probably the endpoint being hit is incorrect. Commented Oct 12, 2015 at 2:37
  • @Crowcoder, Still changing to List doesn't solve the problem. It doen't return data. It was IQuueryable since I just copied from a different project. My problem is in angular service call. Commented Oct 12, 2015 at 3:02
  • @user1068538 Changing it to list won't make a difference here, since the serializer will resolve your IQueryable. However, the other questions are spot on. What error messages are you actually getting? Commented Oct 12, 2015 at 3:03

4 Answers 4

2

You should call the getProducts() method in the control somewhere... I don't see that its getting called.

Usually I create a activate method as below and call it that within the control:

(function () {
    'use strict';
    angular.module('app')
    .controller('homeCtrl', ['$scope', '$cookies', '$http', 'ProductService', '$location', function homeCtrl($scope, $cookies, $http, ProductService, $location) {
        $scope.products = [];

        activate();

        function activate() {
            getProducts();
        }

        function getProducts () {
            ProductService.getProducts().then(function (response) {
                $scope.products = response.data;
            }, function (err) {
                alert(err);
            });
        };
    }])
    .service('ProductService', ['$http', function ($http) {
        this.getProducts = function () {
            return $http.get('/api/ProductAPI');
        };
    }]);
})();
Sign up to request clarification or add additional context in comments.

2 Comments

I tried your code, still no result. I will upload my complete project to GitHub.
Your edited code did work. Thanks. One thing to note Activate() function is not necessary you can call the getProducts() function directly. I have noted it at the bottom of the question.
0

You forgot to define method name inside your API call, try this:

.service('ProductService', ['$http', function ($http) {
   this.getProducts = function () {
      return $http.get('/api/ProductAPI/myProducts');
   };
}]);

1 Comment

Method name is not necessary for the given controller since it has only one GET method and it is marked using [HttpGet] attribute.
0

By default ASP.NET Web API looks for "api/{controller}/{id}" which means it ProductAPIController should have methods like GET, POST, PUT, DELETE based on REST concept to work with model.

But in your ProductAPIController code, GetProducts() is customized action method decorated with HTTPGET but even then Web API routing wont understand this until you define that routing should be "api/{controller}/{action}/{id}".

Open WebApiConfig.cs, change the route template as below and test it. I have added {action} so that Web API routing looks for custom actions in API Controller.

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

Just a suggestion If your planning to use custom action methods like 'GetProducts', 'GetProductsByName' etc; the use this route template.

Always test Web API's either using POSTMAN or Fiddler, before using them in client code. I have attached screen shot of GetProducts tested through PostMan.

GetProducts response in PostMan

2 Comments

I think you missed the bit where the OP states that he can successfully hit the endpoint from the browser - it's only an issue from his angular code.
Good advice, but for this controller method name is optional since it has [HttpGet] attribute, and it has only one GET method. Also I always test web service using Fiddler, I have mentioned it in a comment under the question.
0

If you actually take a look at the browser console using the current version of code that you have in github, you'll see immediately that the issue is that the order that the functions are declared in your controller leads you to a situation whereby you are attempting to execute functions that haven't been defined yet.

I reordered your controller to do the following:

$scope.products = [];

function activate() {
    $scope.getProducts();
}

$scope.getProducts = function () {
    ProductService.getProducts().then(function (response) {
        $scope.products = response.data;
    }, function (err) {
        alert(err);
    });
};

activate();

Additionally, in your index.cshtml you are referencing the product details with the wrong case.

For example, you have this:

{{item.ProductName}}

When it should be this:

{{item.productName}}

2 Comments

It is not necessary to call the function after: github.com/johnpapa/angular-styleguide#bindable-members-up-top. Yes, regarding the case of the variable productName is a good point. It was due to the line config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); in WebApiConfig.cs. This was my problem of cut & paste from another project to upload to GitHub in a hurry. I forgot to remove that line.
What that link describes and the code that you have don't match (note the code samples call functions that are declared in the controller, whereas you are calling a function against your $scope). I downloaded your code from github and ran it. In the browser console, the call to activate() failed due $scope.getProducts() having not been defined yet.

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.