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.
});
});
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 am trying to share a variable between a controller and a function. But i get an error from the controller, saying this:
TypeError: Cannot read property 'getSet' of undefined
I have gone through numerous tutorials, but don't know where am I going wrong.
My service code is like this:
app.service('shareData', function() {
var selected = ["plz", "print", "something"];
var putSet = function(set) {
selected = set;
};
var getSet = function() {
return selected;
};
return {
putSet: putSet,
getSet: getSet
};
});
I am able to reach selected from my function defined like this:
setDisplay = function($scope, $mdDialog, shareData) {
console.log(shareData.getSet()); // this is working
$scope.selected = shareData.getSet();
$scope.hide = function() {
$mdDialog.hide();
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.answer = function(answer) {
$mdDialog.hide(answer);
};
};
My controller is like this:
app.controller('topicController', ['$scope', '$http', '$mdDialog', 'shareData',
function ($scope, $http, $mdDialog, $mdToast, shareData) {
console.log(shareData.getSet()); // NOT WORKING
}]);
You had extra $mdToast in your topicController controller's factory function, you need to remove it.
The reason behind it was not working is, currently you had 4 dependency mentioned in array like ['$scope', '$http', '$mdDialog', 'shareData', function & then you are using its instance inside the function next to DI array. Inside that function you had actually 5 dependencies where $mdToast extra. So behind the scene what happening is $scope of function hold an value of '$scope' DI array likewise you go right to left. But when it comes to $mdToast(in controller function) it was holding a value of 'shareData'(of DI array) & then the next parameter shareData get nothing.
app.controller('topicController', ['$scope', '$http', '$mdDialog', 'shareData',
function ($scope, $http, $mdDialog, shareData) { //<--removed $mdToast
console.log(shareData.getSet());
}
]);
NOTE: You are using DI inline array annotation, so the sequence in which dependency are injected in array, in same sequence you should
inject then in underlying factory function.
I have a factory, "itemData", that holds a service.
app.factory('itemData', function () {
var itemData = {
Title: 'I should be different',
getTitle: function () {
return itemData.Title;
},
setTitle: function (title) {
return itemData.Title=title;
},
// This is the function to call all of the sets for the larger object
editItem: function (entry)
{
itemData.setTitle(entry);
}
};
return itemData;
});
I have 2 controllers (in different files) associated with 2 separate views.
The first:
// IndexCtrl
app.controller("IndexCtrl", ['$scope','$window','itemData',
function($scope, $window, itemData) {
var entry = "I'm different";
// After a submit is clicked
$scope.bttnClicked = function (entry) {
itemData.editItem(entry);
console.log(itemData.getTitle(); //<--itemData.Title has changed
// moves to page with other controller
$window.location.href = "edit.html";
};
}
]);
and the second, which is not doing what I want:
app.controller("editItemCtrl", ['$scope', '$window', 'itemData',
function ($scope, $window, itemData){
$scope.item = {
"Title": itemData.getTitle(), //<--- Title is "I should be different"
}
}]);
Do you have some kind of router in place in your Angular app? When you change the location href, is it actually causing full page reload?
Services are held in memory and therefore will not maintain state across page reloads.
I consider that the expression ['$scope', '$window', 'itemData',
function ($scope, $window, itemData) force to initialize the dependency injection and for this reason you had two different object.
if you see the doc https://docs.angularjs.org/guide/controller there is a section called Setting up the initial state of a $scope object in this section was described the case of scope but I consider that may be extended at the list of the service in the [...] even in the angular.module(moduleName,[...]) if the [....] are filled the functin return a new module and only for empty square bracket return the current module instance
I hop that this can help you
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 am working on a site where you can search a food, and see if its a fruit, a vegetable, or neither (because I'm bored). I decided to use Angular, even though I'm pretty new to it.
I started getting this error: $rootScope:infdig
Infinite $digest Loop
That's may or may not be the exact phrasing or the error, because the page lags out so much, I can't open the Javascript console.
This my result view controller:
app.controller('resultController', ['$scope', '$routeParams', '$http',
function($scope, $routeParams, $http) {
$scope.result = $routeParams.query;
$scope.whatIsIt = function() {
$http.get('json/foods.json').success(function(data) {
var lists = JSON.parse(data);
var itIsA = 'uuggghhhhh';
if (lists['fruit'].contains($scope.result)) {
itIsA = 'fruit';
} else if (lists['vegetable'].contains($scope.result)) {
itIsA = 'vegetable';
}
});
}
}]);
Heres my app module:
var app = angular.module('fruitOrNot', [
'ngRoute'
]);
app.config(['$routeProvider' ,
function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'layouts/fruit-search.html',
controller: 'searchController'
}).when('/:query', {
templateUrl: 'layouts/result.html',
controller: 'resultController'
});
}]);
And here is layouts/result.html:
<h1>{{ result }}</h1>
<p>{{ result }} is a {{ whatIsIt() }}</p>
So I am wondering what could be causing that $digest loop error, or how to find out, because I can't use the console. Thanks!
The whole project is here on Github as well.
The problem that you were having was that you were setting a field on the scope, which implicitly calls $digest and lays out the template again. But, laying out the template makes the http request again, and then changes the scope, which calls $digest. And that is the infinite loop.
You can avoid this by ensuring that the http request never gets triggered during a layout of the template.
A more angular correct way of implementing your app would be to extract your GET request into a proper service and then injecting your service into the controller.
Then in the controller, you make the service call, like this:
app.controller('resultController', ['$scope', '$routeParams', 'whatsitService',
function($scope, $routeParams, $http) {
$scope.result = $routeParams.query;
whatsitService.doit().then(function(result) {
$scope.whatsit = result;
}
})
;
Your template would look like this:
<h1>{{ result }}</h1>
<p>{{ result }} is a {{ whatsit }}</p>
The problem is you trying to call a function and publish the return value directly and the value that you are trying to return is out of scope. You need to put that value in scope.
Code snippet:
app.controller('resultController', ['$scope', '$routeParams', '$http',
function($scope, $routeParams, $http) {
$scope.result = $routeParams.query;
$scope.itIsA = "";
$scope.whatIsIt = function() {
$http.get('json/foods.json').success(function(data) {
var lists = JSON.parse(data);
var itIsA = 'uuggghhhhh';
if (lists['fruit'].contains($scope.result)) {
$scope.itIsA = 'fruit';
} else if (lists['vegetable'].contains($scope.result)) {
$scope.itIsA = 'vegetable';
}
});
}
}]);
and in the HTML Template:
p>{{ result }} is a {{itIsA}}</p>
Infinite $digest Loop can happen in this below case if you have bound an expression to it like : {{events()}}
and
$scope.events = function() {
return Recording.getEvents();
}
But not in the below case. even if you have bound {{events}}
$scope.events = Recording.getEvents();
The reason is in the first angular suppose the value is changing in every digest loop and it keeps updating it. In second one it simply wait for the promise.
It is happening because of in your angular expression '{{ whatIsIt() }}' in
"{{ result }} is a {{ whatIsIt() }}" you are calling a function that returns a different value each time invoke thus causing a new digest cycle which in turn invokes the function.
I think you should bind the result of whatIsIt() to a value and then use that value in template. check this out https://docs.angularjs.org/error/$rootScope/infdig