How to hide an element after $compile? - javascript

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>')

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.

Return value of an Angular Directive and based on value have ng-if to display content

I have a Directive that returns a 0 or 1
<my-code inputcode="{{ data.valBit }}"></my-code>
So data.valBit will be a 0 or 1
If it is a 1 , then I want it to display an HTML section of code, how can I do that? Problem with returning all the html from the directive is that it is put of a form along with ngmessage validation , so it is not fun to pass form or mess with link or compiler
<div ng-if="data.valBit == 1">
part of my form, with ngmessage etc... $error etc...
.......
</div>
Is this possible to get a callback from a directive that I can evaluate and decide whether to show or hide a section of html?
Most simple way to do it is taking advantage of two way data binding.
In Your case, the problem is that your are passing to the directive the evaluated value instead of the variable. Just remove the braces in your directive argument <my-code inputcode="data.valBit"></my-code>.
Example: (PLUNKER)
HTML
<my-code inputcode="data.valBit"></my-code>
<p ng-if="data.valBit == 1">
part of my form, with ngmessage etc... $erroretc...
</p>
DIRECTIVE
app.directive('myCode', function() {
return {
restrict: 'E',
scope: {
inputcode: "=" //two way binding
},
link: function(scope, element, attrs){
scope.inputcode = 1;
}
}
})

Angular js : Unable to create directive

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.

get an element attributes values using Angular after using template

I am relatively new to Angular and this my first attempt at using a custom directive. I have a table I am working with and each cell has a data attribute. For example, "data-dept="service", and I would like to access this value before overwriting it with a directive. In my directive, I have set template = true, and this removes my data attribute.
I liked my website that I am working to better explain what I am attempting to do.
http://partnerportal-preprod.automate-webservices.com/list/#/hoursTable
The cells inside the table in the first row are clickable, but I would like to know if the user is clicking on service for example.
Update
I created plunker to better illustrate my problem.
I have a basic table that I am using a directive to replace a row in the table.
In doing so, I lose the attribute that I would need to set it as a key in my object for later use.
http://plnkr.co/edit/oXRM6lRkidnAHfBA4GsR?p=preview
HTML
<tr>
<td name="valueIneed" hello-world>John</td>
<td>Doe</td>
<td>john#example.com</td>
</tr>
Directive
app.directive('helloWorld', function($parse) {
return {
template: '<td><input type="text"></td>',
replace: true,
transclude: 'true',
scope: {
position: '='
},
link: function (scope,element,attrs,ngModel) {
console.log(attrs.attribute);
scope.clickMe = function () {
console.log('clicked');
scope.isChecked = true;
};
}
};
});
First remove replace: true.
Second to see the value of the name attribute:
console.log(attrs.name);
By using replace: true, the directive was replacing the element that had the name attribute.
replace:true is Deprecated1
From the Docs:
replace ([DEPRECATED!], will be removed in next major release - i.e. v2.0)
specify what the template should replace. Defaults to false.
true - the template will replace the directive's element.
false - the template will replace the contents of the directive's element.
-- AngularJS Comprehensive Directive API
From GitHub:
Caitp-- It's deprecated because there are known, very silly problems with replace: true, a number of which can't really be fixed in a reasonable fashion. If you're careful and avoid these problems, then more power to you, but for the benefit of new users, it's easier to just tell them "this will give you a headache, don't do it".
-- AngularJS Issue #7636

$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.

Categories

Resources