AngularJS controller as syntax "this" is not $scope - angularjs

I'm new to angularjs and I'm trying to employ the styles outlined from this post: https://github.com/johnpapa/angular-styleguide. When using the var vm = this style, rather than $scope, the vars I'm trying to bind to in the template are unavailable. Inspecting vm in the console shows an empty object, not the same as $scope.
/* index.js */
angular.module('myApp', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'ngResource', 'ngRoute', 'ui.bootstrap', 'loginModule', 'sidebarModule', 'jm.i18next'])
.value('baseUri', "/myApp/api")
.config(config)
.run(function($rootScope, $injector) {
$injector.get("$http").defaults.transformRequest = function(data, headersGetter) {
if ($rootScope.oauth !== undefined) {
console.log($rootScope.oauth);
headersGetter()['Access-Token'] = $rootScope.oauth.access_token;
}
if (data) {
return angular.toJson(data);
}
};
});
function config($routeProvider, $i18nextProvider) {
$routeProvider
.when('/', {
templateUrl: 'app/my-module-login/login.html',
controller: 'loginController',
conrollerAs: 'vm' // not available in template
})
.otherwise({
redirectTo: '/'
});
/* login.module.js */
angular.module('loginModule', ['utils']);
/* login.controller.js */
(function(){
'use strict';
angular.module('loginModule')
.controller('loginController', login);
login.$inject = ['$rootScope', '$scope', 'credsFactory', 'loginFactory'];
function login($rootScope, $scope, credsFactory, loginFactory) {
var vm = this; //empty object
vm.user = {};
vm.login = doLogin;
function doLogin(user) {
//user creds
var creds = {
userName: user.username,
password: user.password
};
loginFactory.login(creds)
.success(function (data) {
credsFactory.setCreds(data);
$scope.status = 'Logged in user!';
$scope.creds = creds;
var storageCreds = credsFactory.getCreds();
$rootScope.oauth = {};
$rootScope.oauth.access_token = storageCreds.accessToken;
window.location.hash = "#/logged-in";
}).
error(function(error) {
$scope.status = 'Unable to login user: ' + error.message;
});
}
}
})();
/* login.html */
<div class="form-group form-group-sm">
<div class="text-center">
<!-- how to access vm, {{vm.login(user)}} doesn't work -->
<button type="button" class="btn btn-default" id="login-submit" ng-i18next="button.cancel" ng-click="login(user)"></button>
</div>
</div>

You can declare the controllerAs when setting up the route - there is a typo in your example. (The name does not have to be the same as in loginController function)
Route:
$routeProvider
.when('/', {
templateUrl: 'app/my-module-login/login.html',
controller: 'loginController',
controllerAs: 'vm'
})
View:
<button ng-click="vm.login(user)"></button>
Also, when using "var vm = this;" in the controller function, you do not need to inject $scope. Instead of $scope in doLogin success function:
vm.status = 'Logged in user!';

Related

Unknown provider: DashboardServiceProvider <- DashboardService <- DashboardController

even if I read a lot of solutions according my problem, still to have this Error.
This is my Controller:
#Controller
#RequestMapping( "/dashboard" )
public class DashboardController {
#RequestMapping( value = "", method = RequestMethod.GET )
public HttpEntity<String> dashboard() {
SimpleDateFormat sdf = new SimpleDateFormat( "dd-MM-yyyy" );
return new HttpEntity<String>( "Today is " + sdf.format( new Date() ) );
}
}
this is my index.jsp
<body ng-app="dashboard">
<div ng-controller="DashboardController">
<p>Nome: <input type="text" ng-model="nome"></p>
<p>Cognome: <input type="text" ng-model="cognome"></p>
<input type="button" value="LOGIN" ng-click="login()"/>
</div>
<jsp:include page="includes.jsp"></jsp:include>
<div ng-show="value==1">
{{data}}
</div>
<div ng-show="value==0">
{{ResponseDetails}}
</div>
</body>
this is my module
var Dashboard = angular.module( 'dashboard', ['DashboardService'] )
.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/dashboard', {
templateUrl: '/WEB-INF/pages/dashboard.jsp',
controller: 'DashboardController'
});
}]);
my service
Dashboard.factory('DashboardService', function ($http) {
return {
dashboard: function(successCallback, errorCallback) {
$http.get("/dashboard")
.success(
function (response) {
$scope.data = response;
}
).error(
function (response) {
$scope.data = "ERROR!";
}
)
}
}
});
and finally my controller
angular.module("dashboard", [])
.controller( 'DashboardController', function ($scope, DashboardService) {
$scope.nome = "Daniele";
$scope.cognome = "Comandini";
var data = {
nome: $scope.nome,
cognome: $scope.cognome
};
$scope.value = 0;
var login = function() {
alert("LOGIN ON DASHBOARD");
DashboardService.dashboard();
};
$scope.login = login;
});
My JSP page must only send the request to the DashBoardcontroller, that it has the return the page dashboard.jsp with the current date.
You must not inject dependencies in your module. Dependencies like service, factory have to be injected in controllers. By the way, don't forget to inject ngRoute.
Module becomes:
var Dashboard = angular.module( 'dashboard', ['ngRoute'] )
.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/dashboard', {
templateUrl: '/WEB-INF/pages/dashboard.jsp',
controller: 'DashboardController'
});
}]);
Your controller code is good, just one thing: if you want to use a code compiler like Grunt, Gulp or Webpack, don't forget to add your dependencies as strings:
angular.module('dashboard')
.controller( 'DashboardController', ['$scope', 'DashboardService'], function ($scope, DashboardService) {
I copied your controller with the service injection
.controller( 'DashboardController', ['$scope', 'DashboardService'], function ($scope, DashboardService) {
but in the end I had to write this
Dashboard.controller( 'DashboardController', ['$scope', 'DashboardService' , function ($scope, DashboardService) {
$scope.nome = "Daniele";
$scope.cognome = "Comandini";
var data = {
nome: $scope.nome,
cognome: $scope.cognome
};
$scope.value = 0;
var login = function() {
alert("LOGIN ON DASHBOARD");
DashboardService.dashboard();
};
$scope.login = login;
}]);
I mean I had to include the function into [], and not outside.

how to call the method of controller in routeProvider resolve function

I wanna update the data and then show the view bound with this controller, the code is as follow
angular.module('myApp.student', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/student', {
templateUrl: 'student/student.html',
css: 'student/assets/student.css',
controller: 'studentCtrl',
demand: 'admin'
});
}])
.controller('studentCtrl', ['$scope', 'baseDataUrl', '$http', function($scope, baseDataUrl, $http) {
$scope.update = function() {
$http.get(baseDataUrl + '/student/list').then(function(res) {
$scope.students = res.data;
});
};
$scope.orderProp = "name";
$scope.update();
}]);
how can i do this as there is no way to inject the controller to the config as I know
You could write something like...
angular.module('myApp.student', ['ngRoute'])
.config('routeConfig', function($routeProvider) {
$routeProvider.when('/student', {
templateUrl: 'student/student.html',
css: 'student/assets/student.css',
controller: 'studentCtrl',
demand: 'admin',
resolve: { students : function ($http, baseDataUrl) {
return $http.get(baseDataUrl + '/student/list').then(function (res) {
return res.data;
});
}
}
});
})
.controller('studentCtrl', ['$scope', function($scope, students) {
$scope.students = students;
$scope.orderProp = "name";
}]);
Note that baseDataUrl must either be a constant, or a provider that calls a function to get the required data (in which case, it should be something like baseDataUrl.getUrl()).
With this, it is ensured that every time the /student route is accessed, the scope variable $scope.students contains updated data.

could not modify view after calling ajax from controller using a factory

I have a demo app through which I want to display all countries data in a page(view) using angularjs.
Here is my code:
MyApp.js
var app = angular.module("MyApp", [ 'ui.bootstrap',
'ngRoute',
'MyControllers',
'MyFactory' ]);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/home', {
controller: 'MyController',
templateUrl: 'resources/modules/homeView/homeView.jsp'
})
.when('/view1', {
controller: 'MyController',
templateUrl: 'resources/modules/view1/view1.jsp'
})
.when('/view2', {
controller: 'MyController',
templateUrl: 'resources/modules/view2/view2.jsp'
})
.otherwise({redirectTo: '/home'});
}]);
MyControllers.js
var controllers = angular.module('MyControllers', []);
controllers.controller('MyController', ['$scope', 'Factory',
function($scope, Factory) {
$scope.countries = [];
$scope.showme = false;
$scope.CountryDetailsClick = function() {
var responsePromise = Factory
.getResponse("http://localhost:12345/DemoNew/getCountryList");
responsePromise.success(function(data, status, headers,
config) {
$scope.countries = data;
console.log("countryList size = " + $scope.countries.length);
$scope.showme = true;
});
responsePromise.error(function(data, status, headers,
config) {
alert("AJAX failed");
});
}
}]);
MyFactory.js
var myFactory = angular.module('MyFactory', []);
myFactory.factory('Factory', function($http) {
return {
getResponse : function(url) {
return $http.get(url);
}
}
});
View1.jsp
<body ng-view>
<h1 class="page-header">View1</h1>
{{countries.length}}
</body>
View2.jsp
<body ng-view>
<h1 class="page-header">View2</h1>
{{showme}}
</body>
homeView.jsp
<body ng-view>
<h1 class="page-header">Home</h1>
index.jsp
<body ng-app="MyApp" ng-controller="MyController">
<nav class="navbar navbar-default navbar-static-top" role="navigation"
style="margin-bottom: 0">
Country Details
Country Rate Plans
</nav>
<div id="page-wrapper" ng-view>
</div>
</body>
Now I am getting on console as :-
countryList size = 268
which is correct, I am using spring for backend. I am able to fetch data from the database.
But in View1 I get:
0
Also in View2 I get:
false
That means I am not able to reflect the fetched data to the view. Please help :(
===============================================================
After suggestions given by rob, i changed the following:
I changed my MyFactory.js to:
var myFactory = angular.module('MyFactory', []);
myFactory.factory('Factory', function($http) {
var current = {};
var factory = {
getResponse: function(urlReq){
var data = $http({method: 'GET', url: urlReq}).then(function (result){
current = result.data;
}, function (result){
alert("ERROR: No data returned");
});
},
getList: function(){
return current;
}
}
return factory;
});
And MyControllers.js to:
var controllers = angular.module('MyControllers', []);
controllers.controller('MyController',
function($scope, Factory) {
$scope.CountryDetailsClick = function() {
Factory.getResponse("http://localhost:12345/DemoNew/getCountryList");
}
});
controllers.controller('MyController3',
function($scope, Factory) {
console.log("in controller3");
});
controllers.controller('MyController2',
function($scope, Factory) {
console.log("in controller2");
$scope.countries = Factory.getList();
$scope.showme = true;
});
MyApp.js
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/home', {
controller: 'MyController',
templateUrl: 'resources/modules/homeView/homeView.jsp'
})
.when('/view1', {
controller: 'MyController3',
templateUrl: 'resources/modules/view1/view1.jsp'
})
.when('/view2', {
controller: 'MyController2',
templateUrl: 'resources/modules/view2/view2.jsp'
})
.otherwise({redirectTo: '/home'});
}]);
View2.jsp
<body ng-view>
<h1 class="page-header">View2</h1>
{{showme}}
{{countries}}
</body>
When I click on Country Details, I get "true" and an empty array, but when I click Country Details after clicking Country Rate Plans, it works perfectly fine.
Please find the flaw.!!
You are using the same controller from index.jsp and from your views. So Angular is creating two instances of your controller each with it's own scope. You are calling CountryDetailsClick() from the ng-controller in index.jsp's scope but you are trying to show the value of countries.length from the view's scope.
If you want to share some functionality or state between different controllers (e.g. your CountryDetailsClick() function) then put it in a service.

Angular Js Errors are not understandable

I am unable to understand the errors of Angular JS. I am trying to build a factory but it keeps on giving me the following error in firefox console.
Error: [ng:areq] http://errors.angularjs.org/1.2.9/ng/areq?p0=hospitalController&p1=not%20a%20function%2C%20got%20undefined
My Code is
index
<div class="main ng-scope" ng-view="">
partial
<button data-ng-click="ShowStaff()">show</button>
app.js
var myApp = angular.module('myApp', [
'ngRoute',
'artistControllers'
]);
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/list', {
templateUrl: 'partials/list.html',
controller: 'ListController'
}).
when('/hospital', {
templateUrl: 'partials/hospital.html',
controller: 'hospitalController'
}).
when('/docter', {
templateUrl: 'partials/docters.html',
controller: 'docterController'
}).
when('/details/:itemId', {
templateUrl: 'partials/details.html',
controller: 'DetailsController'
}).
otherwise({
redirectTo: '/hospital'
});
}]);
controller.js
var artistControllers = angular.module('artistControllers', ['ngAnimate']);
artistControllers.controller('ListController', ['$scope', '$http', function($scope, $http) {
$http.get('js/data.json').success(function(data) {
$scope.artists = data;
$scope.artistOrder = 'name';
});
// Starting Factory for Doctor and hospital relationship
artistControllers.factory( 'StaffFactory','$http',function(){
var factory = {};
$http.get('js/hospital.json').success(function(data) {
factory.hospitals = data;
//$scope.hospitalOrder = 'name';
});
$http.get('js/docters.json').success(function(data) {
factory.doctors = data;
//$scope.hospitalOrder = 'name';
});
factory.getDocs = function(){
return factory.doctors;
};
factory.getHos= function(){
return factory.hospitals;
};
factory.getStaff = function(){
var result=[];
var endres=[];
angular.forEach(factory.hospitals, function(hospital){
result=[];
angular.forEach(factory.doctors,function(doc){
if(doc.id==hospital.id)
{
result.push(doc);
}
});
endres.push([hospital,result]);
});
return endres;
}
return factory;
});
artistControllers.SimpleController=function($scope,StaffFactory){
$scope.customers=[];
$scope.hospitals=[ ];
$scope.doctors=[];
$scope.staff=[];
init();
function init()
{
$scope.doctors=StaffFactory.getDocs();
$scope.hospitals=StaffFactory.getHos();
}
$scope.ShowStaff = function()
{
$scope.staff=StaffFactory.getStaff();
}
};
// Ending Factory for Doctor and hospital relationship
}]);
In addition to the actual error explained by #dave, if you want eror messages to be more explicit without having to follow a link, you should use angular.js instead of angular.min.js (the minimized one) for your development environment.
If you follow the link in the error, you will see
Argument 'hospitalController' is not a function, got undefined
It sounds like you have in your html somewhere:
ng-controller="hospitalController"
but you haven't created a controller with that name.

Delay of scope's update after controller's resolve

I'm downloading some data using firebase before presenting new controller's view, but the template "blinks" before showing the data. I have no idea why, it should show the data instantly without any delay. I've recorded it and marked each individual frame (http://i.imgur.com/pUsMCqX.gif). Console logs the data that resolve object returns. You can see that when data is being logged, the template is being shown with no data at frames 2 and 3.
Template:
<div ng-cloak class="wrapper select-character">
<div>
<div>
<h1>Select character</h1>
<div>
<button ng-click="createNewCharacter()">create new character</button>
</div>
characters' list
</div>
</div>
<div ng-repeat="character in characters">
<div>
Name: {{ character.name }}
<br>
Level: {{ character.level }}
<br>
<button ng-click="enterWorld(character.name)">Choose</button>
</div>
</div>
</div>
Controller:
'use strict';
angular.module('App')
.controller('SelectCharacterCtrl', function($scope, $firebaseSimpleLogin, $location, characters) {
$scope.createNewCharacter = function() {
$location.path("/create-character");
};
$scope.enterWorld = function(name) {
alert(name);
};
$scope.characters = characters;
});
App:
'use strict';
angular.module('App', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
'firebase',
'angularfire.firebase',
'angularfire.login'
])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/log-in.html',
controller: 'LogInCtrl'
})
.when('/select-character', {
templateUrl: 'views/select-character.html',
controller: 'SelectCharacterCtrl',
resolve: {
characters: function($q, $timeout, $firebase, $firebaseSimpleLogin) {
var deferred = $q.defer();
var loginObj = $firebaseSimpleLogin(new Firebase("https://<id>.firebaseio.com"));
loginObj.$getCurrentUser()
.then(function(user) { // get login data
var userSync = $firebase(new Firebase('https://<id>.firebaseio.com/users/' + user.uid));
return userSync.$asObject().$loaded();
})
.then(function(user) { // get all characters
var promises = [];
angular.forEach(user.characters, function(name) {
var promise = $firebase(new Firebase('https://<id>.firebaseio.com/characters/' + name));
promises.push(promise.$asObject());
});
$q.all(promises).then(function(sth) {
console.log(sth);
deferred.resolve(sth);
});
});
return deferred.promise;
}
}
})
.when('/create-character', {
templateUrl: 'views/create-character.html',
controller: 'CreateCharacterCtrl'
})
});
Why does the template "blinks" with no data for 2 frames before updating the scope? Any ideas?
Since $asObject does not return a promise, your list of promises are immediately resolved (instead of after all the data is downloaded). Change your list to return promises too:
angular.forEach(user.characters, function(name) {
var promise = $firebase(new Firebase('https://<id>.firebaseio.com/characters/' + name));
promises.push(promise.$asObject().$loaded());
});

Resources