Directive custom control - angularjs

I am trying to create a directive as custom video control. I loading an html file to templateUrl of this directive. The problem is when there are more than one controls, they have the same src file set to all of them and they are sharing state of video as well. When I pause from another control, it pauses video being played on 1st control. Here is directive template that I am using:
dApp.directive('myVideoControl', function(){
return {
scope: {
cameraUrl: '=vcCameraUrl'
},
restrict: 'E',
templateUrl: './../../js/directives/myVideoControl.html',
link: function (scope, element, attrs) {
scope.playVideo = function(){
var v = document.getElementsByTagName("video")[0];
v.play();
}
scope.pauseVideo = function(){
var v = document.getElementsByTagName("video")[0];
v.pause();
}
}
}
});
Will greatly appreaciate if anyone can point out if I am doing anything wrong here.
Thanks.

It looks like the problem you are having is that you are looking up the element by tag name. Basically, every element in your dom with the tag <video> is going to be effected by any use of your directive.
The idea with directives, is that they provide direct access to the element that the directive was assigned. In your case element inside the link function parameters. So you need to reference the individual associated elements like this:
var v = element[0];
v.play();
If you have assigned the directive on a parent element, and want all children, then use the find() jqLite function on the directive element:
var v = element.find('video')[0];
v.play();

var v = document.getElementsByTagName("video")[0];
You are selecting the first video tag in the entire page.
instead get the element inside your template,something like
element.find('video')[0]

Related

Angular Animation Directive

I'm trying to write an angular directive that will animate a list of words similar to http://codepen.io/rachsmith/pen/BNKJme . However, I'm needing to load the text from a json file and then select a random sentence to apply the animation to. I have this part working, but am having trouble accessing the directive's child elements. I am assuming this is because the directive is being called before the elements are rendered, but using scope.$on($viewContentLoaded, function... has not made a difference.
I have jQuery and Underscore available.
Here is my code:
Controller
Data.sentences().then(function (response) {
var sentences = response.data;
$scope.sentence = _.sample(sentences);
});
View
<div class="rotator">
<p>{{sentence.static}}</p>
<text-rotator>
<span class="word" ng-repeat="item in sentence.options">{{item}}</span>
</text-rotator>
</div>
Directive
app.directive('textRotator', function () {
return {
restrict: 'E',
link: function (scope, el, attrs) {
var words = el.children('.word');
//cannot access array of items with class of word
}
};
});
Your assumption is correct, the ng-repeat-ed words are not yet in the DOM at the time the link function of the directive is executed. The sentence object is fetched asynchronously.
Listening on $viewContentLoaded won't help: this is an event fired by ngRoute module when the content of the ngView is loaded. After a digest cycle followed by DOM updates due to a change on the model, this event is not fired.
Actually, I think you're creating yourself troubles as the data could be (should be) passed as a parameter to the directive. The child word elements would be the template of the directive. I suggest something like the following:
app.directive('textRotator', function () {
return {
restrict: 'E',
scope: {
options: '='
},
templateUrl: 'words.html',
link: // ...
}
});
Template:
<text-rotator options="sentence.options"></text-rotator>
This fiddle might help you. The animation part has been replaced by a simple toggling of the opacity. Also, the words are mocked in the controller, you should make sure they are resolved by the router in the definition of the route / state, or otherwise you would have to add a watcher in the directive.

How to modify transcluded content before compile inside directive?

What I want to do, is to handle transclude by hand and modify the content before I insert into the DOM:
return {
restrict: 'E',
transclude: true,
template: '<HTML>',
replace: true,
link: function(scope, element, attrs, ngModelCtrl, $transclude) {
var caption = element.find('.caption');
$transclude(function(clone) {
console.log(clone);
clone.filter('li').addClass('ng-hide'); // this don't work
clone.addClass('ng-hide'); // same this one
clone.attr('ng-hide', 'true'); // same this one
$compile(clone)(scope.$new()).appendTo(caption);
caption.find('li').addClass('ng-hide'); // and this
});
}
}
In angular.js source I found this example:
var templateElement = angular.element('<p>{{total}}</p>'),
scope = ....;
var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
//attach the clone to DOM document at the right place
});
//now we have reference to the cloned DOM via `clonedElement`
but when I add clonedElement.appendTo(caption); inside link function it only add comment with ng-repeat inside.
I need this because I need to hide all elements in this case
<dropdown>
<li ng-repeat="item in items"><a>{{item.label}}</a></li>
</dropdown>
I need to modify the template before compile or DOM after ng-repeat is expanded. Before would be better because I will be able to add logic using ng-hide directive instead of ng-hide class.
I realise it's been a long time since this question was posted, but I hope you may find the following useful.
I've been quite long and heavily in this (transclusion) business, I tried a few ways to achieve what you #jcubic need and finally I came across a solution which is really robust and quite simple.
...
replace: false,
transclude: false,
compile: function( tElement, tAttributes ) {
// store your "transcluded" content of the directive in the variable
var htmlContent = tElement.html();
// then remove it
tElement.html('');
return function postLink(scope, elem, attrs) {
// then html var is available in your link!
var $html = $('<div />',{ html:htmlContent }); // for much easier manipulation (so you can use DOM functions - you can also manipulate directly on htmlContent string)
// so you can manipulate the content however you want
scope.myVariable = true;
$html.find('li').attr('ng-hide', 'myVariable'); // add native directive
$html.removeClass('inner-content').addClass('my-inner-content'); // add/remove class
$html.find('#myElement').attr('my-directive',''); // add custom directive etc. etc.
// after you finished you just need to compile your html and append your directive element - also however you want
// you also convert back $html to the string
elem.append( $compile( $html.html() )(scope) ); // append at the end of element
/* or:
elem.find('.my-insert-point').html( $compile( $html.html() )(scope) ); // append the directive in the specific point
elem.find('[my-transclude]').html( $compile( $html.html() )($parent.scope) ); // once the scope:true it will be the same as native transclusion ;-)
scope.variable = $html.html(); // or you can probably assign to variable and use in your template with bind-html-compile (https://github.com/incuna/angular-bind-html-compile) - may need $sce.trustAsHtml
*/
}
}
...
So as you can see you have full control on your "transcluded" content and you don't even need transclusion! :-)
ps. I tested it with Angular 1.4. Not sure if it works with replace:true (I wasn's bother to test it as it's minor nuisance if it doesn't). You can use pre and post link as normally you'd use within compile function and you need to inject $compile service into your directive.
jcubic. You do not have to use $compile for what you are trying to do.
You can filter the transcluded element 'clone' and add css classes to the filtered nodes , but after that you have to append the modified clone to the template (it is identified by the 'element' attribute of the link function).
element.append(clone)
I created this jsfiddle for you.
If you still have other questions , please create a jsfiddle of your case.It Will be better to make an answer Thx
If you're using angular > 1.3 and ngTransclude in template, so you need to update not the clone, but transcluded DOM, eg:
elm.find('ng-transclude')
http://jsfiddle.net/jhqkxgos/
but be sure to compile found elements if you update some you need to access from controller

Is there any better way for creating by code a directive object?

angular.element('<my-directive></my-directive>')
Why I can not just say the name of my directive ?
Seems so strange to write HTML line inside js...
Any better way for creating instance of directive in memory?
A common pattern for writing directives is like this.
angular.module('myApp.directives', ['myApp.services'])
/*
toggleclass
A generic directive for toggling a class directly on an element
or by specifying a toggle-target option
eg. toggleclass="{class: 'open', target='.element'}"
*/
.directive('toggleclass', function(){
return {
restrict: 'A',
link: function(scope, element, attrs){
element.on('click', function(){
var ops = scope.$eval(attrs.toggleclass);
// If a toggle-target is set
if(ops.target){
element = angular.element(document.querySelector(ops.target));
}
// Toggle the class
element.toggleClass(ops.class);
});
}
}
});
});
angular.element('<my-directive></my-directive>')
AFAIK this piece of code doesnt really create a directive,it just create an element tagged "my-directive".
You dont need the closing tag by the way. angular.element('<my-directive>') is valid.
If you want to get verbose : angular.element(document.createElement('my-directive')) .
It is some kind of jQuery light.
to instanciate a directive you need to compile some DOM and pass a scope.
var e = angular.element('<my-directive></my-directive>');
$compile(e)($scope);

Get the bounding rect for an element outside of a directive

Inside a directive I want to get the result of getBoundingClientRect() for a DOM element that is no where near the element of the directive.
How should I go about this? Service that just returns that object? Is it OK to have DOM logic in a service?
I'd suggest passing the id of the element the directive needs to interact with as an attribute to the directive. Then use the document object to get a handle to that element.
See: http://jsfiddle.net/afX63/6/.
You'll obviously need to work directly with the raw DOM element to access the information you're interested in.
Your markup:
<my-directive handle-id="thatOne"/>
<div id="thatOne">Your directive can find this element easily now</div>
You directive:
app.directive('myDirective', function () {
return {
link: function (scope, elem, attrs) {
var theHandle = angular.element(document.getElementById(attrs.handleId));
theHandle.text('Changed it');
}
}
});

angularJS directive with isolated scope, attribute binding doesn't work

Please see this jsfiddle: http://jsfiddle.net/viro/DK5pC/3/
What I did looks right compared to the tutorials and replies I've found, so I'm sure I'm overlooking something trivial.
I'm trying to do a directive on a html element, that will create a sibling div to display a message associated with the original element.
for example, for this html :
<input ng-model="inp" tst-msg="message" />
I would create as a sibling element:
<div class="msg">Msg:<span ng-bind="tstMsg"></span></div>
I was hoping that tstMsg in the div's scope would be bound to message in the input's scope.
Here's what the directive looks like :
angular.module('tst', [])
.directive('tstMsg', function(){
var template = "<div class='msg' >Msg:<span ng-bind='tstMsg'></span></div>";
var link = function(scope,element,attrs) {
element.parent().append(template);
console.log("link called");
};
return {
restrict: 'A',
scope: {
tstMsg: '='
},
link: link
};
});
Well that doesn't work and I can't figure out why.
You need to $compile the template you're adding to the DOM. Angular hasn't had a chance to add it's handlers, for instance the ng-bind directive to that part of the dom.
So instead of just adding the element like this:
element.parent().append(template);
These steps will let Angular process your template and then add it.
newe = angular.element(template);
$compile(newe)(scope);
element.parent().append(newe);
Updated fiddle

Resources