Angular-JS routing get data from MVC controller - javascript

I have implemented code to retrieve data from MVC Controller using Angular-JS using ngRoute. What I have implemented is, I have two action methods in MVC controller and at client side I have two menu buttons and by clicking on each button, it retrieves data from respective action method. I have used Angular-Route, Angular Factory Method and ng-view
Till now it is fine. But what I can see is AngularJS doesn't go to Action Method every time when i click on the button. Once data have been retrieved, it only shows the respective view. And in this situation I cannot retrieve fresh data from database. So If I have to call action method every time how can I achieve this ?
This is what I have implemented:
AccountController:
public ActionResult GetAccounts()
{
var repo = new AccountRepository();
var accounts = repo.GetAccounts();
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var jsonResult = new ContentResult
{
Content = JsonConvert.SerializeObject(accounts, settings),
ContentType = "application/json"
};
return jsonResult;
}
public ActionResult GetNumbers()
{
var repo = new AccountRepository();
var numbers = repo.GetNumbers();
var setting = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var jsonResult = new ContentResult()
{
Content = JsonConvert.SerializeObject(numbers, setting),
ContentType = "application/json"
};
return jsonResult;
}
Account.JS:
'use strict';
var account = angular.module('accountModule', ['ngRoute']);
account.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/Employees', { templateUrl: '../Templates/Employees.html', controller: 'accountController' })
.when('/Numbers', { templateUrl: '../Templates/Numbers.html', controller: 'numberController' })
.otherwise({
redirectTo: '/phones'
});;
}]);
account.controller('accountController', [
'$scope',
'accountService',
function ($scope, accountService) {
accountService.getEmployees().then(function(talks) {
$scope.employees = talks;
}, function() {
alert('Some error');
});
}
]);
account.controller('numberController', [
'$scope',
'accountService',
function ($scope, accountService) {
accountService.getNumbers().then(function (retrievedNumbers) {
$scope.telephoneNumbers = retrievedNumbers;
}, function () {
alert('error while retrieving numbers');
});
}
]);
AccountService.JS:
account.factory('accountService', ['$http', '$q', function ($http, $q) {
var factory = {};
factory.getEmployees = function () {
var deferred = $q.defer();
$http({
method: 'GET',
url: 'GetAccounts'
}).success(deferred.resolve).error(deferred.reject);
return deferred.promise;
};
factory.getNumbers = function () {
var deferred = $q.defer();
$http({
method: 'GET',
url: 'GetNumbers'
}).success(deferred.resolve).error(deferred.reject);
return deferred.promise;
};
return factory;
}]);
and my view is something like this:
#{
ViewBag.Title = "Index";
Layout = "~/Views/shared/_Layout.cshtml";
}
<script type="text/javascript" src="../../Scripts/Modules/Account/Account.js"></script>
<script type="text/javascript" src="../../Scripts/Modules/Account/AccountService.js"></script>
<div ng-app="accountModule">
<div>
<div class="container">
<div class="navbar navbar-default">
<div class="navbar-header">
<ul class="nav navbar-nav">
<li class="navbar-brand">Employees</li>
<li class="navbar-brand">Numbers</li>
</ul>
</div>
</div>
<div ng-view>
</div>
</div>
</div>
</div>

Related

AngularJS - Load Controller asynchronously From AJAX Without Changing Route

I want to dynamically load an angular controller upon an ajax call that renders a new view(HTML).
Here is what i have:
example of a view. HTML Snippet From AJAX
<!-- CVS Pharmacy Extracare - Add View -->
<div ng-controller="cvsViewCtrl" class="container-fluid">
<div class="col-md-8 col-md-offset-2">
<h3 id="asset-title" class=""></h3>
<br>
<p>Member ID</p>
<input class="input-s1 block-elm transition" type="text" placeholder=""/>
<br>
<input class="add-asset-btn btn btn-success block-elm transition" type="button" value="Add Asset!" ng-click="prepareCVS();"/>
</div>
</div>
the separate script that pertains to the view
App.controller('cvsViewCtrl', ['$scope', '$http', function($scope, $http){
console.log('cvs view loaded');
$scope.prepareCVS = function() {
console.log('admit one');
}
}]);
and the function that loads them
$scope.setAddAssetView = function(a) {
console.log(a);
if($scope.currentAddView == a) {
console.log('view already set');
return;
}
$scope.currentAddView = a;
$('#main-panel').html('');
$http({
method: 'POST',
url: '/action/setaddassetview',
headers: {
'Content-Type': 'application/json',
},
data: {
asset: a,
}
}).then(function(resp){
// Success Callback
// console.log(resp);
var index = resp.data.view.indexOf('<s');
var script = resp.data.view.slice(index);
var html = resp.data.view.replace(script, '');
$('#main-panel').html( html );
$('#asset-title').text(a.name);
var indexTwo = a.view.indexOf('/add');
var scriptLink = insertString(a.view, indexTwo, '/scripts').replace('.html', '.js').replace('.', '');
console.log( scriptLink );
window.asset = a;
$.getScript(scriptLink, function(data, textStatus, jqxhr){
console.log('loaded...');
})
},
function(resp){
// Error Callback
console.log(resp);
});
}
when $.getScript runs, the script gets loaded successfully but it doesn't initialize the controller. i even tried:
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = scriptLink;
s.innerHTML = null;
s.id = 'widget';
document.getElementById('switch-script').innerHTML = '';
document.getElementById('switch-script').appendChild( s );
this appends the script with the right link but still doesn't get initialized. How can i work around this?
Make this changes in your App, and be sure to load the controller file before the html with ng-controller rendered.
var App = angular.module("app", [...]);
App.config(["$controllerProvider", function($controllerProvider) {
App.register = {
controller: $controllerProvider.register,
}
}]);
App.register.controller('cvsViewCtrl', ['$scope', '$http', function($scope, $http){
console.log('cvs view loaded');
$scope.prepareCVS = function() {
console.log('admit one');
}
}]);
That would actually not be the right way to handle things.
If you use ui-router for routing, you can load the controller in the resolve function. A good service for that is ocLazyLoad.
That can be done as follows:
var app = angular.module('myApp', [
'ui.router',
'oc.lazyLoad',
]);
angular.module('myApp').config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('main', {
url: '/main',
templateUrl: 'app/views/main.view.html',
controller: 'mainCtrl',
resolve: {
loadCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load('app/ctrls/main.ctrl.js');
}],
}
})
.state('second', {
url: '/second',
templateUrl: 'app/views/second.view.html',
controller: 'secondCtrl',
resolve: {
loadCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load('app/ctrls/controller_TWO.ctrl.js');
}],
}
})
});
When you're changing from one route to another, resolve will be triggered before the html is loaded and the controller will be registered properly.

Data for listing page and detail page without 2 API calls

I have set up a service to return a listing of clients from my API. Using UI-router, I can successfully pass a client's id to the details state - however, it seems unnecessary here to make another API call to retrieve a single client when I have all the necessary data in my controller.
What is the best way to use the ID in my detail state URL to show data for that client? Also - if a user browses directly to a client detail URL - I'll need to then make a call to the API to get just that client data - or is there a better way?
EDIT: I am not looking to load the two views on the same 'page', but completely switch views here, from a listing page to a detail page.
Routes in App.js
$stateProvider
.state('root', {
abstract: true,
url: '',
views: {
'#': {
templateUrl: '../partials/icp_index.html',
controller: 'AppController as AppCtrl'
},
'left-nav#root': {
templateUrl: '../partials/left-nav.html'
},
'right-nav#root': {
templateUrl: '../partials/right-nav.html'
},
'top-toolbar#root': {
templateUrl: '../partials/toolbar.html'
}
/*'footer': {
templateUrl: '../partials/agency-dashboard.html',
controller: 'AppController as AppCtrl'
}*/
}
})
.state('root.clients', {
url: '/clients',
views: {
'content#root': {
templateUrl: '../partials/clients-index.html',
controller: 'ClientsController as ClientsCtrl'
}
}
})
.state('root.clients.detail', {
url: '/:clientId',
views: {
'content#root': {
templateUrl: '../partials/client-dashboard.html',
//controller: 'ClientsController as ClientsCtrl'
}
}
})
// ...other routes
Service, also in app.js
.service('ClientsService', function($http, $q) {
this.index = function() {
var deferred = $q.defer();
$http.get('http://api.icp.sic.com/clients')
.then(function successCallback(response) {
console.log(response.data);
deferred.resolve(response.data);
},
function errorCallback(response) {
// will handle error here
});
return deferred.promise;
}
})
And my controller code in ClientsController.js
.controller('ClientsController', function(ClientsService) {
var vm = this;
ClientsService.index().then(function(clients) {
vm.clients = clients.data;
});
});
And finally, my listing page clients-index.html
<md-list-item ng-repeat="client in ClientsCtrl.clients" ui-sref="clients-detail({clientId : client.id })">
<div class="list-item-with-md-menu" layout-gt-xs="row">
<div flex="100" flex-gt-xs="66">
<p ng-bind="client.name"></p>
</div>
<div hide-xs flex="100" flex-gt-xs="33">
<p ng-bind="client.account_manager"></p>
</div>
</div>
</md-list-item>
You can use inherited states like suggested here.
$stateProvider
// States
.state("main", {
controller:'mainController',
url:"/main",
templateUrl: "main_init.html"
})
.state("main.details", {
controller:'detailController',
parent: 'main',
url:"/:id",
templateUrl: 'form_details.html'
})
Your service does not change.
Your controllers check if the Model has been retrieved:
app.controller('mainController', function ($scope, ClientsService) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
});
})
app.controller('detailController', function ($q, $scope, ClientsService, $stateParams) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
$scope.Item = data[$stateParams.id];
});
})
See
http://plnkr.co/edit/I4YMopuTat3ggiqCoWbN?p=preview
[UPDATE]
You can also, if you must, combine both controllers:
app.controller('mainController', function ($q, $scope, ClientsService, $stateParams) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
$scope.Item = data[$stateParams.id];
});
})
I would change the service to cache the data. With $q.when() you can return a promise from a variable. So you save your response in a variable, and before doing the API call you check if the cache has been set. If there is any cache, you return the data itself. Otherwise, you do the usual promise call.
.service('ClientsService', function($http, $q) {
var clients = null;
this.getClient = function(id) {
if (clients !== null) {
return $q.when(id ? clients[id] : clients);
}
var deferred = $q.defer();
$http.get('http://api.icp.sic.com/clients').then(function(response) {
clients = response.data;
deferred.resolve(id ? clients[id] : clients);
}, function (response) {
// will handle error here
});
return deferred.promise;
}
})

Passing return of function to another state

I have been trying to send data from one controller to another. A little background this is code being used in an ionic application if that helps any. I want the to send the data from send() function to the SubCtrl. The send function is being called in MainCtrl. I have created a service for this but the data is still not being shared. What am I missing to complete this action?
var app = angular.module('testapp', []);
app.config(function($stateProvider, $urlRouterProvider) {
"use strict";
/* Set up the states for the application's different sections. */
$stateProvider
.state('page2', {
name: 'page2',
url: '/page2',
templateUrl: 'page2.html',
controller: 'MainCtrl'
})
.state('page3', {
name: 'page3',
url: '/page3',
templateUrl: 'page3.html',
controller: 'SubCtrl'
});
$urlRouterProvider.otherwise('/page2');
});
app.factory('dataShare', function($rootScope) {
var service = {};
service.data = false;
service.sendData = function(data) {
this.data = data;
$rootScope.$broadcast('data_shared');
console.log(data);
};
service.getData = function() {
return this.data;
};
return service;
});
app.controller('MainCtrl', function($scope, $state, $http, dataShare) {
$scope.text = 'food';
$scope.send = function() {
dataShare.sendData(this.text);
};
});
app.controller('SubCtrl', function($scope, $state, dataShare) {
"use strict";
var sc = this;
$scope.text = '';
$scope.$on('data_shared', function() {
var text = dataShare.getData();
sc.text = dataShare.data;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script id="page2.html" type="text/ng-template">
<div>{text}}</div>
<input type='text' ng-model='text' />
<button class="button button-outline button-royal" ng-click="send();">add</button>
</script>
<script id="page3.html" type="text/ng-template">
<div>text: {{text}}</div>
</script>
I was able to figure this issue out after reading this page. If anyone is having a similar issue I would encourage this reading. Also the video link on this post was really helpful.

Angular load selectlist with json from mvc controller

Here is my view:
<div>
<select ng-model="firstSupplier" ng-options="firstSupplier as firstSupplier.SupplierName for firstSupplier in jsonSuppliers"></select>
</div>
and here is my angular controller:
(function() {
var app = angular.module("CustCMS");
var indexController = function($scope, $http) {
$http.get("/Home/GetAllSuppliers")
.then(function (response) {
$scope.jsonSuppliers = response.data;
$scope.firstSupplier = $scope.jsonSuppliers[0];
});
};
app.controller("IndexController", ["$scope", "$http", indexController]);
}());
and here is my mvc controller:
public JsonResult GetAllSuppliers()
{
var suppliers = GetAll();
return Json(new { data = suppliers}, JsonRequestBehavior.AllowGet);
}
here is the json that returns to the angular controller jsonSuppliers:
{"data":[{"SupplierId":1,"SupplierName":"Normstahl","PrefixCountryCode":"NS01COUNTRY","UseOrderSystem":false,"IsDirect":false,"Customers":[]},{"SupplierId":2,"SupplierName":"TestSupplier 2","PrefixCountryCode":null,"UseOrderSystem":false,"IsDirect":false,"Customers":[]},{"SupplierId":3,"SupplierName":"Ditec","PrefixCountryCode":"DIIT01COUNTRY","UseOrderSystem":false,"IsDirect":false,"Customers":[]},{"SupplierId":4,"SupplierName":"Ospf","PrefixCountryCode":"CRCOUNTRY","UseOrderSystem":false,"IsDirect":false,"Customers":[]},{"SupplierId":5,"SupplierName":"Alsta","PrefixCountryCode":null,"UseOrderSystem":false,"IsDirect":false,"Customers":[]}]}
Is where something that im missing here?
I don't get any errors in the console....
Is there an easier way to do it that would be much appreciated!

Getting undefined in AngularJS

I'm working on building a little app that accepts input from a form (the input being a name) and then goes on to POST the name to a mock webservice using $httpBackend. After the POST I then do a GET also from a mock webservice using $httpBackend that then gets the name/variable that was set with the POST. After getting it from the service a simple greeting is constructed and displayed back at the client.
However, currently when the data gets displayed now back to the client it reads "Hello undefined!" When it should be reading "Hello [whatever name you inputed] !". I used Yeoman to do my app scaffolding so I hope everyone will be able to understand my file and directory structure.
My app.js:
'use strict';
angular
.module('sayHiApp', [
'ngCookies',
'ngMockE2E',
'ngResource',
'ngSanitize',
'ngRoute'
])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.otherwise({
redirectTo: '/'
});
})
.run(function($httpBackend) {
var name = 'Default Name';
$httpBackend.whenPOST('/name').respond(function(method, url, data) {
//name = angular.fromJson(data);
name = data;
return [200, name, {}];
});
$httpBackend.whenGET('/name').respond(name);
// Tell httpBackend to ignore GET requests to our templates
$httpBackend.whenGET(/\.html$/).passThrough();
});
My main.js:
'use strict';
angular.module('sayHiApp')
.controller('MainCtrl', function ($scope, $http) {
// Accepts form input
$scope.submit = function() {
// POSTS data to webservice
setName($scope.input);
// GET data from webservice
var name = getName();
// Construct greeting
$scope.greeting = 'Hello ' + name + ' !';
};
function setName (dataToPost) {
$http.post('/name', dataToPost).
success(function(data) {
$scope.error = false;
return data;
}).
error(function(data) {
$scope.error = true;
return data;
});
}
// GET name from webservice
function getName () {
$http.get('/name').
success(function(data) {
$scope.error = false;
return data;
}).
error(function(data) {
$scope.error = true;
return data;
});
}
});
My main.html:
<div class="row text-center">
<div class="col-xs-12 col-md-6 col-md-offset-3">
<img src="../images/SayHi.png" class="logo" />
</div>
</div>
<div class="row text-center">
<div class="col-xs-10 col-xs-offset-1 col-md-4 col-md-offset-4">
<form role="form" name="greeting-form" ng-Submit="submit()">
<input type="text" class="form-control input-field" name="name-field" placeholder="Your Name" ng-model="input">
<button type="submit" class="btn btn-default button">Greet Me!</button>
</form>
</div>
</div>
<div class="row text-center">
<div class="col-xs-12 col-md-6 col-md-offset-3">
<p class="greeting">{{greeting}}</p>
</div>
</div>
At the moment your getName() method returns nothing. Also you cant just call getName() and expect the result to be available immediately after the function call since $http.get() runs asynchronously.
You should try something like this:
function getName () {
//return the Promise
return $http.get('/name').success(function(data) {
$scope.error = false;
return data;
}).error(function(data) {
$scope.error = true;
return data;
});
}
$scope.submit = function() {
setName($scope.input);
//wait for the Promise to be resolved and then update the view
getName().then(function(name) {
$scope.greeting = 'Hello ' + name + ' !';
});
};
By the way you should put getName(), setName() into a service.
You can't return a regular variable from an async call because by the time this success block is excuted the function already finished it's iteration.
You need to return a promise object (as a guide line, and preffered do it from a service).
I won't fix your code but I'll share the necessary tool with you - Promises.
Following angular's doc for $q and $http you can build yourself a template for async calls handling.
The template should be something like that:
angular.module('mymodule').factory('MyAsyncService', function($q, http) {
var service = {
getNames: function() {
var params ={};
var deferObject = $q.defer();
params.nameId = 1;
$http.get('/names', params).success(function(data) {
deferObject.resolve(data)
}).error(function(error) {
deferObject.reject(error)
});
return $q.promise;
}
}
});
angular.module('mymodule').controller('MyGettingNameCtrl', ['$scope', 'MyAsyncService', function ($scope, MyAsyncService) {
$scope.getName = function() {
MyAsyncService.getName().then(function(data) {
//do something with name
}, function(error) {
//Error
})
}
}]);

Categories

Resources