Integrating ASP.NET Webforms, WebAPI and AngularJS - angularjs

I'm trying to get my first ASP.NET Webforms and AngularJS app up and running, but I'm struggling...
I created a blank, new ASP.NET 4.5.1 webforms app, and included WebAPI into the mix. I created a sample page for my list of customers, and a standard, EF6-based WebAPI CustomerController : ApiController with all the usual CRUD methods. I tested that WebAPI using Fiddler and low and behold - I get my 8 customers back from my database.
Getting this into AngularJS however has been a bit of a unsuccessful and highly frustrating experience ....
I included AngularJS from NuGet and that seems to have worked - no errors shown or anything, a pile of angular*.js files dumped into my Scripts folder.
I created a basic CustomerList.aspx page based on a master page which includes the <html lang="en" ng-app="TestAngular"> tag.
In order to get the data from the WebAPI service, I created my Angular module, and created a model inside the $scope, and created a service to fetch the data from the WebAPI:
Inside app.js:
var testModule = angular.module('TestAngular', [ ]);
testModule.controller('clientController', function ($scope, clientService) {
$scope.model = [];
clientService.getAllClients(function(results) {
$scope.model.clients = results;
});
$scope.model.clientCount = $scope.model.clients.count;
});
testModule.factory('clientService', function ($http) {
var srv = {};
srv._baseUrl = 'http://localhost:56313';
// Public API
return {
getAllClients: function(callback) {
return $http.get(srv._baseUrl + '/api/Customer').success(callback);
}
};
});
From what limited Javascript understanding I have, this should define a clientService (the testModule.factory() call) that calls my WebAPI URL, gets the JSON back, and the callback function then stuffs those customers retrieved into the $scope.model.clients property, and the $scope.model.clientCount should also be calculated.
My ASPX page looks something like this:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="CustomerList.aspx.cs" Inherits="TestAngular.CustomerList" MasterPageFile="~/Site.Master" %>
<asp:Content runat="server" ID="content" ContentPlaceHolderID="MainContent">
<h2>My customers</h2>
<div class="panel panel-default" data-ng-controller="clientController">
We have a total of {{ model.clientCount }} clients in our database table ...
<div class="list-group" data-ng-repeat="client in model.clients">
<div class="list-group-item">
<span>{{ client.Name }}</span><br/>
<span>{{ client.Name2 }}</span><br/>
</div>
</div>
</div>
</asp:Content>
So the <div> with the data-ng-controller should "connect" that DIV with the AngularJS controller, should load the customers from the WebAPI call, and contain them in the model, which would then be rendered to the ASPX page using the data binding syntax ({{ client.Name }} etc.)
Trouble is: the call to the WebAPI happens and the correct 8 customers are returned, however, when I debug into the Javascript code, the clientCount is always undefined and the ASPX page only shows two empty rows which probably would correspond to customers that have been retrieved - but why only 2, not 8 ??
I'm totally lost and stuck - can anyone spot what I'm doing wrong, what I'm missing here??

You are definately on the right track. At the moment, the problem is down to the clientService getAllClients method.
You should return the promise and then the data will chain through to the controller:
getAllClients: function(callback) {
return $http.get(srv._baseUrl + '/api/Customer').success(callback);
}
You may also want to take a look at the count line:
$scope.model.clientCount = $scope.model.clients.count;
Before the promise is resolved (and the callback is invoked), $scope.model.clients will be undefined. So, I'd expect this line to fail. Also, to get the count of an array, you need length.
You should set the clientCount inside of the callback:
clientService.getAllClients(function(results) {
$scope.model.clients = results;
$scope.model.clientCount = $scope.model.clients.length;
});
Edit:
Typically, it is favoured to use the promise returned by $http. So, the controller would slightly change to:
clientService.getAllClients().then(function(response) {
$scope.model.clients = response.results;
$scope.model.clientCount = response.results.length;
});
And then the service would change to:
getAllClients: function() {
return $http.get(srv._baseUrl + '/api/Customer');
}
Angular uses promises from $q instead of callbacks (for most apis). They make chaining and exception handling much easier.
Also, since, in this case, you know you are handling a promise from $http, you can use the success method in the controller as well:
clientService.getAllClients().success(function(results) {
$scope.model.clients = results;
$scope.model.clientCount = results.length;
});
success unwraps the response and sends only the body through to the callback.

Related

angular to fetch the json service from http

I have my js for angular to fetch the json service from http and am using the {{post.title}} on my html to get the data and post to my html.
The data is not showing up on html page - using code pen.
var app = angular.module("blogApp", []);
app.controller("mainCtrl", function($scope) {
$scope.posts = [];
let postsUrl ="https://jsonplaceholder.typicode.com/posts"
getPosts().then(posts =>{
$scope.posts = posts.slice(3);
$scope.$apply();
});
function getPosts(){
return fetch(postsUrl).then(res=>res.json());
}
});
I have seen your shared codepen. So Ricky as you are new to angularJS, I would suggest you to read the documentation related to angular 1 from here: Angular JS - Documentation
Now coming to your requirement, you required to call an external API and use the data from the result. For that you have to learn about the $http in angularJS : $http documentation
Coming to the code, angular supports the dependency injection. The code you have shared is a mystery for me like what fetch(postsUrl) function is doing? Where is the declaration?
Cut and short, the implementation should be clear and readable. Here is my refactored one:
var app = angular.module("blogApp", []); //here you defined the ng-app module
//you are initializing a controller, you need to inject $http for calling the API
app.controller("mainCtrl", function($scope, $http) {
//Declaration of the posts object
$scope.posts = [];
//Onetime initialization of the API Endpoint URL
let postsUrl ="https://jsonplaceholder.typicode.com/posts";
//A method for getting the posts
function getPosts(){
//We are calling API endpoint via GET request and waiting for the result which is a promise
//todo: Read about the Promises
//A promise return 2 things either a success or a failure callback, you need to handle both.
//The first one is success and the second one is a failure callback
//So in general the structure is as $http.get(...).then(successCallback, failureCallback)
$http.get(postsUrl).then(function(response){
//In promises you get data in the property data, for a test you can log response like console.log(response)
var data = response.data;
$scope.posts = data; //Storing the data in the posts variable
//Note: you don't need to call the $scope.$apply() because your request is with in the angular digest process.
//All the request which are outside the angular scope required a $apply()
}, function(err){
//log the err response here or show the notification you want to do
});
}
//The final step is to call that function and it is simple
getPosts();
});
Coming to the second part to show the data. You have to use the ng-repeat documentation, it is as ng-repeat="var item in collection track by $index". It's documentation is here ng-repeat
So you html should be in this structure:
<div ng-repeat="var post in posts track by $index">
{{post.userid}}
{{post.id}}
{{post.title}}
{{post.body}}
</div>
Now it is onto you to learn and implement.

How to handle multiple components using multiple controllers with one scope value in Angular

I am new to Angular JS.
I have one template for my app, which contains multiple includes for respective controllers. Here is my indexTemplate.html and its controller is indexController.js
<!-- MAIN CONTENT -->
<section class="content">
<!-- LEFT SIDE -->
<div class="form-left-side" ng-init="getAllCityStateZip()">
<!-- Customer info card -->
<div ng-controller="customerInfoController"
ng-include src="'views/load/customerInfo.html'"></div>
<!-- /left side -->
</div>
<!-- RIGHT SIDE -->
<div class="form-right-side">
<!-- Commodity card -->
<div ng-controller="commodityController"
ng-include src="'views/load/commodity.html'"></div>
<!-- /rightside -->
</div>
</section>
<!-- /.content -->
In index controller, I am hitting an API to get datas, as follows
$scope.loadData ={};
//Fetch the Load Data
loadService.fetchLoad()
.then(function(data) {
if (data != null) {
$scope.loadData = data.body;
console.log($scope.loadData)
}
}, function(error) {
var value = error;
}
);
Calling this $scope.loadData in other controllers such as customerInfoController and commodityController. But I am unable to see the data of load in both controllers.
I am trying to get loadData objects in customerInfoController and commodityController, it says undefined
$scope.parent.loadData.id
it is showing undefined.Can someone explain How to handle this.
I searched in Google, so many users suggest Promise, As I have multiple controllers, not sure I can get solution using promise. As I am new to Angular I might be wrong. looking for suggestion.
If I understand correctly you have multiple controllers but you want to access the data in all the controllers.
The easiest solution would be to create a service that fetches the data from the API and stores it in the service. Then you would inject the service into each controller and get the data from the service.
Here's a simple example:
app.service('dataService', function() {
var serviceData;
this.fetchData= function () {
loadService.fetchLoad()
.then(function(data) {
if (data != null) {
serviceData = data.body;
}
}, function(error) {
var value = error;
}
);
}
this.getData = function(){ return serviceData}
});
Then in each controller you would just inject your service and call getData
$scope.data = dataService.getData();
Just Make sure that fetchData is called before getData. You might be able to get away with not using a promise but most likely you will need to use promises to ensure that fetchData has completed before you call getData.

How can I include an angular module in an existing module?

So I've defined a custom section in Umbraco 7:
namespace NZ_Realty_Ltd.CRM
{
[Application("crm", "CRM", "trayuser", 8)]
public class CrmSection : IApplication { }
}
It shows up, no worries here. But it needs a view and controller. So I made a start on the controller:
angular.module("umbraco").controller("CrmController",
function ($scope, $http) {
$http.get('backoffice/crm/ContactApi/GetAll').success(function (data) {
$scope.contacts = data;
});
});
Again, no problem. My data is being read from a C# CRUD api and being sent back to the view. But I want to paginate these results. So I'm trying to use this custom directive to do it: https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination
Here's my html view:
<div ng-controller="CrmController">
<umb-pane>
<p>Page {{currentPage}} / {{totalPages}}</p>
<p>Showing {{pageSize}} items per page:</p>
<ul class="contacts">
<li dir-paginate="contact in contacts | itemsPerPage: 10">
<span>{{contact.name}}</span>
<p>{{contact.bio}}</p>
</li>
</ul>
<dir-pagination-controls></dir-pagination-controls>
</umb-pane>
</div>
The problem is none of these expressions are showing up (they are all blank). I've missed the step of how to include the pagination module. Actually I've been stuck on it for hours. I've tried everything from:
angular.module("umbraco", ['angularUtils.directives.dirPagination']).controller("CrmController",
function ($scope, $http) {
$http.get('backoffice/crm/ContactApi/GetAll').success(function (data) {
$scope.contacts = data;
});
});
... To just including the directive javascript file from <script> tags. But I really have no idea what I'm doing and don't understand the module syntax well enough (I've read through the pagination demo so many times but it just seems SO different to working with the umbraco angularjs app). I saw in the docs that including the second parameter means you're making a new module. But what is the relevance of the information inside the [] on the second parameter? And why would I be creating a new module? Can't I just include the existing directive?
EDIT: This is the closest in my mind to what should work. But I get no contacts listed and no pagination controls showing. My idea with it is to load the pagination module (and thus directive), and then create my controller as normal to avoid conflicts and load order stuff, but also adding default values in the controller as in the example here: http://plnkr.co/edit/Wtkv71LIqUR4OhzhgpqL?p=preview
angular.module("angularUtils.directives.dirPagination");
angular.module("umbraco").controller("CrmController",
function ($scope, $http) {
$http.get('backoffice/crm/ContactApi/GetAll').success(function (data) {
$scope.contacts = data;
$scope.currentPage = 1;
$scope.pageSize = 10;
});
});
You could add this before your controller starts:
app.requires.push('angularUtils.directives.dirPagination');
Read more at:
https://our.umbraco.org/forum/umbraco-7/developing-umbraco-7-packages/47905-Including-an-angular-module

AngularJS Value is NaN after assigned in promise success function

I've been struggling with this the past few Hours and I don't know what to do about it. I am doing an AngularJS application and I use angular-timer Directive.
http://siddii.github.io/angular-timer/
It works perfect when I dynamically assign TimerValue value to the end-time attribute in ng-repeat. But it fails when I use it in My Details page which is using routeParams
<timer end-time="itemDetail.startDate">
so on the Detail page when the user click somewhere he is getting redirected to /someurl/1 The page loads fine the iMages the text everything is working fine. But the timerValue is showing NaN everywhere here is the app structure I've been using
JSON data
[
{
"id" : "1",
"productTitle" : "Motorola 156 MX-VL",
"imgUrl" : "app/assets/images/laptop.png",
"itemPrice" : "170",
"startDate" : 1431249465000
}
]
Page controller
app.controller('detailsController', ['$scope', '$routeParams','itemsService',
function($scope, $routeParams,itemsService) {
itemsService.getItemById($routeParams.itemId).then(function(data){
$scope.itemDetail = data;
});
}]);
The factory
app.factory('itemsService', ['$http','$filter', function($http, $filter){
var itemsService = {
getAllItems: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('app/jsons/items.json').then(function (response) {
return response.data;
});
// Return the promise to the controller
return promise;
}, getItemById : function(id) {
var promise = $http.get('app/jsons/items.json').then(function (response) {
return $filter('filter')(response.data, {id: id})[0];
});
// Return the promise to the controller
return promise;
}
};
return itemsService;
}]);
And in the View I'm using it like this
<div class="large-12 columns" >
Remaning time: <timer end-time="startDate">{{days}} Days, {{hours}} Hours, {{minutes}} Minutes, {{seconds}} Seconds.</timer>
</div>
The timer works if I put the Timestamp directly in the "end-time" attribute and it also work if I assign the Value directly in the controller
$scope.startDate = 1431249465000;
But it doesn't work if I do it like this:
<timer end-time="itemDetail.startDate">
Why is this happening?
you needs eval expression for end-time attributes
<timer end-time="{{itemDetail.startDate}}">
Okay guys. I found a solution that worked for me and its pretty easy though. It seems that the problem is related with the way how angular execute its methods. When i assign a filtered object to the $scope.itemDetail= filteredObj. And when i try to use the itemDetail.startDate in the html the value does not get resolved correctly and i see the NaNs. So this might work correctly when i hook up the Rest api which is still under development but for now i am using mockup jsons with dummy data, and god nows what is going on behind the scenes with Angular. The solution i used was simply to use ng-repeat instead of directly accessing the object property from the html. And it worked.
The solution
i added a ng-repeat directive to the parent div that holds my Item details. And simply accessing the properties like this
<div ng-repeat="item in itemDetails">
{{item.name}}
Remaning time: <timer end-time="item.StartDate">{{days}} Days, {{hours}} Hours, {{minutes}} Minutes, {{seconds}} Seconds.</timer>
</div>
And in this case it works as it suppose to either way or another in my itemDetails object array i have only one Object since i filter the data according the ID that is passed from the $routeParams

angular: write a service which mocks my real backend

I need to write code which accesses data in a backend.
Trouble is that backend is not ready yet. So I want to inject a service which would, possibly by configuration, use the real backend or a mock object.
I think angular services are what I need. But I am not clear how to implement this.
app.factory('myService', function() {
var mySrv;
//if backend is live, get the data from there with $http
//else get data from a mock json object
return mySrv;
});
Somehow I guess I'd have to write two more services, the real one and the fake one, and then call the one or the other in 'myService'?
Maybe I totally misunderstand mocking, but I'd rather not want this to be mocked for test runs (not for unit tests like in this post: Injecting a mock into an AngularJS service - I'd like the app to really use my mock for demo and development purposes.
This is actually where Angular's dependency injection system really comes in handy. Everything in Angular is basically a provider at some level. Methods like service and factory are just convenience functions to avoid boilerplate code.
A provider can be configured during the bootstrap process, which is really handy for setting up scenarios exactly like what you are describing.
At it's simplest, a provider just needs to be a constructor function that creates an object with a $get function. The $get is what creates the actual services, and this is where you can check to see which one to create.
//A simple provider
function DataServiceProvider(){
var _this = this;
_this.$get = function(){
//Use configured value to decide which
// service to return
return _this.useMock ?
new MockService() :
new RealService;
};
}
Now you can register this as a provider with your application module.
angular.module('my-module', [])
.provider('dataService', DataServiceProvider);
And the provider can be configured before it creates the first service instance. By convention, the provider will be available as NAME + 'Provider'
angular.module('my-module')
.config(['dataServiceProvider', function(dataServiceProvider){
//Set the flag to use mock service
dataServiceProvider.useMock = true;
}]);
Now whenever you inject dataService anywhere in your application, it will be using the mock service you provided based on configuration.
You can see a full working example of this in the snippet below.
(function(){
function RealService(){
this.description = 'I\'m the real McCoy!';
}
function MockService(){
this.description = 'I\'m a shady imposter :P';
}
function DataServiceProvider(){
var $this = this;
$this.useMock = false;
$this.$get = function(){
return $this.useMock ?
new MockService() :
new RealService();
};
}
function CommonController(dataService){
this.dataServiceDescription = dataService.description;
}
CommonController.$inject = ['dataService'];
angular.module('common', [])
.provider('dataService', DataServiceProvider)
.controller('commonCtrl', CommonController);
angular.module('provider-app-real-service', ['common'])
.config(['dataServiceProvider', function(dataServiceProvider){
dataServiceProvider.useMock = false;
}]);
angular.module('provider-app-mock-service', ['common'])
.config(['dataServiceProvider', function(dataServiceProvider){
dataServiceProvider.useMock = true;
}]);
var moduleNames = ['provider-app-real-service','provider-app-mock-service'];
angular.forEach(moduleNames, function(modName){
//Have to manually bootstrap because Angular only does one by default
angular.bootstrap(document.getElementById(modName),[modName]);
});
}());
<script src="http://code.angularjs.org/1.3.0/angular.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div class="row" id="provider-app-real-service">
<div class="col-sm-12" ng-controller="commonCtrl as ctrl">
<h1>{{ctrl.dataServiceDescription}}</h1>
</div>
</div>
<div class="row" id="provider-app-mock-service">
<div class="col-sm-12" ng-controller="commonCtrl as ctrl">
<h1>{{ctrl.dataServiceDescription}}</h1>
</div>
</div>
</div>
Does this need to be a service? If you want to create a mock service then Josh's answer above is perfect.
If your not tied to using a service then I suggest looking at my answer to the following question Mock backend for Angular / Gulp app which also about mocking out a backend. Regardless of wether your backend is created or not mocking it out allows for more stable test runs and development of you app.

Resources