How to toggle Angularjs ng-show without $rootScope - angularjs

As you might have read on the Angularjs FAQ the use of $rootScope is not encouraged. Therefore, a service is more appropriate for storing data that would be shared later on between controllers.. But I couldn't make the ng-show directive work without the use of $rootScope.
Here's what i'm doing;
index.html
<div ng-controller="GlobalCtrl">
<div ng-show="showSettings"></div>
<aside ng-show="showSettings" >
<h1>Some text</h1>
<button ng-click="closeSettings()">Ok</button>
</aside>
</div>
app.js
(I bootstrap the app since I'm developing for cordova.. code truncated)
var app = angular.module('tibibt', ['tibibt.controllers', 'tibibt.filters', 'tibibt.services']);
angular.element(document).ready(function () {
angular.bootstrap(document, ['tibibt']);
});
services.js
/* Create an application module that holds all services */
var tibibtServicess = angular.module('tibibt.services', []);
/* Global service */
tibibtServicess.service('globalService', function () {
this.Data = {
showSettings: 'false'
};
this.getAll = function () {
return this.Data;
};
this.setSettings = function (val) {
this.Data.showSettings = val;
};
});
controllers.js
/* Create an application module that holds all controllers */
var tibibtControllers = angular.module('tibibt.controllers', []);
tibibtControllers.controller('GlobalCtrl', function ($scope, globalService) {
// Get initial value
$scope.showSettings = globalService.getAll().showSettings;
console.log('Settings initially set to -> ' + globalService.getAll().showSettings);
// Open settings menu
$scope.openSettings = function () {
globalService.setSettings('true');
console.log('Settings set to -> ' + globalService.getAll().showSettings);
};
// Close settings menu
$scope.closeSettings = function () {
globalService.setSettings('false');
console.log('Settings set to -> ' + globalService.getAll().showSettings);
};
});
The console shows the changes but the ng-show doesn't bind/update to this changes!

this is only an assignment thats evaluated once:
$scope.showSettings = globalService.getAll().showSettings;
so the value will never change.
there are at least to possible solutions:
assign the service to the scope: $scope.settings = globalService. Now you may access the service in your view:
ng-show="settings.getAll().showSettings"
or register a watch by yourself:
$scope.showSettings = false;
$scope.$watch(globalService.getAll().showSettings, function(newValue){
$scope.showSettings = newValue;
});

Try:
$scope.Data = globalService.getAll();
HTML:
<div ng-controller="GlobalCtrl">
<div ng-show="Data.showSettings"></div>
<aside ng-show="Data.showSettings" >
<h1>Some text</h1>
<button ng-click="closeSettings()">Ok</button>
</aside>
</div>
DEMO
Explanation:
This line $scope.showSettings = globalService.getAll().showSettings; assign data by value => the value of showSettings is copied to $scope.showSettings => they are 2 separate blocks of memory. When you change the globalService.Data.showSettings, $scope.showSettings is not updated because it's another block of memory.
Changing to $scope.Data = globalService.getAll(); assign data by reference => they point to the same block of memory.

Related

AngularJS: open default mail application and populate to and subject via link

I'm trying to open a link using ng-href and as soon as I click on it, I need it to open an email address and populate its To, Subject and it's Body field. Can this be made possible?
I tried this,
<a ng-click=”sendMail($event)” href=”gmail.com” />
$scope.sendMail = function($event)
{
if($scope.showMailLink == true )
{
$event.preventDefault();
window.location = $event.target.href;
$window.open("mailto:veron#gmail.com?subject=hello&body=fggf_self");
}
};
This is what I've tried and it isn't actually right. Is this a valid approach?
You should use location.href:
location.href = "mailto:veron#gmail.com?subject=hello&body=fggf"
or target the current window:
$window.open("mailto:veron#gmail.com?subject=hello&body=fggf", '_self');
- Example -
View
<div ng-controller="MyCtrl">
<button ng-click="mailWithLocation()">
Mail via location.href
</button>
<button ng-click="mailWithWindowOpen()">
Mail via $window.open()
</button>
</div>
AngularJS Application
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope, $window) {
$scope.mailWithLocation = function () {
location.href = "mailto:veron#gmail.com?subject=hello&body=fggf"
}
$scope.mailWithWindowOpen = function () {
$window.open("mailto:veron#gmail.com?subject=hello&body=fggf", '_self');
}
});

AngularJS ng-click doesn't work in digest cycle

Html:
<a href id="link "ng-click="print(arg)"> print </a>
Angularjs controller:
$scope.return_promise =function(arg){
return $http.post('\path');
)};
$scope.print = function(arg){
url ="other/path/"
$scope.return_promise(arg).then(function(r){
if(r){
$('#link').attr('href', url);
});
};
Problem: I checked with chrome debugger, the href actually updated, but the event doesn't trigger (i.e. not go to the url). If I click it again, it works.
If I add a statement document.getElementById('#link').click() at the end of if clause, it will prompt an error "digest cycle is in progress"
How can i solve this.
Not sure if I get your question. First, check if the code you paste is the code you wanted add here, because it has numerous errors. If you would like to replace dynamically href attribute do it like so:
<div ng-controller="SomeCtrl as ctrl">
print
</div>
(function () {
'use strict';
angular
.module('module')
.controller('SomeCtrl', SomeCtrl);
SomeCtrl.$inject = ['$scope'];
function SomeCtrl($scope) {
var vm = this;
vm.url = "#";
vm.return_promise = function (arg) {
return $http.post('/path');
};
vm.print = function (arg) {
var url = "other/path/";
vm.return_promise(arg).then(function (r) {
if (r) {
vm.url = url;
}
});
};
}
}());

Why does my scope binding to a service array not update when the array gets replaced

I have a scope binding to an array stored in a service.
When the array changes, the scope notices the change and updates the values in my template.
However, when the Array gets replaced by another array, the scope doesn't seem to recognize a change and won't update the list.
I know that this is a common behaviour of angularjs and that it's probably intended to be like this, but I don't get why.
In my understanding, the scope variable should update, whenever the bound reference changes.
Is $scope.myVar = anyOtherVar; not equivalent to a $scope.$watch('anyOtherVar',function(..){//update myVar}); ?
See my fiddle for demonstrating the problem.
http://jsfiddle.net/sL9k7q9L/1/
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
myApp.factory('myService', function() {
var anyArray = [{"name":"peter"}];
var anyOtherArray = [{"name":"laura"}];
return {
anyArray: anyArray,
newElement: function(){
anyArray.push({"name":"bob"});
},
replaceWholeArray: function(){
anyArray = anyOtherArray;
console.log(anyArray);
}
}
});
function MyCtrl($scope,myService) {
$scope.elements = myService.anyArray;
$scope.newElement = function(){
myService.newElement();
}
$scope.replaceWholeArray = function(){
myService.replaceWholeArray();
}
}
and the corresponding template:
<div ng-controller="MyCtrl">
<button ng-click="newElement()">
newElement()
</button>
<button ng-click="replaceWholeArray()">
replaceWholeArray()
</button>
<ul>
<li ng-repeat="el in elements">{{el.name}}</li>
</ul>
</div>
You are updating variables but that doesn't update any other variable assignments that were made using the original variable.
Thus reference to original array is broken for myService.anyArray
Simple example
var a = 1;
var b = a;
a = 2;
alert(b);// is still 1 due to value of `a` when it was assigned
Instead just update the factory object property but to do that you need to store a reference to the returned object first
myApp.factory('myService', function() {
var anyArray = [...];
var anyOtherArray = [...];
var factoryObject = {
anyArray: anyArray,
newElement: function() {
anyArray.push({"name": "bob"});
},
replaceWholeArray: function() {
// change this part
//anyArray = anyOtherArray;
// To:
factoryObject.anyArray = anyOtherArray;
}
}
return factoryObject
});

Why I dont get array in template?

I have ng-click method showSchedules() that calls AJAX:
$scope.showSchedules = function () {
$scope.loadCalendar = true;
appointmentService.getSchedule().then(function (response) {
calendarService.set(response.data.calendar, function () {
$timeout(function () {
$scope.refleshCalendars();
$scope.loadCalendar = false;
console.log($scope.events);
}, 100);
});
});
};
Inside this method you can see: console.log($scope.events);
It gives me filled array by objects.
When I do {{events}} in trmplate HTML I get [].
Why I get empty array?
HTML code:
<div ng-controller="ScheduleController as vmx">
{{events}}
<mwl-calendar
events="events"
view="vmo.calendarView"
view-title="vmo.calendarTitle"
current-day="vmo.calendarDay"
on-event-click="vmo.eventClicked(calendarEvent)"
on-event-times-changed="vmo.eventTimesChanged(calendarEvent);
calendarEvent.startsAt = calendarNewEventStart;
calendarEvent.endsAt = calendarNewEventEnd"
auto-open="true"
day-view-start="06:00"
day-view-end="23:00"
day-view-split="30"
cell-modifier="vmo.modifyCell(calendarCell)">
</mwl-calendar>
</div>
You want {{vmx.events}} because you are using the controller as annotation
Using controller as makes it obvious which controller you are
accessing in the template when multiple controllers apply to an
element.
ngController documentation (Search for controller as)

Bind javascript method in Angularjs template

My application is using Angularjs at client side. I have five directive which are using same logic. Following are required details of my code
I have one pure javascript class as AppUtilities.js having following method defined
var AppUtilities = {
//Get the guid for given collection
getGUID: function (collectionName) {
var prefix;
var guid;
//get first two character
if (!Utilities.isEmpty(collectionName)) {
prefix = collectionName.substring(0, 2);
//Get timestamp
var timestamp = Utilities.getTimestampId();
//concate prefix to get the guid
guid = prefix + timestamp;
}
return guid;
}
};
I have five different directive in which I need to use "getGUID()" method to bind with template. Since template is only able to bind with scope function therefore I have defined scope method in all these five template as below
scope.getGUID = function (collectionName) {
return AppUtilities.getGUID(collectionName);
}
Then in all the five directive template, this method is bind as scope variable
<h4 class="guid" id="guid" data-ng-bind-template="{{getGUID('goal')}}"></h4>
How can I avoid declaring these method as scope variable and directly use as AppUtilities.getGUID(collectionName) in the HTML template?
There are multiple ways, but honestly, it seems like more effort than its worth, since you can just do:
scope.getGUID = AppUtilities.getGUID;
Of course, you could use $rootScope, but to me personally it feels wrong - I like when things are explicitly declared and do not magically appear.
Alternatively, if you only need to render the GUID in the UI, create a GUID directive. For example:
.directive("guid", function(){
return {
template: "<span>{{getGUID()}}</span>",
link: function(scope, element, attrs){
scope.getGUID = function(){
return AppUtilities.getGUID(attrs.guid || attrs.name);
};
}
}
});
and use as:
<h4 class="guid"><guid name="goal"></guid></h4>
Without manipulating the individual scopes or the root scope, you could simply define a filter which is usable in all templates. Note that, for all the good reasons, I'd still define and inject AppUtilities, even if it is a global, as it's own service.
app.filter('toGUID', ['AppUtilities', function (AppUtilities) {
return function (input) {
return AppUtilities.getGUID(input);
};
}]);
// <pre>{{ 'goal'|toGUID }}</pre>
app.service('AppUtilities', function () {
return AppUtilities;
});
(function (app, ng) {
'use strict';
app.filter('toGUID', ['AppUtilities', function (AppUtilities) {
return function (input) {
return AppUtilities.getGUID(input);
};
}]);
app.service('AppUtilities', function () {
return AppUtilities;
});
var Utilities = {
isEmpty: function (collectionName) {
return false;
},
getTimestampId: function () {
return '_something';
}
};
var AppUtilities = {
//Get the guid for given collection
getGUID: function (collectionName) {
var prefix;
var guid;
//get first two character
if (!Utilities.isEmpty(collectionName)) {
prefix = collectionName.substring(0, 2);
//Get timestamp
var timestamp = Utilities.getTimestampId();
//concat prefix to get the guid
guid = prefix + timestamp;
}
return guid;
}
};
})(angular.module('app', []), angular);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0-rc.2/angular.min.js"></script>
<div data-ng-app="app">
<pre>{{ 'goal'|toGUID }}</pre>
</div>
Hide it in a div:
<div id="hidden" data-ng-model="hidden" style="display:none"></div>
<script>
$('#hidden').html(AppUtilities.getGUID(collectionName));
</script>
Then, ng-bind to the div's contents:
<div id="realDiv" data-ng-bind-html="hidden"></div>

Resources