I need to pass a scope value to a service, what I'm doing now is declearing the service's function in the controller and passing the scope value as a parameter to this function. Here is the code
HTML code
<md-datepicker ng-model="dateDebut" md-placeholder="Enter date"> </md-datepicker>
<md-datepicker ng-model="dateFin" md-placeholder="Enter date" ></md-datepicker>
Controller code
app.controller('graphCtrl', function($scope, $stateParams, graphDetails, $filter,$http) {
var self = this;
self.dateDebut = $scope.dateDebut;
self.dateFin = $scope.dateFin;
var mettreAJour = graphDetails.mettreAJour(self.dateDebut, self.dateFin);
$scope.labels = mettreAJour.labels;
$scope.data = mettreAJour.data;
});
Service code
app.factory('graphDetails', function($filter, $http) {
var labels = [];
var data = [
[]
];
return {
mettreAJour: function(dateDebut, dateFin) {
if (dateDebut && dateFin) {
var dd = $filter('date')(dateDebut, 'yyyy-MM-dd;HH:mm:ss');
var df = $filter('date')(dateFin, 'yyyy-MM-dd;HH:mm:ss');
var dif = dateFin.getDate() - dateDebut.getDate();
//do somthing with labels and data
return {
labels : labels,
data : data
};
}
}
};
});
So I get as an error labels is not defined, and if I comment it I get this error:
Cannot read property 'getDate' of undefined
which means the code does not recognize dateFin nor dateDebut.
Is there another way to pass the scope to the service, or am I missing something in my current code?
If I understand the question correctly you need to somehow reevalute values when either dateDebut or dateFin properties of scope change.
To achive this you can use $watch or $watchGroup methods of the scope. Simplified Demo.
For example
app.controller('graphCtrl', function($scope, graphDetails) {
// start watcher
$scope.$watchGroup(['dateDebut', 'dateFin'], function(args) {
var debut = args[0], fin = args[1];
angular.extend($scope, graphDetails.mettreAJour(debut, fin))
})
})
Most probably this is because you are trying to return labels from mettreAJour but it is not aware of labels & data .
Hope this below snippet will be useful.
app.factory('graphDetails', function($filter, $http) {
var _graphDeatilsObject = {};
_graphDeatilsObject.labels = [];
_graphDeatilsObject.data = [
[]
];
_graphDeatilsObject.mettreAJour = function(dateDebut, dateFin) {
// Rest of the code
}
return _graphDeatilsObject;
});
Also take a look at inline array annotation which is required if you are minifying the code
Related
This is a relatively simple piece of code that calls a service and returns some data. I need to set the $scope with the result of the data. Is there an easy way to set this data to the scope without resorting to to binding the scope to the function in the then clause?
Angular Code
(function () {
var app = angular.module('reports', []);
var reportService = function($http, $q) {
var service = {};
service.getMenuData = function() {
var deffered = $q.defer();
$http.get('/Report/MenuData').success(function(data) {
deffered.resolve(data);
}).error(function(data) {
deferred.reject("Error getting data");
});
return deffered.promise;
}
return service;
};
reportService.$inject = ['$http', '$q'];
app.factory('reportService', reportService);
var reportMenuController =
function ($scope, $http, reportService) {
$scope.getMenuData = function(e) {
reportService.getMenuData().then(function(data) {
// Need to set the $scope in here
// However, the '$scope' is out of scope
});
}
};
reportMenuController.$inject = ['$scope', '$http', 'reportService'];
app.controller('ReportMenuController', reportMenuController);
})();
Markup
<div>
<div ng-controller="ReportMenuController">
<button ng-click="getMenuData()">Load Data</button>
</div>
</div>
There is absolutely no problem to set the $scope from within the function passed to then(). The variable is available from the enclosing scope and you can set your menu data to one of its fields.
By the way: You should consider to use then() instead of success() for your http request. The code looks much nicer because then() returns a promise:
service.getMenuData = function() {
return $http.get('/Report/MenuData').then(function(response) {
return response.data;
}, function(response) {
deferred.reject("Error getting data");
});
}
success() is deprecated by now.
I didn't notice the small detail missing in the plunker where my code was different.
(function () {
...
var reportMenuController =
function ($scope, $http, reportService) {
$scope.getMenuData = getMenuData;
function getMenuData(e) {
reportService.getMenuData().then(function(data) {
// Now I have access to $scope
});
}
};
...
})();
Notice the changes to the two lines as below:
$scope.getMenuData = getMenuData;
function getMenuData(e) {
This also begs a small question which is, "Why is it okay to set getMenuData to the $scope before it is declared?
I have this factory
DatosFactory.js
(function() {
'use strict';
angular.module('InmoManager')
.factory('DatosFactory', function($http, $location) {
var datos = {
propiedadesFiltradas: []
}
...
datos.getPropiedadesFiltradas = function(){
return datos.propiedadesFiltradas;
}
datos.setPropiedadesFiltradas = function(data){
datos.propiedadesFiltradas.length = 0;
datos.propiedadesFiltradas.push(data);
}
return datos;
})
})();
This controller
SidebarController.js
(function() {
'use strict';
angular.module('InmoManager')
.controller('SidebarController', function($http, $scope, DatosFactory) {
var sidebarCtrl = this;
sidebarCtrl.toggleSidebar = function(){
$('#wrapper').toggleClass('toggled');
}
sidebarCtrl.propiedades = DatosFactory.getPropiedadesFiltradas();
});
})();
Making this work
pageSidebar.html
<li class="item" ng-repeat="propiedad in sidebarCtrl.propiedades[0] | orderBy:'titulo'">
This works great!, but, i want to change this:
ng-repeat="propiedad in sidebarCtrl.propiedades[0]"
to
ng-repeat="propiedad in sidebarCtrl.propiedades"
and this
datos.setPropiedadesFiltradas = function(data){
datos.propiedadesFiltradas.length = 0;
datos.propiedadesFiltradas.push(data);
}
to
datos.setPropiedadesFiltradas = function(data){
datos.propiedadesFiltradas = data;
}
But when i make this, the variable sidebarCtrl.propiedades get's undefined (doesn't update when i call datos.setPropiedadesFiltradas()
When you reassign datos.propiedadesFiltradas = data; you break the object reference, and as the result datos.propiedadesFiltradas is no longer points to the object Angular set up bindings to.
On the other hand, when you push object reference stays untouched, and Angular's changes tracking engine can detect changes and rerender view.
One more thing. I would recommend you to use ngClass directive instead of $('#wrapper').toggleClass('toggled');:
sidebarCtrl.toggleSidebar = function() {
sidebarCtrl.toggle = !sidebarCtrl.toggle;
}
and in HTML
<div id="wrapper" ng-class={toggled: toggle}>...</div>
Because of dfsq answer i assume that all i wanted it's not possible, but i found this "partial" solution
With this in the factory:
datos.setPropiedadesFiltradas = function(data){
datos.propiedadesFiltradas.length = 0;
datos.propiedadesFiltradas.push.apply(datos.propiedadesFiltradas,data);
}
i could change this:
ng-repeat="propiedad in sidebarCtrl.propiedades[0]"
to
ng-repeat="propiedad in sidebarCtrl.propiedades"
I am trying to use a service instead of a factory to perform two way data binding, I have seen may tutorials online on using a factory but I prefer using a service instead of a factory, so far I have come up with the following pattern to perform the binding but, on doing console.log() i found that the data from the service doesn't bind to the data on the controller.
controller: ['$scope','LeadsService','ServiceVehicleModels', function($scope, LeadsService, ServiceVehicleModels) {
$scope.colorList = ServiceVehicleModels.colors;
$scope.yearsList = ServiceVehicleModels.years;
$scope.$watch(function() { return ServiceVehicleModels.colors }, function(data) {
return $scope.colorList = data.colors;
},true);
};
Here is my service code
exports.service = function() {
this.colors = [];
this.years = [];
this.trims = [];
var scope = this;
this.setColors = function(colorsArr) {
scope.colors = colorsArr;
};
this.setYears = function(yearsArr) {
scope.years = yearsArr;
};
};
What changes do i need to make to make the data binding work?
You have issue in watcher. This should solve bindings:
$scope.$watch(function() { return ServiceVehicleModels.colors }, function(colors) {
return $scope.colorList = colors;
},true);
I have multiple functions inside of my factory and I cannot call my saveCharacter function from a button click using ng-click. The getCharacters function works just fine. Sorry in advance for the repost, I have gone over many different examples and cannot solve my particular issue with those. I can see my functions when I log the xmlService, but i'm not sure why it won't call to it. I was trying to return a post to a PHP file in saveCharacter, but changed to a string return to try to test to see what my issue was.
Thanks again for any help.
(function(){
var app = angular.module('arena', []);
app.factory('xmlService', function($http){
var factory = {};
factory.getCharacter = function getCharacter(){
return $http.get('xml/characterTemplate.xml');
};
factory.saveCharacter = function saveCharacter(){
return "hello";
//return $http.post('php/crud.php');
};
return factory;
});
app.controller('FighterController', ['$scope','xmlService', function($scope, xmlService){
this.fighterList = fighterList;
$scope.saveFighter = function saveFighter(){
console.log(xmlService);
xmlService.saveCharacter.success(function(data){
console.log(data);
});
}
function loadFighters(){
xmlService.getCharacter().success(function(data){
var x2js = new X2JS();
var charactersList = x2js.xml_str2json(data);
for(var i = 0; i < charactersList.characters.character.length; i++)
{
var currentFighter = charactersList.characters.character[i];
fighterList.push(currentFighter);
}
$scope.FighterController = charactersList;
});
}
loadFighters();
}]);
var fighterList = [
];
})();
Other questions I had while writing my first Angular app, what is the point of the code:
$scope.FighterController = charactersList;
does that allow me to access the returned data on the view side? do I have to reset the scope in my saveFighter function to cause my button to work?
Am I setting the dependencies correctly for my app.controller, and is that dependency injection?
Thank you all, and any comments on how my code is setup are greatly appreciated!
You haven't really explained what you did to fix this issue, so I'll explain it.
Here, you are trying to call xmlService.saveCharacter.success():
xmlService.saveCharacter.success(function(data){
console.log(data);
});
But xmlService.saveCharacter is a function. It has no success property; success is undefined. So this gives the error you were seeing.
You need to call xmlService.saveCharacter():
xmlService.saveCharacter().success(function(data){
console.log(data);
});
But this is still a problem because the saveCharacter() function returns the string "hello". This string doesn't have a success property. Yet again success is undefined, so now that causes the same error.
To fix that error, you just need to remove the return "hello"; and uncomment the code you had commented out:
factory.saveCharacter = function saveCharacter(){
return $http.post('php/crud.php');
};
Fixing those two problems should remedy your issue.
You are missing invoking a function with () change code to:
$scope.saveFighter = function saveFighter(){
console.log(xmlService);
xmlService.saveCharacter().success(function(data){
// ----------------------^
console.log(data);
});
}
$scope.FighterController = charactersList;is assigning data of characterList to scope variable and scope variable are accessible in html scope is like a bridge between controller and views.
I recommend you to start reading angularjs
I adjusted my factory to this structure and now I can call my functions.
app.factory('xmlService', function($http){
var factory = {
getCharacter: function(){
return $http.get('xml/characterTemplate.xml');
},
saveCharacter:function(){
console.log('hello?');
return $http.post('php/crud.php');
}
};
return factory;
});
in my controller
$scope.saveFighter = function(){
console.log(xmlService);
xmlService.saveCharacter().success(function(data){
console.log(data);
});
}
function loadFighters(){
xmlService.getCharacter().success(function(data){
var x2js = new X2JS();
var charactersList = x2js.xml_str2json(data);
for(var i = 0; i < charactersList.characters.character.length; i++)
{
var currentFighter = charactersList.characters.character[i];
fighterList.push(currentFighter);
}
$scope.FighterController = charactersList;
});
}
loadFighters();
I have 2 services and would like to update a variable in the 1st service from the 2nd service.
In a controller, I am setting a scope variable to the getter of the 1st service.
The problem is, the view attached to the controller doesn't update when the service variable changes UNLESS I use angular.extend/copy. It seems like I should just be able to set selectedBuilding below without having to use extend/copy. Am I doing something wrong, or is this how you have to do it?
controller
app.controller('SelectedBuildingCtrl', function($scope, BuildingsService) {
$scope.building = BuildingsService.getSelectedBuilding();
});
service 1
app.factory('BuildingsService', function() {
var buildingsList = [];
var selectedBuilding = {};
// buildingsList populated up here
...
var setSelectedBuilding = function(buildingId) {
angular.extend(selectedBuilding, _.find(
buildingsList, {'building_id': buildingId})
);
};
var getSelectedBuilding = function() {
return selectedBuilding;
};
...
return {
setSelectedBuilding: setSelectedBuilding,
getSelectedBuilding: getSelectedBuilding
}
});
service 2
app.factory('AnotherService', function(BuildingsService) {
...
// something happens, gives me a building id
BuildingsService.setSelectedBuilding(building_id);
...
});
Thanks in advance!
When you execute this code:
$scope.building = BuildingsService.getSelectedBuilding();
$scope.building is copied a reference to the same object in memory as your service's selectedBuilding. When you assign another object to selectedBuilding, the $scope.building still references to the old object. That's why the view is not updated and you have to use angular.copy/extend.
You could try the following solution to avoid this problem if you need to assign new objects to your selectedBuilding:
app.factory('BuildingsService', function() {
var buildingsList = [];
var building = { //create another object to **hang** the reference
selectedBuilding : {}
}
// buildingsList populated up here
...
var setSelectedBuilding = function(buildingId) {
//just assign a new object to building.selectedBuilding
};
var getSelectedBuilding = function() {
return building; //return the building instead of selectedBuilding
};
...
return {
setSelectedBuilding: setSelectedBuilding,
getSelectedBuilding: getSelectedBuilding
}
});
With this solution, you have to update your views to replace $scope.building bindings to $scope.building.selectedBuilding.
In my opinion, I will stick to angular.copy/extend to avoid this unnecessary complexity.
I dont believe you need an extend in your service. You should be able to watch the service directly and respond to the changes:
app.controller('SelectedBuildingCtrl', function($scope, BuildingsService) {
// first function is evaluated on every $digest cycle
$scope.$watch(function(scope){
return BuildingsService.getSelectedBuilding();
// second function is a callback that provides the changes
}, function(newVal, oldVal, scope) {
scope.building = newVal;
}
});
More on $watch: https://code.angularjs.org/1.2.16/docs/api/ng/type/$rootScope.Scope