I have a template, inside that template is div with the class big-box and it's inside a div container called .movie-container.
Template,
.movie-container
.big-box
I want to animate the .big-box element to a height of 300px (from 0px) when a user clicks on the .movie-container.
I've tried to setup a directive with a click function but it's not working. When I click on the .big-box div it doesn't show the click in the console log.
app.directive('big-box', [function() {
return {
link: function(scope, elem, attrs) {
elem.bind('click', function() {
console.log ('click')
});
}
}
}]);
So I could use some tips.
The naming convention used for the directive is not correct. You have to use camel case while defining your custom directives.
app.directive('bigBox', [function() { // Use camel case
return {
link: function(scope, elem, attrs) {
elem.bind('click', function() {
console.log ('click')
});
}
}
}]);
and in your html you need to use it like this :
<div class="movie-container">
<div big-box> <!-- using the directive -->
click here
</div>
</div>
The name must be written using dashes and each dash represents a capital letter from the directive definition. It is a angularjs convention.
Here is a working fiddle.
Related
I've been searching for an answer to simple but not trivial question: What is a right way to catch image' onload event in Angular only with jqLite? I found this question , but I want some solution with directives.
So as I said, this is not accepted for me:
.controller("MyCtrl", function($scope){
// ...
img.onload = function () {
// ...
}
because it is in controller, not in directive.
Here's a re-usable directive in the style of angular's inbuilt event handling directives:
angular.module('sbLoad', [])
.directive('sbLoad', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
var fn = $parse(attrs.sbLoad);
elem.on('load', function (event) {
scope.$apply(function() {
fn(scope, { $event: event });
});
});
}
};
}]);
When the img load event is fired the expression in the sb-load attribute is evaluated in the current scope along with the load event, passed in as $event. Here's how to use it:
HTML
<div ng-controller="MyCtrl">
<img sb-load="onImgLoad($event)">
</div>
JS
.controller("MyCtrl", function($scope){
// ...
$scope.onImgLoad = function (event) {
// ...
}
Note: "sb" is just the prefix I use for my custom directives.
Ok, jqLite' bind method doing well its job. It goes like this:
We are adding directive' name as attribute in our img tag . In my case , after loading and depending on its dimensions , image have to change its class name from "horizontal" to "vertical" , so directive's name will be "orientable" :
<img ng-src="image_path.jpg" class="horizontal" orientable />
And then we are creating simple directive like this:
var app = angular.module('myApp',[]);
app.directive('orientable', function () {
return {
link: function(scope, element, attrs) {
element.bind("load" , function(e){
// success, "onload" catched
// now we can do specific stuff:
if(this.naturalHeight > this.naturalWidth){
this.className = "vertical";
}
});
}
}
});
Example (explicit graphics!): http://jsfiddle.net/5nZYZ/63/
AngularJS V1.7.3 Added the ng-on-xxx directive:
<div ng-controller="MyCtrl">
<img ng-on-load="onImgLoad($event)">
</div>
AngularJS provides specific directives for many events, such as ngClick, so in most cases it is not necessary to use ngOn. However, AngularJS does not support all events and new events might be introduced in later DOM standards.
For more information, see AngularJS ng-on Directive API Reference.
I have a problem to use CKeditor.
First in the top I have this :
<textarea class="ckeditor" id="test"></textarea>
No problem I have a textarea with the buttons style (bold,underline,...) of ckeditor.
But I have the same texterea in a template :
<script type="text/ng-template".....
.....
<textarea class="ckeditor" id="test"></textarea>
.....
.....</script>
And I have a simple textarea... Do you know this problem ?
You should use directives for this, because angular loads in templates asynchronously while the ckeditors are created (by jquery probably) when the document is loaded.
Either use an existing library, like https://github.com/lemonde/angular-ckeditor, or create a simple directive yourself:
angular.module("myApp", [])
.directive("ckeditor", [function(){
return {
restrict: "A",
link: function (scope, elem, attrs) {
CKEDITOR.replace(elem[0], {
// configuration
});
}
}
}]);
html:
<textarea ckeditor></textarea>
I am using directive and template. My template is as follows
<div>
<a data-my-dir="item" data-attr1="true"
data-attr2="{{itemParentId}}"
data-attr3="{{item.id}}">
</a>
</div>
Here due to curly braces watches are added and it is affecting my performance. I don't want watches as I am not going to modify attr2 or attr3.
I want to directly resolved value here only.
We can use bindonce directive where we don't want watches where we can use bo-text="xyz" instead, but here I want to pass values as attr to my custom directive.
Inside my directive's link function I am binding click event with element as follows
link: function (scope, element, attrs) {
element.bind('click', function (event) {
var myAttr1 = attrs.attr1;
var myAttr2 = attrs.attr2;
}
}
So due to those watches in template on attr1 and attr2 I am getting these values resolved in click event.
What are the alternatives?
One time Binding
Seems like a good use case for one time binding (if you are using angular 1.3+)
<div>
<a data-my-dir="item"
data-attr1="true"
data-attr2="{{::itemParentId}}"
data-attr3="{{::item.id}}">
</a>
</div>
the directive would look like
angular.module('app', [])
.directive("myDir", function() {
return {
restrict: "A",
scope: {
"attr1": "#",
"attr2": "#",
"attr3": "#",
},
template: '<span> {{attr1}} {{attr2}} {{attr3}}</span>'
};
})
Demo
http://plnkr.co/edit/GJCZmb9CTknZZbcRHN7s?p=preview
You could use data-attr2="itemParentId" directly but for that you need to use = as currently you are using # option of directive.
app.directive('myDir', function(){
return {
scope: {
dataAttr1: '=', //or '=dataAttr1'
dataAttr2: '=' //or '=dataAttr2'
},
link: function(scope, element, attrs){
console.log(scope.dataAttr1);
console.log(scope.dataAttr2);
}
}
})
I have just started learning Angular and have set up Angular routing for my first app.
I have a shell page and some sub pages, for example products.html, help.html etc
the sub pages contains forms with bootstrap switches and validation, which for some reason stops working (the switch is rendered as a regular checkbox and validation does not run - please note that I am not using Angular JS).
However, if a place the same code directly in the shell page it works fine.
How do I get the sub pages to behave exactly like they the code in the shell page?
Basically, if I have the following code in a form in one of my subpages help.html:
<div class="form-group">
<label for="active">My checkbox<br />
<div class="switch">
<input type="checkbox" value="1" id="active" name="active">
</div>
</div>
...the switch does not render correctly, but if I move the code directly to the shell page it renders correctly.
So what is the difference in what happens in the sub page (which is shown in a on the shell page) or somw code that is placed directly in the shell page HTML.
I am assuming you are using this Bootstrap Switch plugin.
I am also assuming that you are initialising the switches that work on the shell page by doing something like:
$(function () {
$('input[type="checkbox"]').bootstrapSwitch();
});
The problem with this is that it will only apply the bootstrap switch plugin to the checkboxes that it finds on the first page load, not after you change pages within an ng-view element.
What I recommend you do instead is to create a simple AngularJS directive to apply the bootstrap switch plugin to the checkboxes. The reason this will work is that Angular will compile the contents of a view every time you change pages and the link() functions of all the directives found will be run. Thus, your checkboxes, if they use this new directive, will always have the plugin applied correctly.
The directive could be as simple as:
app.directive('bsSwitch', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
$(element).bootstrapSwitch({
onSwitchChange: function(event, state) {
scope.$apply(function() {
ngModelCtrl.$setViewValue(state);
});
}
});
}
}
});
Then change your markup in the views to be:
<div class="form-group">
<label for="active">My checkbox</label>
<br />
<div class="switch">
<input type="checkbox" value="1" id="active" name="active" bs-switch>
</div>
</div>
EDIT: If you wish to apply the bootstrap switch to all checkboxes on the application without the need for additional attributes, you could instead create a directive that will apply to all <input>s, and then just check if they are checkboxes.
Like so:
app.directive('input', function() {
return {
restrict: 'E',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
if (attrs.type === 'checkbox')
$(element).bootstrapSwitch({
onSwitchChange: function(event, state) {
scope.$apply(function() {
ngModelCtrl.$setViewValue(state);
});
}
});
}
}
});
And then you can omit the bs-switch attribute in your markup.
See my working Plunkr example (Plunkr is a better tool than jsFiddle for Angular examples, and allows you to create multiple HTML, CSS and JS files).
EDIT 2: As Mark pointed out, it was broken if the checkbox was initially checked. Here is a fixed version of the directive (and an updated Plunkr):
// As an element directive that will apply to all checkbox inputs:
// <input type="checkbox" ng-model="blah">
app.directive('input', function() {
return {
restrict: 'E',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
if (attrs.type === 'checkbox' && !Object.hasOwnProperty(attrs, 'bsSwitch')) {
$(element).bootstrapSwitch({
onSwitchChange: function(event, state) {
scope.$apply(function() {
ngModelCtrl.$setViewValue(state);
});
}
});
var dereg = scope.$watch(function() {
return ngModelCtrl.$modelValue;
}, function(newVal) {
$(element).bootstrapSwitch('state', !! newVal, true);
dereg();
});
}
}
}
});
I am a angularjs newbie, and I must miss something simple.
What I am trying to do is to create tooltip by using angular ui. I create a customer directive, in which it will add 3 angular directives to the attribute of the element based on the placeholder value:
myApp.directive('ngTooltip', function () {
return{
restrict: 'A',
link: function (scope, element, attrs) {
attrs.$set('tooltip', attrs['placeholder']);
attrs.$set('tooltip-placement', 'bottom');
attrs.$set('tooltip-trigger', 'focus');
}
}
});
In my markup, I have
and it got rendered as expected:
<input name="test" placeholder="this is test" tooltip="this is test" tooltip-placement="bottom" tooltip-trigger="focuse" />
However, the tooltip does not work. If I directly apply the 3 tooltip attributes to the markup, the tooltip works.
Looks like the 3 directives added by the custom directive do not get evaluated by angularjs.
Any ideas?
You can't dynamically add directives without recompiling the element with $compile, which will cause an infinite loop unless you resort to some workaround. There is an easier way to take care of this: declare a directive template and AngularJS will handle the directives properly.
myApp.directive('ngTooltip', function () {
return{
restrict: 'A',
template: '<input tooltip tooltip-placement="bottom" tooltip-trigger="focus">',
replace: true,
link: function (scope, element, attrs) {
attrs.$set('tooltip', attrs['placeholder']);
}
}
});
Working example: plunker.