Angular directive works with inline style but not with class - angularjs

I have this directive:
angular.module('exampleApp').directive('exampleDirective', ['$document', '$window', function($document, $window) {
return {
restrict: 'E',
scope: false,
template: '<div class="foo"></div>',
link: function($scope, $element, $attrs) {
var targetElement = angular.element(document.querySelector($attrs['targetSelector']));
console.log(targetElement.css('height'));
}
}
]);
Which works fine with this html:
<div id="div1" style="height: 100px">A div</div>
<example-directive target-selector="#div1" />
Because in the console I see: "100px" printed out
But with this html:
<style>
.div-example {
height: 100px;
}
</style>
<div id="div2" class="div-example">A div</div>
<example-directive target-selector="#div2" />
Which is basically the same but without inline-style, the directive is simply not working. The console is printing: "" (empty string).
How do I solve this?
I tried scope: true but it didn't work either.
Is targetElement.css('height') only good for inline style?

When you do .css('height'), jQuery can tell you only the values set in the element's style property or attribute.
If you want to know the actual height of an element, try with
targetElement[0].clientHeight
Or with jQuery:
targetElement.height()
See the difference explained a bit better here: https://api.jquery.com/height/

Related

angular bind html tags from controller to html view

I need to render the $scope.htmlView tags in to html view.
I already tried using ng-bind-html. It renders the html tags but scope variable values will not appear.
How can I render both html tags and and scope variable values?
This is the controller:
$scope.newObj = {
billStatus : true;
eventTime : "2015-01-10"
};
$scope.htmlView = '<p>{{newObj.eventTime}}</p> <div style="margin-top: -15px;"><md-checkbox ng-checked="{{newObj.billStatus}}" style="margin-left: 0px;" aria-label="Bilable"><span style="margin-left:0px;">Bilable</span> </md-checkbox></div>'
Expected result is:
<p> 2015-01-10</p>
<div style="margin-top: -15px;">
<md-checkbox ng-checked="true" style="margin-left: 0px;" aria- label="Bilable">
<span style="margin-left:0px;">Bilable</span>
</md-checkbox>
</div>
I search over the internet over days and still could't find out a way to figure out this. please help me. thank you.
You have to do 2 things.
Use data-ng-bind-html=""
Use $sce.trustAsHtml(string)
UPDATED:
If you wont to use angular expressions, you have to compile them using
$compile.
You can read more via this $SCE
I will tell you a long way but it will help you.Make a custom directive like this.
app.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
});
Use as
<span dynamic="{{htmlView}}" >
Hi please check this fiddle
https://plnkr.co/edit/iqNltdDYv2n9Agke0C2C?p=preview
HTML
<div ng-controller="ExampleController">
<p >{{newObj.eventTime}}</p>
<p dynamic="htmlView"></p>
</div
and JS
(function(angular) {
'use strict';
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.newObj = {
billStatus : true,
eventTime : "2015-01-10"
}
$scope.htmlView = '<p> {{newObj.eventTime}}</p> <div style="margin-top: -15px;">Hello <md-checkbox ng-checked="{{newObj.billStatus}}" style="margin-left: 0px;" aria-label="Bilable"><span style="margin-left:0px;">Bilable</span> </md-checkbox></div>'
}])
.directive('dynamic', function($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, element, attrs) {
scope.$watch(attrs.dynamic, function(html) {
element[0].innerHTML = html;
$compile(element.contents())(scope);
});
}
};
});
})(window.angular);

Angular Directive: Isolate Scope & Transclude is true, but HTML in the directive needs to continue to bind to parent $scope

To start, I set up a JSFiddle here: http://jsfiddle.net/qLagkgt5/3/
I hope the title is clear, but basically, I am trying to use directives to help in repeatable html. In the example on JSFiddle, I am using a box directive with options for spacing and box-type.
The html that I am turning into repeatable code:
<div class="box">
<div class="box-inner">
{{CONTENT GOES HERE}}
</div>
</div>
With optional classes:
<div class="box spacing-small box-rounded">
<div class="box-inner">
{{CONTENT GOES HERE}}
</div>
</div>
What I'd like to be able to do is:
<box spacing="small" box-type="rounded">
{{CONTENT GOES HERE}}
</box>
This is obviously a very simplified set of html that doesn't necessarily need a directive, but it is just an example to illustrate what I am running into.
Now to the angular side of things...
Here is my controller:
app.controller("Ctrl", ["$scope", function($scope) {
$scope.text = "Starting Text... FOOBAR!";
}]);
And here is my directive:
app.directive("box", function() {
return {
restrict: "E",
transclude: true,
replace: true,
scope: {
spacing: "#",
boxType: "#"
},
template: '<div class="box" ng-class="{\'spacing-{{spacing}}\' : spacing, \'box-{{boxType}}\' : boxType}"> <div class="box-inner" ng-transclude></div></div>'
}
});
If I now place my directive inside html with a controller like this:
<div class="wrap" ng-controller="controller">
{{text}}
<box spacing="small">
<input ng-model="text"/>
</box>
</div>
The $scope.text that is outside the <box> is never updated when I change the input inside the box.
How do I make it so that when this directive is used, the content inside the box goes up to the parent scope rather then the isolated scope?
If I nest a box inside another box, can I also have it go up to the same parent scope?
Thanks!
I read something here on stackoverflow that immediately jumped in my head when I read your post. It said something like "If you do it without a dot you are doing it wrong..." I'll search for the article and post it here as soon as I found it but for now I think I "fixed" your code:
<div ng-app="app" ng-controller="Ctrl">
<h1><span class="grey">$scope.data.text:</span> {{data.text}}</h1>
<box spacing="large" box-type="rounded">
<h2><span class="grey">$scope.text in box directive:</span> {{data.text}}</h2>
<input ng-model="data.text" />
</box>
<box spacing="small" box-type="rounded-shadow">
<h2><span class="grey">$scope.text in different box directive:</span> {{data.text}}</h2>
<input ng-model="data.text" />
</box>
</div>
var app = angular.module("app", []);
app.controller("Ctrl", ["$scope", function($scope) {
$scope.data = {};
$scope.data.text = "Starting Text... FOOBAR!";
}]);
app.directive("box", function() {
return {
restrict: "E",
transclude: true,
replace: true,
scope: {
spacing: "#",
boxType: "#"
},
template: '<div class="box" ng-class="{\'spacing-{{spacing}}\' : spacing, \'box-{{boxType}}\' : boxType}"> <div class="box-inner" ng-transclude></div></div>'
}
});
You have to create an object and use this for databinding. I am now using "data.text" and do the binding with this expression.
Cheers,
Tim.
P.S.: There are a lot of links.
To mention only two:
AngularJS: If you are not using a .(dot) in your models you are doing it wrong?
AngularJS: dot in ng-model

Using ng-switch in directive with transclude

I am trying to create a template that shows some transcluded content. When I use ng-show everything works fine, but using ng-if or ng-switch gives me problems. I get this error message: Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found
I understand that ng-switch creates a new scope. But the transclude should still go up to the parent chain. Is this a defect in angularjs? See http://jsfiddle.net/HgvP7/
Here is my html, modified from the documentation example:
<div ng-app="docsTransclusionExample">
<div ng-controller="Ctrl">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
<!-- my-dialog.html -->
<script type="text/ng-template" id="my-dialog.html">
<div ng-switch="1+1">
<div ng-switch-when="2">
<div ng-transclude></div>
</div>
</div>
</script>
</div>
And the code:
angular.module('docsTransclusionExample', [])
.controller('Ctrl', function($scope) {
$scope.name = 'Tobias';
})
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'my-dialog.html',
link: function (scope, element) {
scope.name = 'Jeff';
}
};
});

How to pass HTML to angular directive?

I am trying to create an angular directive with a template, but I also don't want to lose the HTML inside of the div. For example, here is how I would like to call my directive from HTML:
<div my-dir>
<div class="contents-i-want-to-keep"></div>
</div>
Then, there is my directive:
app.directive('myDir', [ '$compile', function($compile) {
return {
restrict: 'E',
link: function(scope, iElement, iAttrs){
// assigning things from iAttrs to scope goes here
},
scope: '#',
replace: false,
templateUrl: 'myDir.html'
};
}]);
and then there is myDir.html, where I define a new element:
<div class="example" style="background: blue; height: 30px; width: 30px"></div>
Even when I set replace to false, I lose the inner contents-i-want-to-keep div - my understanding of the angular docs was that this would be appended after my template. Is there some way to preserve this (possibly through my linking function?) so that the result will be
<div class="example" style="background: blue; height: 30px; width: 30px">
<div class="contents-i-want-to-keep"></div>
</div>
Thanks!
You'll need to use ng-transclude, add transclude: true in your directive options, and add ng-transclude to your template:
<div class="example" style="…" ng-transclude></div>`
This plunkr has an example on how to use ng-transclude with a template, to keep the original dom element.
http://plnkr.co/edit/efyAiTmcKjnhRZMTlNGf

Replace ng-include node with template?

Kinda new to angular. Is it possible to replace the ng-include node with the contents of the included template? For example, with:
<div ng-app>
<script type="text/ng-template" id="test.html">
<p>Test</p>
</script>
<div ng-include src="'test.html'"></div>
</div>
The generated html is:
<div ng-app>
<script type="text/ng-template" id="test.html">
<p>Test</p>
</script>
<div ng-include src="'test.html'">
<span class="ng-scope"> </span>
<p>Test</p>
<span class="ng-scope"> </span>
</div>
</div>
But what I want is:
<div ng-app>
<script type="text/ng-template" id="test.html">
<p>Test</p>
</script>
<p>Test</p>
</div>
I had this same issue and still wanted the features of ng-include to include a dynamic template. I was building a dynamic Bootstrap toolbar and I needed the cleaner markup for the CSS styles to be applied properly.
Here is the solution that I came up with for those who are interested:
HTML:
<div ng-include src="dynamicTemplatePath" include-replace></div>
Custom Directive:
app.directive('includeReplace', function () {
return {
require: 'ngInclude',
restrict: 'A', /* optional */
link: function (scope, el, attrs) {
el.replaceWith(el.children());
}
};
});
If this solution were used in the example above, setting scope.dynamicTemplatePath to 'test.html' would result in the desired markup.
So thanks to #user1737909, I've realized that ng-include is not the way to go. Directives are the better approach and more explicit.
var App = angular.module('app', []);
App.directive('blah', function() {
return {
replace: true,
restrict: 'E',
templateUrl: "test.html"
};
});
In html:
<blah></blah>
I had the same problem, my 3rd party css stylesheet didn't like the extra DOM-element.
My solution was super-simple. Just move the ng-include 1 up. So instead of
<md-sidenav flex class="md-whiteframe-z3" md-component-id="left" md-is-locked-open="$media('gt-md')">
<div ng-include="myService.template"></span>
</md-sidenav>
I simply did:
<md-sidenav flex class="md-whiteframe-z3" md-component-id="left" md-is-locked-open="$media('gt-md')" ng-include="myService.template">
</md-sidenav>
I bet this will work in most situations, even tho it technically isn't what the question is asking.
Another alternative is to write your own simple replace/include directive e.g.
.directive('myReplace', function () {
return {
replace: true,
restrict: 'A',
templateUrl: function (iElement, iAttrs) {
if (!iAttrs.myReplace) throw new Error("my-replace: template url must be provided");
return iAttrs.myReplace;
}
};
});
This would then be used as follows:
<div my-replace="test.html"></div>
This is the correct way of replacing the children
angular.module('common').directive('includeReplace', function () {
return {
require: 'ngInclude',
restrict: 'A',
compile: function (tElement, tAttrs) {
tElement.replaceWith(tElement.children());
return {
post : angular.noop
};
}
};
});
Following directive extends ng-include native directive functionality.
It adds an event listener to replace the original element when content is ready and loaded.
Use it in the original way, just add "replace" attribute:
<ng-include src="'src.html'" replace></ng-include>
or with attribute notation:
<div ng-include="'src.html'" replace></div>
Here is the directive (remember to include 'include-replace' module as dependency):
angular.module('include-replace', []).directive('ngInclude', function () {
return {
priority: 1000,
link: function($scope, $element, $attrs){
if($attrs.replace !== undefined){
var src = $scope.$eval($attrs.ngInclude || $attrs.src);
var unbind = $scope.$on('$includeContentLoaded', function($event, loaded_src){
if(src === loaded_src){
$element.next().replaceWith($element.next().children());
unbind();
};
});
}
}
};
});
I would go with a safer solution than the one provided by #Brady Isom.
I prefer to rely on the onload option given by ng-include to make sure the template is loaded before trying to remove it.
.directive('foo', [function () {
return {
restrict: 'E', //Or whatever you need
scope: true,
template: '<ng-include src="someTemplate.html" onload="replace()"></ng-include>',
link: function (scope, elem) {
scope.replace = function () {
elem.replaceWith(elem.children());
};
}
};
}])
No need for a second directive since everything is handled within the first one.

Resources