Data attribute change not being observed - angularjs

I'm changing data attribute of a dom element and using $observe within the directive to detect for any changes but it doesn't seem to work after clicking on the "change data" button
HTML
<body ng-controller="MainCtrl">
<div id="container" data-name="somename" mydirective>Data</div>
<button ng-click="changeData();">Change Data Attribute</button>
</body>
JS
app.controller('MainCtrl', function($scope) {
$scope.changeData = function() {
var el = document.querySelector('#container');
angular.element(el).attr('data-name', 'hello');
}
});
app.directive('mydirective', function() {
return {
link : function(scope, element, attrs, ngModel) {
attrs.$observe("name", function (newValue) {
console.log(newValue);
});
}
}
Plnkr : http://plnkr.co/edit/saM7fO0DdsaaDBW7ADQH?p=preview

why dont you use expression data-name="{{datax}}" ?
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.datax = "somename";
$scope.changeData = function() {
//var el = document.querySelector('#container');
//angular.element(el).attr('data-name', 'hello');
$scope.datax = "hello";
}
});
app.directive('mydirective', function() {
return {
link : function(scope, element, attrs, ngModel) {
attrs.$observe("name", function (newValue) {
console.log(newValue);
});
}
}
});

Related

angularjs play audio from a link from firebase

I'm facing a problem to load a html5 media player to play audio (OGG) file from a link firebase.
Here's my view
<my-audio>
<audio>
<source id="lol" ng-src="{{msg.text}}" />
</audio>
<button class="play">Play Music</button>
<button class="pause">Pause Music</button>
</my-audio>
Here's my directive
angular.module('myApp')
.directive('myAudio', function() {
return {
restrict: 'E',
link: function(scope, element, attr) {
var player = element.children('.player')[0];
element.children('.play').on('click', function() {
player.play();
});
element.children('.pause').on('click', function() {
player.pause();
});
}
};
});
{{msg.text}} comes from a firebase link, example:
https://firebasestorage.googleapis.com/v0/b/smartdev-8a133.appspot.com/o/whatsapp_media_audios%2F08CB2B0764907A3300.ogg?alt=media&token=a1b80178-2309-4ea3-b22f-b6d6aa52efd9
The problem is on how you select the elements by class, according to angular.element docs, .children() doesn't support selector, therefore you have to select them in a different way.
For example:
var $playBtn = angular.element(element[0].getElementsByClassName('play'));
I've made an example using a static Audio object to exemplify:
angular.module('myApp', [])
.directive('myAudio', function() {
return {
restrict: 'E',
link: function(scope, element, attr) {
// Using a static Audio object
var player = new Audio('https://firebasestorage.googleapis.com/v0/b/smartdev-8a133.appspot.com/o/whatsapp_media_audios%2F08CB2B0764907A3300.ogg?alt=media&token=a1b80178-2309-4ea3-b22f-b6d6aa52efd9');
var elm = element[0],
byClass = function(klass) {
return angular.element(elm.getElementsByClassName(klass));
};
byClass('play')
.on('click', function() {
player.play();
});
byClass('pause')
.on('click', function() {
player.pause();
});
}
};
});
angular.element(function() {
angular.bootstrap(document, ['myApp']);
});
<my-audio>
<button class="play">Play Music</button>
<button class="pause">Pause Music</button>
</my-audio>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>
UPDATE Added example using html <audio> tag.
angular.module('myApp', [])
.directive('myAudio', function() {
return {
restrict: 'E',
link: function(scope, element, attr) {
// Using html <audio> tag with controlls
var player = element.find('audio')[0];
var elm = element[0],
byClass = function(klass) {
return angular.element(elm.getElementsByClassName(klass));
};
byClass('play')
.on('click', function() {
player.play();
});
byClass('pause')
.on('click', function() {
player.pause();
});
}
};
});
angular.element(function() {
angular.bootstrap(document, ['myApp']);
});
<my-audio>
<audio src="https://firebasestorage.googleapis.com/v0/b/smartdev-8a133.appspot.com/o/whatsapp_media_audios%2F08CB2B0764907A3300.ogg?alt=media&token=a1b80178-2309-4ea3-b22f-b6d6aa52efd9" controls></audio>
<button class="play">Play Music</button>
<button class="pause">Pause Music</button>
</my-audio>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>

How to expose directive methods using a service

How to expose directive methods without using $broadcast or '=' between modules?
Using $broadcast (events) if there are multiple directives all will be notified. It cannot return value too.
Exposing directive's function by html attribute I think it is not that best that Angular has to offer.
Angular Bootstrap UI do it using services (I guess): It have a service named "$uibModal".
You can call a function "$uibModal.open()" of Modal Directive by injecting $uibModal service.
Is that the right way?
An example of a directive that registers its API with a service:
app.service("apiService", function() {
var apiHash = {};
this.addApi = function (name,api) {
apiHash[name] = api;
};
this.removeApi = function (name) {
delete apiHash[name];
};
this.getApi = function (name) {
return apiHash[name];
};
});
app.directive("myDirective", function (apiService) {
return {
restrict: 'E',
scope: {},
template: `<h1>{{title}}</h1>`,
link: postLink
};
function postLink(scope, elem, attrs)
var name = attrs.name || 'myDirective';
var api = {};
api.setTitle = function(value) {
scope.title = value;
};
apiService.addApi(name, api);
scope.$on("$destroy", function() {
apiService.removeApi(name);
});
}
});
Elsewhere in the app, the title of the directive can be set with:
apiService.getApi('myDirective').setTitle("New Title");
Notice that the directive registers the api with a name determined by the name attribute of the directive. To avoid memory leaks, it unregisters itself when the scope is destroyed.
Update
How could I use it from a controller?
app.controller('home', function($scope,apiService) {
$scope.title = "New Title";
$scope.setTitle = function() {
apiService.getApi('mainTitle').setTitle($scope.title);
};
})
<body ng-controller="home">
<my-directive name="mainTitle"></my-directive>
<p>
<input ng-model="title" />
<button ng-click="setTitle()">Set Title
</button>
</p>
</body>
The DEMO
angular.module('myApp', [])
.service("apiService", function() {
var apiHash = {};
this.addApi = function(name, api) {
apiHash[name] = api;
};
this.getApi = function(name) {
return apiHash[name];
};
})
.directive("myDirective", function(apiService) {
return {
restrict: 'E',
scope: {},
template: `<h1>{{title}}</h1>`,
link: postLink
};
function postLink(scope, elem, attrs) {
var name = attrs.name || 'myDirective';
var api = {};
api.setTitle = function(value) {
scope.title = value;
};
apiService.addApi(name, api);
scope.$on("$destroy", function() {
apiService.addApi(name, null);
});
}
})
.controller('home', function($scope,apiService) {
$scope.title = "New Title";
$scope.setTitle = function() {
apiService.getApi('mainTitle').setTitle($scope.title);
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="home">
<my-directive name="mainTitle"></my-directive>
<p>
<input ng-model="title" />
<button ng-click="setTitle()">Set Title
</button>
</p>
</body>
.factory('myService', [function() {
return {
charCount: function(inputString) {
return inputString.length;
}
}
}])
this service exposes function charCount();
in your directive you have to inject it like this
.directive('testDirective', ['myService', function(myService) {
return {
restrict: 'A',
replace: true,
template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
link: function($scope, el, attrs) {
$scope.myTestString = 'string of length 19';
$scope.strLen = myService.charCount( $scope.myTestString );
}
}
}])
and, of course call it
$scope.strLen = myService.charCount( $scope.myTestString );
<html>
<style>
#out {
width:96%;
height:25%;
padding:10px;
border:3px dashed blue;
font-family: monospace;
font-size: 15px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var APP = angular.module('MYAPP', []);
APP.controller('main', ['$scope', '$element', '$compile', 'myService', function($scope, $element, $compile, myService) {
$scope.test = 'my Test Controller';
$scope.directiveTest = "directive test";
var testSvc = myService.charCount($scope.test);
$scope.showTestDir = true;
}])
.directive('testDirective', ['myService', function(myService) {
return {
restrict: 'A',
replace: true,
template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
link: function($scope, el, attrs) {
$scope.myTestString = 'string of length 19';
$scope.strLen = myService.charCount( $scope.myTestString );
}
}
}])
.factory('myService', [function() {
return {
charCount: function(inputString) {
return inputString.length;
}
}
}])
.filter('toUpper', function() {
return function(input) {
return input.toUpperCase();
}
})
.filter('toLower', function() {
return function(input) {
return input.toLowerCase();
}
})
;
</script>
<body ng-app="MYAPP">
<div id="out" ng-controller="main">
{{test}} - not filtered
<br/>
{{test|toUpper}} - filtered toUpper
<br/>
{{test|toLower}} - filtered toLower
<br/>
<br/>
<div test-directive ng-if="showTestDir"></div>
</div>
</body>
</html>

How to bind multiple custom events in angularjs?

I need to bind custom events in angularjs(1.x) and I tried with the following code,
HTML
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link href="https://www.polymer-project.org/components/polymer/polymer.html" rel="import">
<link href="https://www.polymer-project.org/components/paper-button/paper-button.html" rel="import">
<div ng-app="demo-app">
<div ng-controller="DemoController">
<template bind-angular-scope is="auto-binding">
<paper-button raised on-tap="{{clickMe}}" on-mouseover="{{mouseOver}}">click me</paper-button>
</template>
<pre><code>{[{text}]}</code></pre>
</div>
</div>
Script
<script>
angular.module('demo-app', [])
.config(function ($interpolateProvider) {
$interpolateProvider.startSymbol('{[{').endSymbol('}]}');
})
.directive('bindAngularScope', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
for (k in scope) {
if (!element[0][k]) {
element[0][k] = scope[k];
}
}
}
}
})
.controller('DemoController', function ($scope) {
$scope.text = '';
$scope.clickMe = function () {
$scope.text += '\nyou clicked me!!';
$scope.$apply();
};
$scope.mouseOver = function () {
$scope.text += '\nyou hovered me!!';
$scope.$apply();
}
});
</script>
This is not working.Could you point out me the issue or Is there is any solution for binding custom events(multiple) ? Do we need to create a custom directive for each of them ?
Note:
The above code is referred from the following url,
How to bind custom events in AngularJS?
Thanks in advance!
angular.module('demo-app', [])
.config(function ($interpolateProvider) {
$interpolateProvider.startSymbol('{[{').endSymbol('}]}');
})
.directive('bindAngularScope', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
for (k in scope) {
if (!element[0][k]) {
element[0][k] = scope[k];
}
}
elem.bind('click', function() {
/* Place your click logic here * /
});
}
}
})

How can I access $event inside a directive?

I can't seem to pass it as an attribute!
I can add an ng-click="function($event)" and pass $event that way to a controller function, but I would like to access it inside a directive.
The ultimate purpose is to stopPropagation() and/or preventDefault() of an element inside a directive instead of in a controller.
EDIT: I will post code accordingly
<div ng-app="myApp" ng-controller="MyCtrl">
<button stop-toggle ng-click="testFunction($event)">
click me
</button>
Hello, {{name}}!
</div>
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Superhero';
$scope.testFunction = function($event){
console.log("you can access the event here:", $event);
$event.stopPropagation();
}
}
myApp.directive('stopToggle', function(){
return {
link: function(scope, elem, attrs){
elem.bind('click', function(){
console.log("how do i access the $event here??");
// elem.stopPropagation(); // invalid function
// elem.preventDefault(); // invalid function
})
}
}
});
http://jsfiddle.net/Lvc0u55v/10656/
var myApp = angular.module('myApp',[]);
myApp.directive('stopToggle', function(){
return {
link: function(scope, elem, attrs){
elem.on('click', function(e){
e.stopPropagation();
e.preventDefault();
})
}
}
});
Just pass event as an argument to the function inside .on()

AngularJS : Issue passing data to a new browser window on Internet Explorer

I am trying to pass some objects to a new browser window. I followed the suggestion from AngularJS: open a new browser window, yet still retain scope and controller, and services
It works on Chrome, but doesn't on IE. My shared objects are always undefined on IE. Any suggestions?
Code for simplified version of what I am trying to do
My parent html
<html ng-app="SampleAngularApp">
<body>
<div ng-controller="popupCtrl">
<my-popup foo="foo" abc="abc">Open Popup from here</my-popup>
</div>
</body>
</html>
My parent JS
var SampleAngularApp = angular.module('SampleAngularApp', []);
var popupCtrl = function ($scope) {
$scope.foo = { baz: 'qux' };
$scope.abc = "12345";
};
SampleAngularApp.directive('myPopup', ['$window', function ($window) {
return {
restrict: 'EA',
scope: {
foo: '=',
abc: '='
},
link: function (scope, elem, attrs) {
elem.css({ 'cursor': 'pointer' });
elem.bind('click', function () {
var popWdw = $window.open("popupWindow.html", "popupWindow", "width=500,height=500,left=100,top=100,location=no");
popWdw.abc = scope.abc;
popWdw.foo = JSON.stringify(scope.foo);
});
}
};
}]);
My popup html
<html ng-app="PopupApp">
<body ng-controller="childCtrl">
</body>
</html>
My popup JS
var PopupApp = angular.module('PopupApp', []);
var childCtrl = function ($scope) {
alert(window.foo);
};
PopupApp.controller(childCtrl);
Per shaunhusain and Sunil D's suggestions, I have changed my code as below and it works
My parent JS
link: function (scope, elem, attrs) {
elem.css({ 'cursor': 'pointer' });
elem.bind('click', function () {
$window.abc = scope.abc;
$window.foo = JSON.stringify(scope.foo);
var popWdw = $window.open("popupWindow.html", "popupWindow", "width=500,height=500,left=100,top=100,location=no");
});
}
My popup JS
var childCtrl = function ($scope) {
alert(window.opener.foo);
};

Resources