AngularJS using service variable with ng-if - javascript

I want to be able to determine which directive is displayed based on a variable from a shared service. This is what I have so far.
main.html
<character-select ng-if="stateChangeService.playerState === 'characterSelect'"></character-select>
<fight-display ng-if="stateChangeService.playerState === 'fight'"></fight-display>
service
angular
.module('outerZone')
.service('stateChangeService', stateChangeService);
function stateChangeService() {
var vm = this;
vm.playerState = 'characterSelect';
}
Currently when I load the page, nothing displays. The service is injected into both the character-select directive and the fight-display directive as well as the MainController. Any ideas?

There is no way your view knows anything about your service. You should assign your service to context.
angular.module('outerZone').controller('MainController', MainController)
MainController.$inject = ['stateChangeService'];
function MainController(stateChangeService){
var vm = this;
vm.stateChangeService = stateChangeService;
}
<body ng-controller="MainController as mc">
<character-select ng-if="mc.stateChangeService.playerState === 'characterSelect'"></character-select>
<fight-display ng-if="mc.stateChangeService.playerState === 'fight'"></fight-display>
</body>

View in Angular can access variables in current $scope or parent scopes.
so you have to assign service to current $scope or $rootScope.
(function() {
angular.module('app')
// your service
.service('myService', myServiceFunction);
function myServiceFunction(){
this.data = function(){ return true; };
};
// your controller
.controller('myController', myControllerFunction);
// inject $scope and service
myControllerFunction.$inject=['$scope','myService'];
function myControllerFunction($scope, myService){
//attatch service to current scope
$scope.myService = myService;
};
})();
<div ng-app='app' ng-controller="myController">
{{myService.data()}}
</div>

You can only use those variable on view/HTML which are bounded to $scope or this(while using controllerAs pattern). You should expose your service/factory/constant on view, OR rather better way would write a getter for accessing particular variable from service. Its best practice to expose relevant matter from service. There after you could call that getter from view like getPlayerState()
HTML
<character-select ng-if="getPlayerState() === 'characterSelect'"></character-select>
<fight-display ng-if="getPlayerState() === 'fight'"></fight-display>
Code
app.controller('myCtrl', function(stateChangeService, $scope){
$scope.getPlayerState = function(){
return stateChangeService.playerState;
}
})

Related

How do I reach $rootScope from a component?

I am very new to AngularJS/Ionic/Cordova programming and am trying to handle the visibility of a component using a global variable, so it can be hidden or shown from other components. I am creating the variable when calling the run function, assigning it to $rootScope.
app.run(function($rootScope, $ionicPlatform) {
$ionicPlatform.ready(function() {
// Some Ionic/Cordova stuff...
// My global variable.
$rootScope.visible = true;
});
})
My component is:
function MyComponentController($rootScope, $scope) {
var self = this;
self.visible = $rootScope.visible;
alert(self.visible);
}
angular.module('myapp')
.component('myComponent', {
templateUrl: 'my-component.template.html',
controller: MyComponentController
});
And the template:
<div ng-if="$ctrl.visible">
<!-- ... -->
</div>
However the alert message always shows "undefined". What am I missing?
$rootScope.visible isn't watched when being assigned as self.visible = $rootScope.visible. And it is undefined at the moment when component controller is instantiated.
It can be
function MyComponentController($rootScope, $scope) {
var self = this;
$scope.$watch(function () { return $rootScope.visible }, function (val) {
self.visible = val;
});
}
By the way, it is likely available as $scope.$parent.visible and can be bound in template as ng-if="$parent.visible", but this is antipattern that is strongly discouraged.
There may be better approaches:
top-level AppController and <my-component ng-if="visible">, so the component doesn't have to control its own visibility
broadcasting it with scope events, $rootScope.$broadcast('visibility:myComponent')
using a service as event bus (that's where RxJS may be helpful)
using a router to control the visibility of views, possibly with route/state resolver (this is the best way)
How about change self to $scope like this:
function MyComponentController($rootScope, $scope) {
$scope.visible = $rootScope.visible;
alert($scope.visible);
}

How to access $scope from one controller to another in angular

I have these controller codes from different js files.
NLGoalsCtrl.js
angular.module('mysite').controller('NLGoalsCtrl', function ($scope) {
$scope.goals_selected = [];
});
NLSessionsCtrl.js
angular.module('mysite').controller('NLSessionsCtrl', function ($scope) {
//access $scope.goals_selected here
});
I need to be able to access the $scope.goals_selected from the NLSessionsCtrl. How do I do this? Thanks guys.
Use a factory/service to store the goals which will be responsible for sharing data among the controllers.
myApp.factory('myService', [function() {
var goals = {};
return {
getGoals: function() {
return goals
},
setGoals: function(op) {
goals = op;
},
}
}])
.controller('NLGoalsCtrl', [function($scope, myService) {
$scope.goals_selected = {};
//Update goals_selected
myService.setGoals($scope.goals_selected );
}])
.controller('NLSessionsCtrl', [function($scope, myService) {
//Fetch
$scope.goals_selected = myService.getGoals();
}]);
$scope is an "object" that "binds" to DOM element where you apply controller. So the context of $scope remains inside the controller.
If you want to access the variables in 2 controllers , try to share it via a service/Factory or $rootScope
Here is a sample App
the one that you want to access in any controller assign it to the $rootScope.
and access it in any controller you want.

AngularJS pass Javascript object to controller

I'm trying to pass a Javascript object into my AngularJS controller and having no luck.
I've tried passing it into an init function:
<div ng-controller="GalleryController" ng-init="init(JSOBJ)">
And on my controller side:
$scope.init = function(_JSOBJ)
{
$scope.externalObj = _JSOBJ;
console.log("My Object.attribute : " + _JSOBJ.attribute );
};
Though the above doesn't seem to work.
Alternatively, I've tried pulling the attribute from the AngularJS controller that I am interested in for use in an inline <script> tag:
var JSOBJ.parameter = $('[ng-controller="GalleryController"]').scope().attribute ;
console.log("My Object.parameter: " + JSOBJ.attribute );
Can anyone tell me: what is the best practice regarding this?
I don't have the option to rewrite the plain Javascript object as it is part of a 3rd party library.
Let me know if I need to provide further clarification and thanks in advance for any guidance!
-- JohnDoe
Try setting it as a value:
angular.module('MyApp')
.value('JSOBJ', JSOBJ);
Then inject it into your controller:
angular.module('MyApp')
.controller('GalleryController', ['JSOBJ', function (JSOBJ) { ... }]);
Since your object is a part of third-party library you have to wrap it app in something angular.
Your options are:
if it is jquery pluging init'ed for a DOM node etc you can create a directive
example
myApp.directive('myplugin', function myPlanetDirectiveFactory() {
return {
restrict: 'E',
scope: {},
link: function($scope, $element) { $($element).myplugin() }
}
});
if it is something you need to init you can use factory or service
example
myApp.service(function() {
var obj = window.MyLib()
return {
do: function() { obj.do() }
}
})
if it is plain javascript object you can use value
example
myApp.value('planet', { name : 'Pluto' } );
if it is constant ( number, string , etc) you can use constant
example
myApp.constant('planetName', 'Greasy Giant');
Reference to this doc page: https://docs.angularjs.org/guide/providers
Also I strongly encourage you to read answer to this question: "Thinking in AngularJS" if I have a jQuery background?
If you have JSOBJ accessible via global scope (via window), than you can access it through window directly as in plain JavaScript.
<script>
...
window.JSOBJ = {x:1};
...
</script>
Option 1.
<script>
angular.module('app',[]).controller('ctrl', ['$scope', function($scope) {
$scope.someObject = window.JSOBJ;
}]);
</script>
However it makes the controller code not testable. Therefore $window service may be used
Option 2.
<script>
angular.module('app',[]).controller('ctrl', ['$scope', '$window', function($scope, $window) {
$scope.someObject = $window.JSOBJ;
}]);
</script>
If you want to make some abstraction layer to make your controller agnostic for the source from where you get the object, it is recommended to define service which is responsible for fetching value and then inject it to your controllers, directives, etc.
Option 3.
<script>
angular.module('app',[])
.service('myService', function() {
var JSOBJ = ...; // get JSOBJ from anywhere: localStorage, AJAX, window, etc.
this.getObj = function() {
return JSOBJ;
}
})
.controller('ctrl', ['$scope', 'myService', function($scope, myService) {
$scope.someObject = myService.getObj();
}]);
</script>
Besides of it, for simple values you can define constant or value that may be injected to any controller, service, etc.
Option 4.
<script>
angular.module('app',[]).
value('JSOBJ', JSOBJ).
controller('ctrl', ['$scope', 'JSOBJ', function($scope, JSOBJ) {
$scope.someObject = JSOBJ;
}]);
</script>

Create a factory/service for an object that changes dynamically, and that could be passed on to a controller

I have an object that updates via socket.io.
var my_dynobj;
socket.on('update dynobj', function(dynobj){
my_dynobj = dynobj;
});
I want to have it in my angular app as a factory or a service that could be injected as a dependency.
In there I will want to attach my_object to a $scope so that it can be plugged into HTML {{my_object}} and will update whenever it is itself updated in its own factory definition via socket. socket.on('update object', …
But I can't figure out a way to make that happen.
angular.module('app', [])
.factory('dynobj_factory', [
function(){
var my_dynobj;
socket.on('update dynobj', function(dynobj){
my_dynobj = dynobj;
});
return {what?}
}])
.controller('ctrl', ['$scope', 'dynobj_factory',
function($scope, dynobj) {
$scope.my_dynobj = dynobj.what?
}])
<div>{{my_dynobj}}</div>
For
return {/* what ? */}
You'll need to return something like;
return {dynamicObj: my_dynobj}
And that will do the trick. Any changes to my_dynobj will reflect through all directives, controllers, factories it is injected into.

Dependency Injection of functions with Factories (AngularJS)

I have a few functions that are used in different controllers, and rather than copy and paste it multiple times into the controllers, I want to pull it out and place it in a factory.
However, when I try to call the function in the HTML via Angular {{expressions}} it doesn't work.
Instead, I've made functions inside each of the controllers' $scopes that call the factory's functions so the DOM can read the expressions--but that seems redundant. Is there a way to fix this so I can simply call functions from the factory?
Here was what I originally had tried:
index.html:
<div ng-controller="MyController">
The rating of this item is: {{MakeGraph.getRating(whichItem)}}<br />
The votes of this item is: {{MakeGraph.getVotes(whichItem)}}
</div>
MyController.js:
controllers.controller('MyController', ['$scope', 'MakeGraph', '$routeParams', function($scope, MakeGraph, $routeParams){
$scope.whichItem = $routeParams.itemId;
//no other reference to MakeGraph here
}])
factory.js:
app.factory('MakeGraph', function(){
var service = {};
service.getRating = function(itemNo){
...
return ...;
};
service.getVotes = function(itemNo){
...
return ...;
};
return service;
});
Instead, I can only get it to work when I change MyController to have something like this:
$scope.getRating = function(){
return MakeGraph.getRating(itemNo);
};
Can I fix this so I don't have to call the function inside the $scope? Thanks.
add $scope.MakeGraph = MakeGraph to your controller
controllers.controller('MyController', ['$scope', 'MakeGraph', '$routeParams', function($scope, MakeGraph, $routeParams){
$scope.whichItem = $routeParams.itemId;
$scope.MakeGraph = MakeGraph
}])
then you could access MakeGraph from your view
example:
<a ng-click="MakeGraph.getVotes(12)"> Click me </a>
note that if you return promises in your service, you will probably still need to wrap then in your controller in order to properly handle the .success / .then / .error ... events

Categories

Resources