In a nutshell, I'm using an AngularJS service in both a directive and a controller. When a change to a service value is performed in the controller that value does not update in the directive. What am I missing here?
I tried adding a watch on the directive's scope.value but it's not actually changing.
var myApp = angular.module('myApp', [])
.directive('myDirective', function (myService) {
return {
controller: function ($scope, myService) {
angular.extend($scope, myService);
},
scope: {},
template: 'Directive: <select ng-model="absValue" ng-options="r for r in absRange()"></select> {{absValue}}'
};
})
.service('myService', function () {
return {
absRange: function () {
var value = this.value;
return range(2 * this.value + 1).map(function (r) { return r - (value + 1); });
},
absValue: 0,
value: 3
};
});
function myController($scope, myService) {
this.range = range(5);
angular.extend(this, myService);
}
Here's the plunk: http://plnkr.co/edit/oAlzJ5gNQ4uXaJV2Bh9z?p=preview
Related
I have an angular module with the following code:
angular.module('sampleModule', [])
.service('myService', [function () {
this.showAlert = function () {
alert("Hello");
};
this.sum = function (a, b) {
return a + b;
};
}])
.controller('SampleCtrl', ['myService', '$scope', function ($scope, myService) {
this.doSum = function () {
var result = myService.sum(1, 2);
alert(result);
};
}]);
When I invoke doSum I get:
TypeError: myService.sum is not a function
Any ideas? Thanks!
Your controller DI is wrong- note the order of the arguments:
.controller('SampleCtrl', ['$scope', 'myService', function ($scope, myService) {
this.doSum = function () {
var result = myService.sum(1, 2);
alert(result);
};
}]);
Issue with sequencing of injections are not proper. $scope should come before myService.
Correct code:
.controller('SampleCtrl', ['$scope', 'myService', function ($scope, myService) {
this.doSum = function () {
var result = myService.sum(1, 2);
alert(result);
};
}]);
This may be very basic but I'm new to angular and am stumped. I have 2 views that need to access the same user input data from a form. Each view has it's own controller.
Here's where I'm at:
JAVASCRIPT
.config(function($routeProvider) {
$routeProvider
.when('/view1', {
templateUrl : 'view1.html',
controller: 'ctrl1'
})
.when('/view2', {
templateUrl : 'view2.html',
controller : 'ctrl2'
})
})
//SERVICE TO HOLD DATA
.service('Data', function() {
return {};
})
//CONTROLLER 1
.controller('ctrl1', ['$scope', 'Data', function($scope, Data) {
$scope.data = Data;
var $scope.initValue = function() {
$scope.data.inputA = 0; //number
$scope.data.inputB = 0; //number
}
var $scope.onSubmit = function() {
$scope.data.result = $scope.data.inputA + $scope.data.inputB;
}
}])
//CONTROLLER 2
.controller('ctrl2', ['$scope', 'Data', function($scope, Data) {
$scope.data = Data;
}
}])
HTML (view2.html)
<p>Result is {{data.result}}</p>
This displays nothing, I'm thinking it's because the service or controller resets the values when changing views? Am I just totally wrong for using a service to do this?
You have to update the data in the service so that it can be used in another controller:
// define a var container in the service
// you can make it neat by creatin a getter and setter
.service('Data', function() {
var value = null;
var setValue = function(val) {
this.value = val;
};
var getValue = function() {
return this.value;
};
return {
value: value,
setValue: setValue,
getValue: getValue,
};
}
Then in controller 1 you can set the value in the service like so:
//CONTROLLER 1
.controller('ctrl1', ['$scope', 'Data', function($scope, Data) {
$scope.inputA = 0;
$scope.inputB = 0;
$scope.onSubmit = function() {
$scope.result = $scope.inputA + $scope.inputB;
Data.setValue($scope.result);
}
}])
And in controller 2 you can use the value like so:
//CONTROLLER 2
.controller('ctrl2', ['$scope', 'Data', function($scope, Data) {
$scope.value = Data.getValue();
}])
Hope this will help.
I already have seem other topics with this kind of issue, but no one could help me... So here is my issue:
I have a navbar with a button for search, this buttons makes and get request from a webservice and returns a json object which must be apply to fill an table list. The problem is, my button and my table are in separated controllers, and it does work like I expected.
var app = angular.module('clientRest', []).controller('lista', ['$scope', 'loadLista', function($scope, loadLista) {
$scope.contatos = loadLista.getContatos();
}]).controller('pesquisa', ['$scope', '$http', 'loadLista', function($scope, $http, loadLista) {
$scope.listar = function() {
$http.get("http://localhost/wsRest/index.php/contato").success(function(response) {
loadLista.setContatos(response);
});
};
}]).service('loadLista', function() {
var contatos = [];
return {
getContatos: function() {
return contatos;
},
setContatos: function(c) {
contatos = c;
}
};
});
My code...
When I call listar() from pesquisa controller I need to send received data to $scope.contatos from lista controller to make my ng-repeat work, everything with a single click.
How can I do it?
Thanks everyone
Better to use a service to share data between two controllers / modules as this might be the best approach. You can refer the code segment given below to understand the concept.
angular.module('app.A', [])
.service('ServiceA', function() {
this.getValue = function() {
return this.myValue;
};
this.setValue = function(newValue) {
this.myValue = newValue;
}
});
angular.module('app.B', ['app.A'])
.service('ServiceB', function(ServiceA) {
this.getValue = function() {
return ServiceA.getValue();
};
this.setValue = function() {
ServiceA.setValue('New value');
}
});
In order to trigger the data receipt event, you may use
Broadcast / emit messages - with #broadcast / #emit
An angular promise with a call back
Controller initiation function to reload the previously read information from a service
.controller('MyController', function($scope, ServiceA) {
$scope.init = function() {
$scope.myValue = ServiceA.getValue();
};
// Call the function to initialize during Controller instantiation
$scope.init();
});
Use $rootScope.$emit to emit a change event when setting the variable and use $on to get the value in the lista controller. I used customListAr here just to demostrate a button click. Does this help?
var app = angular.module('clientRest', [])
.controller('lista', ['$scope', 'loadLista', '$rootScope',
function($scope, loadLista, $rootScope) {
console.log(loadLista);
$scope.contatos = loadLista.getContatos();
$rootScope.$on('change', function() {
$scope.contatos = loadLista.getContatos();
});
}
])
.controller('pesquisa', ['$scope', '$http', 'loadLista',
function($scope, $http, loadLista) {
$scope.listar = function() {
$http.get("http://localhost/wsRest/index.php/contato").success(function(response) {
loadLista.setContatos(response);
});
};
$scope.customListAr = function() {
loadLista.setContatos(["item 1" , "item 2", "item 3"]);
}
}
])
.service('loadLista', ['$rootScope',
function($rootScope) {
var contatos = [];
return {
getContatos: function() {
return contatos;
},
setContatos: function(c) {
contatos = c;
$rootScope.$emit('change');
}
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="clientRest">
<div ng-controller="lista">
<ul>
<li ng-repeat="a in contatos">{{a}}</li>
</ul>
</div>
<div ng-controller="pesquisa">
<button ng-click="customListAr()">Click Me</button>
</div>
</div>
Your problem is that when you do $scope.contatos = loadLista.getContatos(); you are setting a static value, and angular is unable to effectively create a watcher for that object because your setContatos method is creating a new object each time. To get around this, have the controller's scope hold a reference to the parent object and then it will automatically have a watcher on that object.
var app = angular.module('clientRest', [])
.controller('lista', ['$scope', 'loadLista', function($scope, loadLista) {
$scope.contatos = loadLista.contatos;
}])
.controller('pesquisa', ['$scope', '$http', 'loadLista', function($scope, $http, loadLista) {
$scope.listar = function() {
$http.get("http://localhost/wsRest/index.php/contato"
).success(function (response) {
loadLista.contatos.data = response;
});
};
}])
.service('loadLista', function() {
var lista = {
contatos: {},
};
return lista;
});
// view:
<ul>
<li ng-repeat="contato in contatos.data">
{{ contato }}
</li>
</ul>
I am trying to reuse a few bigger functions over 3 controllers in Angular JS. I don't want to pin the functions to my root scope as I want to keep it clear of functions which will be used only 3 times within those 3 controllers.
angular.module('adminModule', ['adminDependency'])
.controller('ctrl1', ['$scope', 'details', function ($scope, details) {
// use functions
}])
.controller('ctrl2', ['$scope', 'details', function ($scope, details) {
// use functions
}])
.controller('ctrl3', ['$scope', 'details', function ($scope, details) {
// use functions
}])
Can you tell me how i can achieve that without writing my functions into the root scope?
Tried it inside a factory but calling AdminModule.toLevelKey() wont work...
.factory('AdminModule',
[ '$resource', 'serviceURL', function ($resource, serviceURL) {
return $resource(serviceURL + 'class/:id', {
id : '#id'
}, {
getClasses : {
method : 'GET',
url : serviceURL + 'extended/class',
isArray : true
},
toLevelKey : function (value) {
var return_key = parseInt(Math.floor(value / 3));
var return_level = value % 3;
return { level : return_level + 1, levelTranslationKey : return_key + 1 };
},
fromLevelKey : function (level, key) {
if (angular.isDefined(level)) {
var value = (key - 1) * 3 + (level - 1);
return value;
} else {
return null;
}
}
}
);
} ]);
This can be done by a service:
.service('myService', function(){
return {
fn: function(){
// do what you want
}
}
});
usage:
.controller('ctrl2', ['$scope', 'details', 'myService',
function ($scope, details, myService) {
// use functions
myService.fn();
}])
In accordance with the above comment of David FariƱa: "Are there even more options?".
Except executing, you also can pass data from one controller to another and broadcast event, when it happens.
SharedService:
angular.module("yourAppName", []).factory("mySharedService", function($rootScope){
var mySharedService = {};
mySharedService.values = {};
mySharedService.setValues = function(params){
mySharedService.values = params;
$rootScope.$broadcast('dataPassed');
}
return mySharedService;
});
FirstController:
function FirstCtrl($scope, mySharedService) {
$scope.passDataInSharedSevice = function(params){
mySharedService.setValues(params);
}
}
SecondController:
function SecondController($scope, mySharedService) {
$scope.$on('dataPassed', function () {
$scope.newItems = mySharedService.values;
});
}
I've got directive and service in my app (declared in separate files):
Service:
(function(){
angular.module('core', [])
.factory('api', function() {
return {
serviceField: 100
};
})
})();
Directive:
(function(){
angular.module('ui', ['core'])
.directive('apiFieldWatcher', function (api) {
return {
restrict: 'E',
replace: true,
scope: true,
template: '<div>+{{apiField}}+</div>',
controller: function($scope) {
$scope.apiField = 0;
},
link: function (scope) {
scope.$watch(function(){return api.serviceField}, function(apiFld){
scope.apiField = apiFld;
});
}
}
});
})();
And in another separate file I have native model:
function Model() { this.fld = 0; }
Model.prototype.setFld = function(a) { this.fld = a; }
Model.prototype.getFld = function() { return this.fld; }
How can I bind (two way) my native this.fld field to value in my AngularJS service?
The solution is in using this code:
Model.prototype.setFld = function(a) {
this.fld = a;
injector.invoke(['$rootScope', 'api', function($rootScope, api){
api.setField(a);
$rootScope.$digest();
}]);
};
Model.prototype.getFldFromApi = function() {
var self = this;
injector.invoke(['api', function(api){
self.fld = api.getField();
}]);
};
http://plnkr.co/edit/nitAVuOtzGsdJ49H4uyl
i think it's bad idea to use $digest on $rootScope, so we can maybe use
var scope = angular.element( elementObject ).scope();
to get needed scope and call $digest for it