How to load <script> inside AngularJS Application - javascript

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

Related

angularjs click event, after appending content [duplicate]

I am working with angularjs 1.2.0-rc.3. I'd like to include html code into a template dynamically. For that I use in the controller :
html = "<div>hello</div>";
$scope.unicTabContent = $sce.trustAsHtml(html);
In the template I got :
<div id="unicTab" ng-bind-html="unicTabContent"></div>
It works fine for regular html code. But when I try to put angular template it is not interpreted, it is just included in the page. For example I'd like to include :
<div ng-controller="formCtrl">
<div ng-repeat="item in content" ng-init="init()">
</div>
</div>
Thanks a lot
This solution doesn't use hardcoded templates, and you can compile Angular expressions embedded within an API response.
Step 1.
Install this directive: https://github.com/incuna/angular-bind-html-compile
Step 2. Include the directive in the module.
angular.module("app", ["angular-bind-html-compile"])
Step 3. Use the directive in the template:
<div bind-html-compile="letterTemplate.content"></div>
Result:
Controller Object
$scope.letter = { user: { name: "John"}}
JSON Response
{ "letterTemplate":[
{ content: "<span>Dear {{letter.user.name}},</span>" }
]}
HTML Output =
<div bind-html-compile="letterTemplate.content">
<span>Dear John,</span>
</div>
For reference sake, here's the relevant directive:
(function () {
'use strict';
var module = angular.module('angular-bind-html-compile', []);
module.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
}());
This is what I've made, no idea if it's the angular wayTM, but it works and is super simple;
.directive('dynamic', function($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, element, attrs) {
scope.$watch(attrs.dynamic, function(html) {
$compile(element.html(html).contents())(scope);
});
}
};
});
So;
<div id="unicTab" dynamic="unicTabContent"></div>
Edit: I found the angular way, and it's super simple.
$templateCache.put('unicTabContent', $sce.trustAsHtml(html));
<div id="unicTab" ng-include="'unicTabContent'"></div>
Don't need to make your own directives or anything.
But it's a bind-once sort of deal, it wont see changes made to your html like the custom directive does.
As Vinod Louis says in his comment, the best way to do that was to use templates. I had to define a template outside of the regular code, for example I added that code inside of my index.html :
<script type="text/ng-template" id="unic_tab_template.html">
<div ng-switch on="page">
<div ng-switch-when="home"><p>{{home}}</p></div>
<div ng-switch-when="form">
<div ng-controller="formCtrl">
<div ng-repeat="item in content">{{item.name}}:{{item.value}}</div>
</div>
</div>
<div ng-switch-default>an error accured</div>
</div>
</script>
This template is conditional, so depending on the value of $scope.page, it switches between the 3 templates (the third being an error handler). To use it I had :
<div id="unicTab" ng-controller="unicTabCtrl">
<div ng-include="'unic_tab_template.html'"></div>
</div>
That way my page changes depending on the $scope inside of my unicTabCtrl controller.
To conclude the idea of inserting angularsjs template seams to be difficult to realize ($compile seams to be the solution, but I wasn't able to make it work). But instead you may use conditional templating.
I was trying to do the same thing and came across this module.
http://ngmodules.org/modules/ng-html-compile
I just included it and then I was able to use "ng-html-compile" instead of "ng-bind-html"
My solution to similar problem in my current app without using template (not elegant, but working):
directive('ngBindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
compile: function compile(tElement, tAttributes, transcludeFn) {
return function postLink(scope, element, attributes) {
scope.$watch(function() {
return scope.$eval(attributes.ngBindHtml);
}, function(newValue, oldValue) {
$compile(element.children())(scope);
});
};
}
};
}]);
It requires ngBindHtml on the same element and compiles the element content after it changes with ngBindHtml.
<div id="unicTab" ng-bind-html="unicTabContent" ng-bind-html-compile></div>
ng-html-compile looks similar but at first glance it won't be recalculated when the template content is changing. But I haven't tried it.
One way is use a directive for purpose of inserting custom templates that include angular expresssions
<div id="unicTab" unic-tab-content></div>
app.directive("unicTabContent",function(){
return {
restrict:"A",
template:'{{unicTabContent}}'
}
})
The code below is much simpler using Angular's built-in $interpolate and $sce objects. First inject the $interpolate and $sce Angular objects into your directive as you do anything custom you need in your directive.
amqApp.directive('myDir', ['$interpolate', '$sce', function ($interpolate,$sce ) {...}
Then create all your scoped variables found in your imported html expressions...
$scope.custom = 'Hello World';
Next use $interpolate to process your custom HTML and its expressions...then make sure you use the $sce object to trust it as HTML before binding...
var html = $interpolate('<b>{{custom}}</b>')($scope);
$scope.data = $sce.trustAsHtml(html);
Finally, in your view, just make sure use an element with the "ng-bind" or "ng-bind-html" on it in your view display. I found the $sce piece wont display the HTML as HTML (sees it as text) if you don't bind it in your html template like this...
<span ng-bind-html="data"></span>
You should see in bold...
Hello World
I used this trick to import in text/HTML with custom angular {{expressions}} from a web.config.
A lot simplified solution of #clement-roblot based on built-in templates.
Controller:
app.controller('TestCtrl', [
'$scope',
'$templateCache',
function ($scope, $templateCache) {
$templateCache.put('test.html', '2 + 2 = {{ 2 + 2 }}');
}
]);
View:
<div ng-include="'test.html'"></div>

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

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!

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.

AngularJS, directive element is null

I'm working on porting a jQuery plugin to AngularJS just because it seems fun.
In the past, when using jQuery, I was using jQuery to manipulate the DOM. So, I have a function in jQuery to load the plugin and in that function is was maniuplating the DOM.
Now, when using AngularJS, I've read that there are directives for that specific purpose, but I don't manage to find the solution.
I have the following html:
<body class="officeui-space-no-margin officeui-space-no-padding">
<!-- Defines the OfficeUI section. In this section, all the contents for the OfficeUI user interface will be written. -->
<div ng-controller="OfficeUIController" id="OfficeUI">
<div class="title officeui-align-center">
<span>Here the title can go.</span>
</div>
<!-- Defines the main holder for the ribbon. -->
<div id="ribbonHolder">
<!-- Render the template for the ribbon. -->
<ng-include src="'Partials/Templates/Ribbon/tabs.html'"></ng-include>
</div>
</div>
<!-- Bottom scripts: Used for Initialization. -->
<script type="text/javascript">
// Initialize the 'ribbonHolder' element as a ribbon.
$('#ribbonHolder').ribbon();
</script>
</body>
You see here that I'm loading a template to render, of which the contents can be found below:
<ul role="tablist" class="officeui-space-no-margin officeui-space-no-padding">
<!-- Render all the tabs in the collection. -->
<tabs-container>
<li role="tab" class="officeui-display-inline-block officeui-align-center" ng-repeat="tab in tabs">{{tab.Name|tabs}}</li>
</tabs-container>
</ul>
In the template above, I do have an element tabs-container which should be my directive.
I've the JavaScript that defines the AngularJS stuff, including registering this directive:
var officeUIApplication = angular.module('OfficeUI.Ribbon.Controllers', []);
officeUIApplication.directive('tabsContainer', function() {
var functionLink = function(scope, element, attrs) {
console.log(element.children());
};
return {
restrict: 'E',
link: functionLink
};
});
According to my very limited knowledge of AngularJS, I'm just learning it, it's in the variable functionLink where I should manipulate the DOM of attach event handlers to specific parts.
But inside functionLink I call the following code:
console.log(element.children());
But in the console I do see that this particular element is empty.
Why is that, and is this the good approach?
Another approach of which I've tought it to include something in jQuery so that the code is only executed after AngularJS has finished it's work, but I don't like that particular idea, on the other hand, how do I create a jQuery plugin that that passes options and event handlers, or isn't that possible anymore and am I in fact creating an AngularJS plugin?
Thanks for the response.
Try this:
officeUIApplication.directive('tabsContainer', function($timeout) {
var functionLink = function(scope, element, attrs) {
$timeout(function() {
element.ribbon();
});
};
return {
restrict: 'E',
link: functionLink
};
});
jQuery plugins usually require the DOM to be ready for it to initialize properly. In angular, there is no real concept of DOM ready. $timeout is executed after the render phase, which is why it works.

Angular expression selecting a directive

I need to render a angular directive, selecting it by appealing to a string previously defined at a variable (usually declared in the controller). Despite such a variable is accessible as an Angular expression, when I try to use for selecting a directive it doesn't work:
<!DOCTYPE html>
<html ng-app="app">
<body ng-controller="TextController">
<!-- item.dir is accessible: -->
<div>Value of item: {{item.dir}}</div>
<!-- It works. the directive "hello" is rendered -->
<div class="hello"></div>
<hello></hello>
Here you should see additional text:
<!-- Doesn't work item.dir is not recognized-->
<!-- as a class -->
<div class="{{item.dir}}"></div>
<!-- as an attribute-->
<div {{item.dir}}></div>
<!-- trying ng-class (it fails)-->
<div ng-class="item.dir"></div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.5/angular.min.js"></script>
<script>
var appModule = angular.module('app', []);
// The directive to render
appModule.directive('hello', function() {
return {
restrict: 'ACE',
template: '<div>works: Transcoded text</div>',
replace: true
};
});
appModule.controller('TextController', function ($scope) {
$scope.item = {dir: 'hello'}; // the name of the directive, the idea is to use it for referring to many future directives.
});
</script>
</body>
</html>
Here's a plunker of the code: http://plnkr.co/edit/tM73sY3vTfPFHmn6iCYE?p=preview
So, what I am missing? How do I achieve to get Angular using string interpolation when using a directive? Thanks!
For directives to work, Angular needs to compile your html (something automatically done when a page is loaded).
Having a way to control freely which directive to instantiate is a bit like pulling the rug under your feet and is atypical. One of the issue is that the compilation "destroy" the internal binding/watchers data and some of the original DOM and thus there is not enough information to "recompile" a DOM node.
NOTE: you cannot change attribute or element names (only attribute values) with angular using this type of binding: {{ }} But ng-class="..." and class="{{...}}" works.
I do not understand what you are trying to achieve exactly. If the intention is really about modifying the item.dir's value and having Angular "reconfigure" your application, it is "possible" but I highly suspect it will induce "state" drawbacks.
Nevertheless, here's a working "hack" that "remembers" the original DOM html and recompiles it when needed. This is done in 2 compilation phases: First phase is to restore the original bindings and a second phase that runs after the $digest cycle so the original bindings finished populating the class name from the scope (i.e. to have item.dir in effect). The drawback of course is if you made modifications to the enclosing DOM, this will wipe them! Alternatively, it might be possible to remember only specific attributes and revert "that" only while keeping other portion of the DOM intact (but might create other issues).
appModule.directive('forceRecompilation', ['$timeout', '$compile', function($timeout, $compile) {
return {
restrict: 'A',
link: function(scope, element, attr) {
var originalHtml = element.html();
scope.$watch(attr.forceRecompilation, function(){
// restore original HTML and compile that
element.html(originalHtml);
$compile(element.contents())(scope);
// wait for all digest cycles to be finished to allow for "binding" to occur
$timeout(function(){
// recompile with bounded values
$compile(element.contents())(scope);
});
});
}
};
}]);
...to be used as an enclosing tag of the section of the DOM to be acted upon. It will "revert & recompile" everything under it when the expression changes. (here "item.dir"):
<div force-recompilation="item.dir">
<div class="{{item.dir}}">
</div>
Plunker: http://plnkr.co/edit/TcMhzFpErncbHSG6GgZp?p=preview
In the plunker, there are 2 directives "hello" and "hello2". Change the text to "hello" and back to "hello2" to see the effect.
EDIT: The following is a directive that allows inserting markup to be compiled as described in a comment below. This is just a slightly modified version of Angularjs - inline directives with ng-bind-html-unsafe
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope',
function($scope) {
$scope.test = false;
$scope.dir = "ng-click";
$scope.clicked = function() {
$scope.test = !$scope.test
}
$scope.myHTML =
'I am an <b ng-show="test">invisible</b> HTML string with ' +
'links! and other <em>stuff</em>';
}
])
// modified plunker taken from https://stackoverflow.com/questions/18063280/angularjs-inline-directives-with-ng-bind-html-unsafe
//
// Allows an attribute's value to be evaluated and compiled against the scope, resulting
// in an angularized template being injected in its place.
//
// Note: This directive is prefixed with "unsafe" because it does not sanitize the HTML. It is up
// to the developer to ensure that the HTML is safe to insert into the DOM.
//
// Usage:
// HTML: <div unsafe-bind-html="templateHtml"></div>
// JS: $scope.templateHtml = '<a ng-onclick="doSomething()">Click me!</a>';
// Result: DIV will contain an anchor that will call $scope.doSomething() when clicked.
.directive('unsafeBindHtml', ['$compile',
function($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.unsafeBindHtml);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM element
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}
]);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.5/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.5/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p unsafe-bind-html="myHTML"></p>
(click on the link to see <code>ng-click</code> in action)
</div>
</body>
</html>
Apparently, the tg-dynamic-directive does the trick.

Categories

Resources