Pass toggling Boolean from service to controller - angularjs

I want to show/hide an element based on the Boolean value that is changing in my service. I want the change of this Boolean to happen in my service so multiple controllers can access the true or false value, but I am having trouble returning this value to one or more controllers. Currently I'm only able to pass one value which is false, although the value does show it's changing in my service. Here is an example of my controller...
angular.module('myApp')
.service('ThisService', function(){
function toggleDisplay(){
return displayElement = !displayElement;
}
});
.controller('ThisCtrl', function (thisService, $scope) {
function init(){
$scope.displayElement = ThisService.toggleDisplay();
}
$scope.toggleElement = function(){
$scope.displayElement = ThisService.toggleDisplay();
}
init();
});
My HTML...
<div ng-show="displayElement">Show hide me</div>
<button ng-click='toggleElement()'></button>
Can you please tell me how to return the true/false value to my controller correctly?

You can use a value and then toggle that in your service. However, your service definition is not valid, you have a semi-colon in the middle of your chain of modules and you define your service with the name "ThisService", but then you try to reference it in your controller as "thisService" (it's case sensitive).
JS:
angular.module("myApp", [])
.value("DisplayElement", { value: true })
.service("ThisService", function(DisplayElement) {
this.toggleDisplay = function() {
return DisplayElement.value = !DisplayElement.value;
}
})
.controller("ThisCtrl", function(ThisService, $scope) {
function init() {
$scope.displayElement = ThisService.toggleDisplay();
}
$scope.toggleElement = function() {
$scope.displayElement = ThisService.toggleDisplay();
}
init();
});
HTML:
<div ng-app="myApp">
<div ng-controller="ThisCtrl">
<div ng-show="displayElement">Show hide me</div>
<button ng-click="toggleElement()">Toggle Display</button>
</div>
</div>
jsFiddle
You could even eliminate the service and just access the value directly in your controller (you'd have to inject it first).

Related

How can I monitor the change of a variable in my factory

I have a very simple preloading screen script that i need to get from several controllers. However I can't get it to update automatically the variable and I don't know what I'm doing wrong.
Factory:
myApp.factory("preloader", function(){
var preload = {};
preload.loaded = true;
preload.turnOn = function () {
preload.loaded = true;
console.log('on');
}
preload.turnOff = function () {
preload.loaded = false;
console.log('off');
}
preload.getState = function() {
return preload.loaded;
}
return preload;
});
Controller
mazda.controller('preloadingHome', ['$scope', "preloader", function($scope, preload) {
$scope.users = false;
$scope.showPreload = preload.getState();
console.log(preload.loaded);
$scope.turnOn = function(){
preload.turnOn();
}
$scope.turnOff = function(){
preload.turnOff();
}
//
// $scope.state = preload.state;
// preload.turnOff();
}]);
View
<body data-ng-controller="preloadingHome">
<div>aaa: {{ showPreload }}</div>
<div>b: {{users}} </div>
<input type="checkbox" ng-model="users" />
<input type="checkbox" ng-change="turnOff()" ng-model="pene" />
<input type="checkbox" ng-change="turnOn()" ng-model="pene2" />
<!-- script src="js/scripts.min.js"></script -->
<script src="js/scripts.min.js"></script>
<script src="js/bundle.js"></script>
</body>
My problem is: The {{ showPreload }} variable load on the view always stays true no matter how I change it.
I think it is important to point out that the reason this does not work is because your getState() method returns by value, not by reference.
When your controller instantiates, it sets the showPreloaded variable to the value of the preloaded.loaded object member, which is true. Every time you change the state after that, you are updating the object member in your factory properly, but the $scope.showPreloaded value is equal to true, as it is not referencing the value in the factory.
Here is how to change your code:
In the view
<div>aaa: {{ showPreload.loaded }}</div>
In the factory:
preload.getState = function() {
return preload;
}
The factory will now be returning the reference to the object preload. Javascript always returns references when returning an object, and returns the value when returning a primitive
Your factory's method is not a two-way binding, instead, expose a variable from your factory/service, and bind to that, for example
factory.state = { isTurnedOn : true };
you can either directly bind to that factory variable, or have your controller own variable linked to the factory variable
Return the whole object rather than returning a primitive. There is no inheritance of primitives
In factory
preload.getState = function() {
return preload;
}
In controller
$scope.preloadState = preload.getState();
In view
<div>aaa: {{ preloadState.preload ?'Yes':'No'}}</div>
Now angular view watchers will detect property changes of the object and update view
You should try to write this: scope.preload = preloader;
And then you can manipulate your object scope.preload.

Angularjs ngClass work not correct or how he work?

Why ng-class don't work with scope var's
sheikContolls.controller('MainPageController', ['$scope', 'authService',
function ($scope, authService) {
if(!authService.isAuthenticated()){
$scope.class1 = "good";
}
else {
$scope.class1= "bad";
}
}
]);
HTML:
<div class="demo" ng-class="class1"></div>
When you first start the class is added and we have class="demo good", but if then isAuthenticated() return true, class don't change, why ??
The same with next HTML:
<div class="demo {{class1}}"></div>
If i make a function - all work perfect.
<div class="demo" ng-class="getClass()"></div>
The code in the controller is called only once when the controller is instantiated and never again later, so it does never check authService.isAuthenticated() again.
When you use a function in your HTML-Code that function is called regularely and watched for its result, so later changes will be applied.
You could move the detailed check in the controller and call it from the HTML for some encapsulation:
sheikContolls.controller('MainPageController', ['$scope', 'authService',
function ($scope, authService) {
$scope.isAuthenticated = function(){
return authService.isAuthenticated();
};
}
]);
<div class="demo" ng-class="{good: isAuthenticated(), bad: !isAuthenticated() }"></div>
authService.isAuthenticated is not bound to the scope, and the controller definition function only runs once so initially class1 is good, but that functionality never runs again.
You will have to bind isAuthenticated to the scope in some way for this to work. I might consider an alternate solution like binding to a value on authService that is updated after authorization is done, but in the meantime you can do this:
$scope.getClass = function () {
if (authService.isAuthenticated()) {
return "bad";
}
return "good";
};
http://plnkr.co/edit/1xFRoTusNLHXeIhosGOp?p=preview

angularjs: scope value doesn't get updated in view

there are buttons in detail.html file:
<div ng-controller="test.views.detail">
<div data-ng-repeat="item in details" scroll>
<button ng-click="showDetails(item)">The details</button>
in detail.js file
angular.module('test')
.controller('test.views.detail', function($scope) {
$scope.detailsClicked = false;
$scope.showDetails = function(item){
$scope.detailsClicked = true;
}....
in formDetail.html code:
<div ng-controller="test.views.detail">
{{detailsClicked}}
<div ng-if="detailsClicked">...
Initially it shows false for detailsClicked, when I click on button it goes to showDetails function but value of $scope.detailsClicked never get updated! It is straight forward not sure why it doesn't work:(
This is because you're using the same controller at two places and expecting the scope object to be the same which it is not. Everytime you call ng-controller in your markup a new scope object will be created. If you want them to be based off the same data then use a service.
Here is an example
app.controller('test.views.detail', function($scope, detailsClicked) {
$scope.detailsClicked = detailsClicked;
$scope.showDetails = function(item){
$scope.detailsClicked.isClicked = true;
}
});
Create a factory/service which will retain the data, make sure the data is a
app.factory('detailsClicked', function(){
var data = {
isClicked: false
}
return data;
});

Scope values to a requested content

I have a view that contains a button, when the button is clicked, a $http.get request is executed and the content is appended on the view.
View:
<button ng-click="includeContent()">Include</button>
<div id="container"></div>
Controller:
$scope.includeContent = function() {
$http.get('url').success(function(data) {
document.getElementById('container').innerHTML = data;
}
}
The content to include:
<h1>Hey, I would like to be {{ object }}</h1>
How can I scope a value to object? Do I need to approach this in a complete different way?
The built-in directive ng-bind-html is the way you are looking for.
Beware, that ng-bind-html requires a sanitized string, which is either done automatically when the correct libary is found or it can be done manually ($sce.trustAsHtml).
Don't forget to inject $sce in your controller.
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.data = $sce.trustAsHtml(data);
}
}
<button ng-click="includeContent()">Include</button>
<div ng-bind-html="data"></div>
As you also want to interpolate your requested HTML, I suggest using $interpolate or, if it can contain whole directives or should have a full fledged two-way-data-binding, use $compile instead.
In your case alter the assignment to
$scope.data = $sce.trustAsHtml($interpolate(data)($scope));
Don't forget to inject $interpolate/$compile aswell.
As I don't know about your $scope structure I assume that "object" is available in this scope. If this isn't the case then change the $scope parameter to whatever object contains your interpolation data.
You should use a controller to do this (I imagine you are since you're using $scope).
ctrl function () {
var ctrl = this;
ctrl.includeContent = function () {
$http.get("url").success(function (data) {
ctrl.object = data;
});
};
}
<div ng-controller="ctrl as ctrl">
<button ng-click="ctrl.includeContent()">Include</button>
<div id="container">
<h1 ng-show="ctrl.object">Hey, I would like to be {{ctrl.object}}</h1>
</div>
</div>
You need not select an element and append the data to it. Angular does it for you. That's what is magic about angular.
In your controller's scope, just update object and angular does the heavy-lifting
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.object = data;
}
}
If that's html code from a server, then you should use the 'ng-bind-html' attribute:
<button ng-click="includeContent()">Include</button>
<div id="container" ng-bind-html="htmlModel.ajaxData"></div>
Controller:
$scope.htmlModel = {ajaxData:''};
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.htmlModel.ajaxDataL = data;
}
}
One way is to use ng-bind-html as suggested.
Another way is with $compile:
app.controller('MainCtrl', function($scope, $http, $compile) {
$scope.error='error!!!';
$scope.includeContent = function() {
$http.get('url').success(function(data) {
var elm = angular.element(document.getElementById('container')).html(data);
$compile(elm)($scope);
}).error(function(){
var elm = angular.element(document.getElementById('container')).html('{{error}}');
$compile(elm)($scope);
})
}
});
Also, typically in angular, when you want to manipulate the DOM you use directives.
DEMO

Angular, two way binding with two variables

I have two variables that are related by a function, and the user should be able to change one or the other in an input field which should automatically change the other.
How can I do that, right now I'm just using $watch for both.
Here's some sample code and a fiddle.
JS,
angular.module("test", [])
.controller("MyController", function ($scope) {
$scope.letter = 'A';
$scope.number = 1;
$scope.map = { 'A': 1, 'B': 2, 'C': 3, 'D': 4 };
$scope.$watch('letter', function(new_val, old_val){
if(new_val != old_val){
$scope.number = $scope.map[new_val];
}
});
$scope.$watch('number', function(new_val, old_val){
...
});
});
HTML,
<div ng-app="test">
<div ng-controller="MyController">
<input ng-model="letter" />
<input type="number" ng-model="number" />
</div>
</div>
There are a number of ways you can do this, and using $watch is certainly one of them. As mentioned by Matt, you could also use the ng-change directive to fire a method on your controller.
The third way that I would like to offer up, is to make use of ES5 properties, and the Controller 'as' syntax that Angular introduced in 1.2+
If you define your controller as a JS object instead of using an anonymous function, you can add properties and methods to the prototype:
myController = function () {
this.map = {'A': 1,'B': 2,'C': 3,'D': 4};
this._letter = 'A';
this._number = 1;
};
Now we can extract the work you have already done for getting your letter and number values into functions:
myController.prototype.getLetterValue = function (num) {
for (var key in this.map) {
if (this.map.hasOwnProperty(key)) {
if (this.map[key] === num) {
return key;
}
}
}
};
myController.prototype.getNumberValue = function (letter) {
return this.map[letter];
};
Lastly, we are going to declare a couple of properties on your controller that encapsulate the desired functionality using Object.defineProperty.
Object.defineProperty(
myController.prototype,
"letter", {
get: function () {
return this._letter;
},
set: function (newValue) {
this._letter = newValue;
this._number = this.getNumberValue(this._letter);
},
enumerable: true,
configurable: true
});
Object.defineProperty(
myController.prototype,
"number", {
get: function () {
return this._number;
},
set: function (newValue) {
this._number = newValue;
this._letter = this.getLetterValue(this._number);
},
enumerable: true,
configurable: true
});
Add this controller to your module:
angular.module("test", [])
.controller("MyController", myController);
And lastly, you just need to modify your binding syntax slightly in order to use the new Controller 'as' syntax. This will allow you to bind directly to properties and methods on your controller instead of having to use $scope
<div ng-app="test">
<div ng-controller="MyController as ctrl">
<input ng-model="ctrl.letter" />
<input type="number" ng-model="ctrl.number" />
</div>
</div>
Live Demo
Summary
This isn't exactly less code, but does have several advantages.
Your controller is decoupled from $scope and $watch making it more portable
The controller code is easier to read because all the functionality isn't nested inside an anonymous function
The code is a little more forward looking because future versions of Angular will probably eliminate $scope and the $digest loop altogether by using native observables.

Resources