Angular js : Unable to create directive - javascript

I am trying to create directive but my directive function is not getting called.
index.html
<div ng-repeat="que in questions.Cars">
<question-dir>print from direcetive</question-dir>
</div>
ConytrolDirective.js
(function () {
"use strict";
angular
.module("autoQuote")
.directive('questionDir',["questionDir"] );
function questionDir()
{
return {
template : "<h1>Made by a directive!</h1>"
};
}
}());

There are several mistakes in your code.
You should have function name instead of "questionDir"
.directive('questionDir',[questionDir] );
Use kebab case(- separated words) while using directive name
`<question-dir>print from direcetive</question-dir>`
Additionally you need to refer <script src="controlDirectives.js"></script> on your index.html page.
Demo here

I don't see many mistakes in your code but it would have helped many to stop assuming only if you had posted your code sample link too.
But one primary change you have to make is : Change your directive definition function invoking from
.directive('questionDir', ["questionDir"])
to
.directive('questionDir', questionDir)
See this working fiddle which has just added questions.Cars into $rootScope to make answer look more relevant to your query.
To preserve content of your directive element :
What i see in your question is : Element <question-dir> has child content / inner text print from direcetive in it and it gets overridden by Directive Element <h1>Made by a directive!</h1> completely.
That's the default nature of Angular : Element's (to which the directive is being applied) children will be lost to Directive Element. If you wanted to perserve the element's original content/children you would have to translude it.
Use transclude : true in Directive Definition Object and add <div ng-transclude></div> where you want directive Content / text print from direcetive to be included.
Code snippet :
function questionDir() {
return {
transclude : true,
template: "<div ng-transclude></div><h1>Made by a directive!</h1>"
};
}
Check this one for transclude changes.
Check these 2 links for more information on Transclude :
Understanding the transclude option of directive definition
Understanding transclude with replace

You had a few things wrong in you code:
In your IIFE closure you need to pass in angular
You need to normalize the directive name to kabab case in your html element name: `'
You need to include the reference to the directory factory in your directive registration: .directive('questionDir', questionDir);
Missing module dependencies array: .module("autoQuote", []) -- you should only have this defined once with the dependancies, [] so ignore this if you already have it elsewhere in your code.
Missing restrict in the Directive Definition Object: restrict: 'E',
If you click on Run code snippet you will see this directive now works.
(function (angular) {
"use strict";
angular
.module("autoQuote", [])
.directive('questionDir', questionDir);
function questionDir()
{
return {
restrict: 'E',
template: "<h1>Made by a directive!</h1>"
};
}
}(angular));
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="autoQuote">
<question-dir>print from direcetive</question-dir>
</div>
Finally, if you are using AngularJS 1.5 or greater you should consider using the component syntax for this.

Related

Directives in Angular

I have this directive view here in my code:
<div class="busy-indicator angular-animate" ng-show="busy"></div>
<div class="breadcrumblist" ng-class="atTopLevel ? ['at-top-level'] : null">
<div class="breadcrumblist-display angular-animate">
<label id="searchBox">Search</label><input class="c-context-menu-container t-menu-background c-context-menu-text" type="text" autocomplete="off" id="IdSearch" ng-model = "searchText.Name">
<div class="breadcrumblist-parents">
<div ng-show="::parents && parents.length >= 1"
ng-repeat="parentLink in parents"
class="breadcrumblist-parent-link t-breadcrumb--parent-bgcolor t-border--bottom-grey48"
ng-click="navUpToParent(parentLink)"
ng-class="{'selected': isSelected(parentLink.object), 'draggable': isDraggable(parentLink.object)}"
data-index="{{$index}}">
</div>
But, the searchBox is appearing for all places on my app but I want to make it appear just for one directive in particular. What should I do? I tought about make a scope variable to just "ng-show" this particular searchbox with a condition, but I don't know exactly how to do that, can you help me?
Until you give more information to your specific problem, here are some possibly relevant things you can do to debug your problem and find a solution
the searchBox is appearing for all places on my app but I want to make it appear just for one directive in particular
The combination of the name of the directive + its restriction might cause the directive to appear in unwanted locations.
More information on restrict if needed.
So for example, if you have a directive called 'breadcrumblistParentLink', which is restricted to C (class) - you might find it in undesired locations since you're also using this class to style some elements on your page.
If that's the case, you might find it helpful restricting directives to attributes and elements and giving some unique names.
I would also like to refer to your question
just "ng-show" this particular searchbox with a condition, but I don't know exactly how to do that
If you want a specific behavior for one instance of a directive, there are multiple ways to do that.
Most common is passing an attribute. For example, this is how you use it
<div my-awesome-directive show-searchbox="true"></div>
And this is how you'd put show on the directive scope
angular.module('myApp',[]);
angular.module('myApp').directive('myAwesomeDirective', function(){
return {
template: 'this is my template <span ng-show="showSearchBox">This is search box</span>',
restrict: 'A',
scope: {
showSearchBox: '<'
},
link: function(scope, element, attrs){
console.log(scope.showSearchBox);
}
}
})
You can play around with it here: https://plnkr.co/edit/4MdNeEafbZjq2kEFKYAl?p=preview
You can also look directive at attrs variable (attrs.showSearchBox) and spare the binding in some cases.
For example:
angular.module('myApp',[]);
angular.module('myApp').directive('myAwesomeDirective', function(){
return {
template: 'this is my template <span ng-show="showSearchBox()">This is search box</span>',
restrict: 'A',
scope: {
},
link: function(scope, element, attrs){
scope.showSearchBox = function(){
return attrs.showSearchBox;
}
}
}
})
Note I am using a function on the scope.
This function can also refer to the DOM and do something like $(element).is('[show-search-box]') if you are more comfortable with jquery - but it is highly recommended to stick to angular way of doing things.

$compile doesn't work inside Angular directive

with this Angular directive every time my model changes, new HTML item appended to the page:
app.directive('helloWorld', function($compile) {
return {
restrict: 'AE',
replace: true,
scope:{
arrayItem: '=ngModel'
},
link: function ($scope, ele, attrs) {
$scope.$watch( 'ngModel' , function(){
ele.html('<div ng-click="sendLike({{arrayItem.data.timeline_content}})" class="timeline-item"> Hello {{arrayItem2.data.timeline_content}} </div>');
$compile(ele.contents())($scope);
});
}
};
});
And this is HTML view:
<hello-world ng-repeat="arrayItem in arr" ng-model="arrayItem"></hello-world>
But ng-click inside dynamically generated HTML doesn't work. actually recompiling of this new added section does not works.
UPDATE:
this is what i want to achieve:
in fact i want to create a chat Application. messages stored inside an Array and i have to bind that array to the HTML view. if i click on every message, i need to an alert() fired inside the controller. my controller is like this:
app.controller("timelineCtrl", function ($scope) {
$scope.arr={};
$scope.sendLike = function (id) {
alert(id);
};
.
.
.
}
in the jQuery way, i simply use DOM manipulation methods and add new tags for each message but in Angular way i have to bind that array as a ng-model or something like that.
at first glance, i realize that designing a directive should be a good idea and inside module i can access to main scope and do needed thing with that directive and i expect that changes inside that directive should projected to HTML view but it fails and things like ng-click doesn't work for dynamically created tags.
There are two ways that you could accomplish this, with or without a directive.
Let's start without a directive; we'll assume that you have an array in the controller.
<div ng-controller="timelineCtrl" class="timelineframe">
<div ng-repeat="post in timeline | orderBy:'-lineNumber'" class="post">
<div ng-click="sendAlert(post)">
<span class="postnumber">{{::post.lineNumber}}:</span>
<span class="message">{{::post.message}}</span>
</div>
</div>
</div>
whenever an object is added to $scope.timeline, it can be assigned a lineNumber, and we can use angular OrderBy to sort the direction in reverse lineNumber order (using -). The $scope.sendAlert(post) will send the specific post to the function. in our bindings, we use :: to indicate that these are one time bindings, i.e. not values that need to be monitored independently of the array. This can improve performance on large lists.
using a Directive, we can accomplish this in a very similar manner, by making a Directive that renders a specific post, and passing the post in as a property.
app.directive('timelinePost', function() {
return {
restrict: 'AE',
scope:{
post: '='
},
template: '<div ng-click="postCtrl.sendAlert()">
<span class="postnumber">{{::postCtrl.post.lineNumber}}:</span>
<span class="message">{{::postCtrl.post.message}}</span>
</div>',
controller: 'postController',
controllerAs: 'postCtrl',
bindToController: true
};
app.controller("postController", function(){
var self = this; //save reference to this
self.sendAlert = function(){
//the specific post is available here as self.post, due to bindToController
};
};
//usage in HTML:
<div ng-controller="timelineCtrl" class="timelineframe">
<div ng-repeat="post in timeline | orderBy:'-lineNumber'" class="post">
<timeline-post post='post'></timeline-post>
</div>
</div>
you could further wrap this timeline in a directive in a similar manner, if you so desired. Either of these will accomplish the same task, of looping through your data, ordering it so that the newest post is at the top, and updating whenever the array changes. In the non-directive method, the timelineCtrl handles the $scope.sendAlert function; in the directive method, it is handled by the directive controller postController.
Please note, this is a rough draft based on what you have described and the information from various posts over the last 2 days. I haven't created a dataset to iterate through to test thoroughly, but the logic here should get you started.

AngularJS- How to call controller's function on click of checkbox which is created in directive template

I am new to angularJS. I want to create checkboxes dynamically using templates in directives. I created controller and directives in separate files. I am creating checkbox in template in directive and want to invoke controller's function on ng-click of check box but I am unable to do so.
Here is my code sample.
Controller:
var app=angular.module('abc',[]);
app.controller('DemoCtrl', function($scope) {
$scope.ctrlFn = function(test) {
alert("hi "+test);
console.log(test);
}
});
I referred the https://github.com/iVantage/angular-ivh-treeview to create checkboxes tree view. I inlcuded all the css and js files in my sample. From the link I got the following js file which is creating the checkboxes in template in directive as shown below:
ivh-treeview.min.js:
angular.module("ivh.treeview",[]),
angular.module("ivh.treeview").directive("ivhTreeviewCheckbox",[function(){
"use strict";
return{restrict:"A",
scope:{node:"=ivhTreeviewCheckbox"},
require:"^ivhTreeview",
link:function(a,b,c,d){
var e=a.node,f=d.opts(),g=f.indeterminateAttribute,h=f.selectedAttribute;
a.isSelected=e[h],
a.ctrl=d,
a.$watch(function(){return e[h]},function(b){a.isSelected=b}),
a.$watch(function(){return e[g]},function(a){b.find("input").prop("indeterminate",a)})},
template:['<input type="checkbox"','ng-model="isSelected"','ng-change="ctrl.select(node, isSelected)" />'].join("\n")}
}]);
View:
<div class="col-sm-8" ng-controller="DemoCtrl as demo">
<div ivh-treeview="demo.bag"
ivh-treeview-selected-attribute="'isSelected'"
ivh-treeview-id-attribute="'uuid'"
ivh-treeview-expand-to-depth="0">
</div>
</div>
I want to call ctrlFn() on click of checkbox created in directive template. Please suggest a way to do the same.
Thanks In Advance
Sounds like you are looking for a two-way binding between directive and parent $scope.
// parent controller scope
$scope.person = { name:'coldstar', class:'sexy beast' };
// directive declaration
<div a-person='person'></div>
// directive code
scope: {
// the = sign is the key. could also be a function instead of object
innerPerson: "=aPerson"
},
link: function (scope, elm, attr){
// now this change will be reflect in the parent controller also
scope.innerPerson.name = "not coldstar anymore";
}
Edit:
I also noticed "'isSelected'" which should be "isSelected" if isSelected is a $scope.* entity

Use ng-show and etc. in directive "A"

I saw related asks, but dont understand anyway.
If I have directive: http://pastebin.com/QtAzGv62
And I need to add "ng-show" (or any other standart angular directive) functional to this directive (for related DOM element, that is ), that must depends on AuthService option (named "logged").
How?! :)
Since you are not binding to a particular 'element' you just need to make the methods available to your directive scope so change
elem.bind('click', function() {
AuthService.tryLogin();
});
to
scope.loggedIn = false;
scope.tryLogin = function(){
AuthService.tryLogin();
scope.loggedIn = true;
}
Then you can do in your directive html
<div ng-show="loggedIn">You dun logged in man!</div>

How to hide an element after $compile?

There is a directive:
.directive('location', function () {
return {
restrict : 'A',
scope : {},
replace : true,
templateUrl: 'common/components/location/location.html',
link : function (scope, element, attr) {……}
}
});
So it is used in another directive:
var scope = $rootScope.$new(true);
var directive = $compile('<div location></div>')(scope);
$document.find('body').append(directive);
directive.hide(); - not working
How to hide directive generated html after inserting it to body? Directive has "replace" set to true
http://plnkr.co/edit/e7fNua?p=preview
The line you've marked as wrong is actually working meaning that after directive.hide() if you print the element on console you'll see:
console.log(directive[0]);
-> <div location="" class="ng-scope" style="display: none;"></div>
This one is a bit tricky but easy if you carefully follow what you told angular to perform. Let's go through it step by step
$compile('<div location></div>')(scope); - here you're telling angular to compile given template and link it with given scope. Since you have specified templateUrl that must be fetched angular has not yet completed processing this element.
$document.find('body').append(directive); directive.hide(); - append the element to body and immediately hide it. If you print the directive[0] to console you'll see: <div location="" class="ng-scope" style="display: none;"></div>
Note that there is still no <section><p>Text</p></section> inside DOM
Angular completes fetching location.html template.
Since you have replace: true inside directive declaration the directive[0] is removed from DOM and replaced by compiled <section><p>Text</p></section>.
As a result you still see Text on page.
You can change this behaviour in various ways:
change directive declaration to replace: false
use inline template: '<section><p>Text</p></section>'
wrap template that you pass to $compile in another element: $compile('<div class="wrapped"><div location></div></div>')

Categories

Resources