I have problem with below code. I prefer controller as controller syntax and assigning data to this instead of $scope. Problem is that it does not work in below case, everything is fine with $scope.user, but this.user doesn't show anything in template. Did I anything wrong or do I need to use $scope in this case?
angular.module('services', [])
.factory('current_user', ['$http', '$q', function($http, $q){
var deferred = $q.defer();
$http.get('/api/user').success(function(data){
deferred.resolve(data);
});
return deferred.promise;
}]);
angular.module('controllers', [])
.controller('myUser', ['current_user', '$scope', function(current_user, $scope){
current_user.then(function(data){
this.user = data; #does not work
$scope.user = data; #works fine
})
}]);
<div ng-controller="myUser as myUser">
<p>Current user: {{ user }}</p>
<p>Current user: {{ myUser.user }}</p>
</div>
When passing a callback into a function like that, you can't be guaranteed that the context (i.e. this) is what you expect. In this case, this is likely to point to the Promise itself.
To work around this, you can either capture a reference to your controller using var ctrl = this outside of your callback:
angular.module('controllers', [])
.controller('myUser', ['current_user', function(current_user){
var ctrl = this;
current_user.then(function(data){
ctrl.user = data;
});
}]);
The above also is why attaching the property to $scope works, because it's always going to be a reference to your controller in that context.
Or else you can use bind to force the context of your promise callback to be your controller:
angular.module('controllers', [])
.controller('myUser', ['current_user', function(current_user){
current_user.then(function(data){
this.user = data;
}.bind(this));
}]);
Related
How can I access $scope data from view to my factory in angularjs? I can access $scope.items from my controller, but when I need to use it in my factory to use the data and generate a pdf I cannot access it.
angular.module('myApp', [])
.controller('myCtrl', function($scope, $http, testFactory) {
$scope.link = "http://localhost:3450/loading.html";
testFactory.all().then(
function(res){
$scope.link = res;
},
function(err){
console.log(err);
}
);
})
.factory('testFactory', function($q){
var pdfInfo = {
content: [
//data should be here...
]
};
var link = {};
function _all(){
var d = $q.defer();
pdfMake.createPdf(pdfInfo).getDataUrl(function(outputDoc){
d.resolve(outputDoc);
});
return d.promise;
}
link.all = _all;
return link;
});
I used factory when I click the generate button from my view, it will wait until the pdf is generated. Coz when I did not do it this way before, I need to click the button twice just to get the pdf generated.
You can just pass the data to your factory as a
function parameter.
angular.module('myApp', [])
.controller('myCtrl', function($scope, $http, testFactory) {
var pdfInfo = {
content: $scope.items
};
$scope.link = "http://localhost:3450/loading.html";
testFactory.all(pdfInfo).then(
function(res) {
$scope.link = res;
},
function(err) {
console.log(err);
}
);
})
.factory('testFactory', function($q) {
var link = {};
function _all(pdfInfo) {
var d = $q.defer();
pdfMake.createPdf(pdfInfo).getDataUrl(function(outputDoc) {
d.resolve(outputDoc);
});
return d.promise;
}
link.all = _all;
return link;
});
I did it. I forgot to send the $scope.items to my factory. So what i did is I added testFactory.all($scope.items) in my controller instead of just plain testFactory.all().
Then in my factory,
I used function _all(value), so I can used the values passed by the views through controller. I am not sure if this is the proper way, but it works. Please suggest good practice if you have.
It is a bad practice to move around $scope to other services, as they may change it and effect your controller logic. It will make a coupling between controllers to other services.
If your factory requires data from the controller, it is better to just pass those parameters to the factory's function.
EDIT: I see you managed to do that, and yes - passing $scope.items is the preferred way (and not, for example, passing $scope).
I get data from remote request by companiesData.getCompanies() and put it into controller variable.
The controller does not wait for promise resolution, figuring the response array empty.
JS controller :
angular.module('X.Exh', [])
.controller('ExhibitorsController', function($scope, $state, $stateParams, companiesData) {
this.companies = [];
companiesData.getCompanies().then(function(response) {
this.companies = response.data;
console.log(this.companies); // working very well
});
});
HTML:
<ion-alpha-scroll ng-model="Exh.companies" key="name" display-key="name" subheader="true" use-complete-alphabet="true">
<!-- Basically the ion alpha scroll is just doing a ng-repeat for every item, it is not the problem here -->
Not waiting for the HTTP request, Exh.companies figures empty. (of course if I don't do this.companies = []; at the beginning of my controller, my HTML says that Exh.companies is undefined.
How do I get data properly?
this inside the unnamed function does not influence original this.companies:
angular
.module('X.Exh', [])
.controller('ExhibitorsController', function($scope, $state, $stateParams, companiesData) {
var vm = this;
vm.companies = []; // you can omit this but for documentation and code clear you can declare it;
companiesData.getCompanies().then(function(response) {
vm.companies = response.data;
console.log(vm.companies); // does not point to local this.companies but to the caller context.
});
});
Please note that vm. runs when you use controllerAs pattern.
Alternatively you can simply access $scope variable:
angular
.module('X.Exh', [])
.controller('ExhibitorsController', function($scope, $state, $stateParams, companiesData) {
$scope.companies = []; // you can omit this but for documentation and code clear you can declare it;
companiesData.getCompanies().then(function(response) {
$scope.companies = response.data;
console.log($scope.companies); // does not point to local this.companies but to the caller context.
});
});
I dont understand what I am missing
I dont get result on html i think this problem managing with controllerAs syntax
note: I can see the result in console console.log(this.movie); - its from controller
app.js
var app = angular.module('mfApp',['ngRoute', 'appControllers']);
app.config(function($routeProvider){
$routeProvider.
when('/:detail',{
templateUrl: 'template/detail.html',
controller : 'detailCtrl' ,
controllerAs: 'movieAs'
}).otherwise({
redirectTo: '/'
});
});
controller.js
var mfControllers = angular.module('appControllers', []);
mfControllers.controller('detailCtrl', ['$scope', '$routeParams', 'appServices', function($scope, $routeParams, appServices){
this.movie = [];
this.id = $routeParams.detail;
appServices.homeSearch(this.id).success(function(response){
this.movie = response;
console.log(this.movie);
// Yes, I do get array result in console
});
}]);
html - template/detail.html
My try
{{movieAs.Title}}
{{movieAs.movie.Title}}
{{movie.movieAs.Title}}
{{movie.Title}} {{title}}
mfControllers.controller('detailCtrl', ['$scope', '$routeParams', 'appServices', function($scope, $routeParams, appServices){
var me = this;
me.movie = [];
me.id = $routeParams.detail;
appServices.homeSearch(me.id).success(function(response){
me.movie = response;
console.log(me.movie);
// Yes, I do get array result in console
});
}]);
EDIT
In Javascript functions are stored as objects, so from your callbeck method inside succeess, you call this which refers to the method that you are running and not to the controller scope.
It is best practice to store the reference to the controller in a variable which can be accessed by the callback methods. me is quite an arbitrary name but widely used to refer as the parent caller. https://github.com/johnpapa/angular-styleguide
The problem is due to wrong this reference.
var mfControllers = angular.module('appControllers', []);
mfControllers
.controller('detailCtrl', ['$scope', '$routeParams', 'appServices', function($scope, $routeParams, appServices) {
var vm = this;
vm.movie = [];
vm.id = $routeParams.detail;
appServices
.homeSearch(vm.id)
.success(function(response) {
// vm !== this; here
vm.movie = response;
console.log(vm.movie);
});
}]);
A good practice when using controllerAs syntax is to assign this to vm at the very beginning of a controller. It holds the reference of the controller correctly.
That is necessary because of javascript function scoping. It will be long to explain it here, but the gist of it is that function creates a new scope, this inside a function will be different. Todd Mott has a very great write up on this.
I have this short all in one Angular script
<script src="http://code.angularjs.org/1.2.14/angular.min.js"></script>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('MainController', ['$scope', '$http', function($scope, $http) {
var getContent = function(filename){
var fc = 'empty';
$http.get(filename+'.json').success(function(data) {
fc = data;
});
return fc;
};
$scope.filename = 'file1';
$scope.content = getContent($scope.filename);
}]);
</script>
<div ng-app="myApp" ng-controller="MainController">
{{filename}} - {{content}}
</div>
and a file in the same directory file1.json:
[
{
"file": "1"
}
]
I can't get the function at getContent to return the file's content. Where am I wrong ?
$http.get sends an asynchronous request to the server. It does not wait for the response. Function getContents continues and returns 'empty'. After a while, when the response arrives, fc = data is executed, but this is not assigned to $scope.content.
Instead, getContents should return a promise object and the caller should have the callback function (in your example, you have used 'success', but that is now deprecated and should be replaced by 'then').
This is how it works:
myApp.controller('MainController', ['$scope', '$http', function($scope, $http) {
var getContent = function(filename){
return $http.get(filename+'.json');
};
$scope.filename = 'file1';
getContent($scope.filename).then(function(data) {
$scope.content = data;
});
}]);
Your function returns a promise, not the result of the function itself as that is executed asynchronously.
You want something close to:
getContent($scope.filename).then(function(response){ $scope.content = response; });
As a side note, it is better to avoid $scope, and to use the controllerAs syntax instead. Only use $scope if you need functionality specifically provided by that service.
With controllerAs syntax,, you can just use this in your controller, instead of $scope, and then in your view, use ctrlAs..
I'm using angular js and have got a controller looking like this
myApp = angular.module("myApp.controllers", []);
myApp.controller("ScheduleCtrl", function($scope, $http, ScheduleService){
$scope.hours = 4;
ScheduleService.test();
ScheduleService.initializeSchedule();
});
and a service (in another file) looking like this
myApp = angular.module('myApp.services', []);
myApp.factory('ScheduleService', ['$rootScope', function($rootScope){
return {
test :
function(){
alert("Test");
},
initializeSchedule :
function(){
alert($rootScope.hours);
}
};
});
To assure everyone that things are hooked up properly from service to controller, the first call to "test()" inside my controller produces the desired output in the alert box. However, for the next function, which as of now should be alerting "4", is instead alerting "undefined".
How can I use either $rootScope or something else in order to utilize scope variables to my service.
You need to inject $rootScope into your controller and use $rootScope instead of $scope. DEMO
myApp.controller("ScheduleCtrl", function($scope, $rootScope, $http, ScheduleService){
$rootScope.hours = 4;
ScheduleService.test();
ScheduleService.initializeSchedule();
});
But in this case you don't need to use $rootScope. You can just pass data as parameter into service function.
return {
test :
function(){
alert("Test");
},
initializeSchedule :
function(hours){
alert(hours);
}
};
How to use Angular $rootScope in your controller and view
To share global properties across app Controllers you can use Angular $rootScope. This is another option to share data.
The preferred way to share common functionality across Controllers is Services, to read or change a global property you can use $rootscope.
All other scopes are descendant scopes of the root scope, so use $rootScope wisely.
var app = angular.module('mymodule',[]);
app.controller('Ctrl1', ['$scope','$rootScope',
function($scope, $rootScope) {
$rootScope.showBanner = true;
}]);
app.controller('Ctrl2', ['$scope','$rootScope',
function($scope, $rootScope) {
$rootScope.showBanner = false;
}]);
Using $rootScope in a template (Access properties with $root):
<div ng-controller="Ctrl1">
<div class="banner" ng-show="$root.showBanner"> </div>
</div>
The problem is that $scope is an isolated scope created for your controller. You need to inject the $rootScope into your controller and modify hours on that if you want it to show up in your service.
See more about scope hierarchies here.
controller:
angular.module('myApp', []).controller('ExampleController', ['$scope', '$rootScope', 'ScheduleService', function($scope, $rootScope, ScheduleService) {
$scope.hours = 4;
$rootScope.hours = 42;
ScheduleService.test();
ScheduleService.initializeSchedule();
}]);
service:
angular.module('myApp')
.factory('ScheduleService', function($rootScope) {
return {
test :
function(){
alert("Test");
},
initializeSchedule :
function(childScopeHours){
alert($rootScope.hours);
}
};
});
Working plnkr.