AngularJS | Passing directive's name as an attribute to another directive - javascript

In my markup I have a container, and two buttons inside of it. Each button opens the same popup window but with different content. Container and buttons are made with directives and the popup - with service. The HTML:
<div my-container>
<my-button ng-click="openPopup()" popupContent="<popup-content1></popup-content1>"></my-button>
<my-button ng-click="openPopup()" popupContent="<popup-content2></popup-content1>"></my-button>
<my-popup></my-popup>
</div>
As you can see I try to add some content to the popup window as an attribute and the value of the attribute is the name of a directive that has to be inserted in the DOM.
First, I get the attributes in my-button directive:
//...
link: function(scope, elm, attrs) {
scope.openPopup()(true, attrs.popupContent);
}
//...
Then I pass the value of the attribute to my-container directive's controller to scope:
//...
$scope.openPopup = function(msg, content) {
//...
$scope.popupContent = content;
};
And after that I try to get the attribute's value in the popup's template:
<header> .... </header>
<ng-transclude>{{ popupContent }}</ng-transclude>
<footer> ... </footer>
So, the problem is that I got strings like
<popup-content1></popup-content1>
in my popup instead of the real content like "Lorem ipsum...." (which is in my popup-content1 and popup-content2 directives templates).
How can I get the 'real' content into my popup?
Many thanks in advance!

Related

Passing Attribute Value to Directive Template

End Goal
We'd like to have an attribute we can add to any element which we would associate to a contextual help dialog so that when we hover the mouse cursor over an element, a pop-up will show the help information. Here's an example of what we want to do:
<label help-info="This is what will be displayed in the help dialog">Help Example</label>
I'm having some trouble correctly passing the string parameter to the template though.
Here's what I've tried:
Index.html
<html ng-app="myApp">
<head>
<script data-require="angular.js#*" data-semver="2.0.0-alpha.20" src="https://code.angularjs.org/2.0.0-alpha.20/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="HelpInfoDirective.js"></script>
</head>
<body ng-controller="myCtrl">
<div help-info="test"></div>
</body>
</html>
HelpInfoTemplate.html
<div>
{{'this is a' + _attrs.help-info}}
</div>
HelpInfoDirective.js
(function(){
var app = angular.module('myApp', []);
app.directive('helpInfo', function(){
return{
restrict: 'A',
scope: true,
templateUrl: function(_elements, _attrs){
'HelpInfoTemplate.html'
}
// link: function(_scope, _elements, _attrs){
// _scope.helpAttr = _attrs.help-info;
// }
}
});
})();
First, I tried passing the parameter by using the link parameter and it didn't work so I tried placing the template into a function with no luck.
What is the correct way to pass an attribute's value to a template?
Also, once we have the value, how can we modify the returned template so we can use ng-show to show the pop-up (we'll place it into a modal eventually). I'm open to suggestions.
Link to the Plunk
What is the correct way to pass an attribute's value to a template?
Binding values to templates
Values are bound to templates through the compilation process. You can compile code in the link function of your directive using the $compileservice and providing the scope that the template needs to bind to:
app.directive('helpInfo', function($compile){
return{
restrict: 'A',
scope: {title : '#'},
link: function(scope, el){
var newElement = $compile('<div>{{title}}</div>')(scope);
el.hover(function(){
el.after(newElement);
});
el.mouseout(function(){
el.nextAll().first().remove();
});
}
}
});
Defining the scope for your directive
You'll need to setup an isolated scope for the directive an specify the name of the attribute where you'll define the text shown in your popup. I've used 'title' as the attribute in this example.
You can then use the directive like this:
<div help-info title="I am a title">Click me</div>
Demo
Here's a Plunker that shows this in action.
http://plnkr.co/edit/VwdHC3l9b3qJ4PF6cGV1?p=preview
Also, once we have the value, how can we modify the returned template so we can use ng-show to show the pop-up
In the example I provided I used the jQuery hover() and mouseout() events to respond to users hovering over and away from the DOM element. If you want to take this further, here's a tutorial that shows how to put popups or alerts into services.
You need to assign it like this:
app.directive('helpInfo', function(){
return{
restrict: 'A',
scope: true,
template: '<div>{{helpAttr}}</div>',
link: function(_scope, _elements, _attrs){
_scope.helpAttr = _attrs.helpInfo;
}
}
});
Demo
You can just use your own template and insert {{helpAttr}} binding wherever you need it.

Creating directive to part of template which is shown after a click

I've got a template with two kind of article-container: Viewer and Editor:
<article ng-if="!editor" ng-controller="article">
<div>Some content</div>
</article>
<article ng-if="editor" ng-controller="article">
<div mySharedScope></div>
</article>
While clicking the button the user can switch between those two container:
<button ng-click="editor = !editor" ng-bind="editor ? 'Abort' : 'Edit'"></button>
Now I want to create a directive on the second container. So this is what I did:
app.directive('mySharedScope', function () {
return {
template: 'New content'
};
});
But something is missing, as this doesn't work.
I want to use a directive to do some DOM mainpulation link: function ($scope, element, attrs) { }
Two things, first is that the directive mySharedScope will transform in it's directive definition from camel case
<div mySharedScope></div>
to a dashes like so
<div my-shared-scope></div>
After you switch that out, you'll need to make sure you're translcuding content nested inside of your first directive (article), and placing the ng-transclude directive inside of its template.
see docs for this on angular website
as a basic implementation of this, i've created a fiddle with the two of your two directives that appropriately switch when a button is triggered. The content is transcluded here, so feel free to cherry pick what you need from it.
https://jsfiddle.net/wvty8rpc/2/
A directive named 'mySharedScope' translates to attribute 'my-shared-scope':
<article ng-if="editor" ng-controller="article">
<div my-shared-scope></div>
</article>

How to load <script> inside AngularJS Application

I need to add google preview wizard api (https://developers.google.com/books/docs/preview-wizard) script inside my angularJS application.
<script type="text/javascript"> GBS_insertEmbeddedViewer('ISBN:0738531367',600,500);</script>
When I try to do this directly (or inside a div tag) inside a controller, it over writes the whole web page and just displays the preview button.
Same is the scenario with I try to load this piece of script with directives. (AEC)
Could not use ng-templates as keeping a script anything inside ng-template didn't make sense.
Closest I ever got to the problem is when I try to load this piece of script in the main index.html page. It gets loaded, but I'm not able to pass a variable ISBN in to the script (even keeping the isbn attached to scope ).
Data binding with the controller is not happening when ever the variable is inside the script tag
Please suggest a way out of this.
angular has directives you can build one like this
angular.module('app')
.directive('embedViewer', ['$compile',function($compile) {
return {
restrict: 'AE',
template: '<div></div',
replace: true,
scope: {
isbn: '=embedViewer'
},
link: function(scope, el, attrs) {
var rawTmpl = '<iframe src="books.google.com/books/previewlib.js">; <script type="text/javascript"> GBS_insertEmbeddedViewer("{{isbn}}",600,500);</script> </iframe>';
scope.$watch('isbn', function(val) {
if (val) {
var template = $compile(rawTmpl)(scope);
el.html(template);
}
})
}
}
}]);
and you can use in any template like
<div embed-viewer="book.isbn"></div>
<!-- OR -->
<div data-embed-viewer="book.isbn"></div>
<!-- OR -->
<embed-viewer="book.isbn"></embed-viewer>
this code isnt tested

Template inside of a directive

I have a strange situation where I need to put a template inside of a template in my directive. The reason for this is that AngularJS will not read ng-repeat code inside of attributes.
In an ideal world, this is what my code would look like:
<div ng-repeat="phone in phones">
<button popover="<div ng-repeat='friend in phone.friends'>{{friend.name}}</div>"></button>
</div>
Unfortunately this does not work because of the quotes around the popover attribute. This has led me down a pretty deep rabbit hole where I'm trying to put a template inside of a template like in this plunker:
http://plnkr.co/edit/ZA1uA1UOlU3cCH2mbE0X?p=preview
HTML:
<div my-popover></div>
Javascript:
angularApp.directive('myPopover', function( $compile) {
var getTemplate = function()
{
var scope = {title: "other title"};
var template = "<div> test template. title: {{title}}</div> ";
return $compile(template)(scope);
}
return {
restrict: 'A',
template: '<button class="btn btn-default" popover="{{content}}" popover-title="title">template</button>',
link: function(scope) {
scope.content = getTemplate();
}
};
})
Unfortunately this does not work because AngularJs complains about a circular reference. Please help! (this has been taking me all day)
I'm not sure I understand exactly what you are trying to achieve, but from the look of it you might want check out the transclude option for directives.
From the docs:
use transclude: true when you want to create a directive that wraps
arbitrary content.
If you use transclude, you can store the popover content inside the button, and "forward" that content to where you want it using the ng-transclude directive.
Your code would then look something like this:
<button>
<div ng-repeat='friend in phone.friends'>{{friend.name}}</div>
</button>
You can see some examples in action in the guide to directives.

Angularjs directive removes all content from parent div

I have custom directive declaration with replace set to true.
When I place it in html with separate close tag:
<div>
<search-box search="search(query)" query="query" ></search-box>
<div class="dataDiv">
<!--other div elements -->
</div>
</div>
everything works as expected.
But after rewriting html in this way:
<div>
<search-box search="search(query)" query="query" />
<div class="dataDiv">
<!--other div elements -->
</div>
</div>
directive completely replaces all parent div content with it's template and removes dataDiv from resulting html page.
Is this expected angular behavior or something that can be changed in directive declaration?
Directive:
function SearchBox() {
return {
restrict: 'E',
replace: true,
template: '...',
scope: {
query: '='
},
link: function($scope, $element){
...
},
controller: function ($scope) {
...
}
}
}
Depending on your doctype you might be giving the wrong meaning to the "closing" slash.
by default, yeoman generator and angular-seed use <!doctype html> (html5)
In HTML 5, <foo /> means <foo>. The slash is just syntactic sugar
Check this Are (non-void) self-closing tags valid in HTML5?
Depending on the browser implementation, it will add automatically a closing tag in one place or another, just like when you leave bodyor strong open and read the rendered code.

Categories

Resources