Include external js widget in angular template - angularjs

I need to include script tag which will render widget on my angularjs template.
For example I'd include this
<script type="text/javascript" src="http://100widgets.com/js_data.php?id=106"></script>
But angular will not render it.

Since the 100widgets scripts manipulate DOM and add other <script> tags to HTML file it could not work properly.
Morover some of these widgets are Flash based so the script add reference to SWF objects.
I think one possibility is to analyze the output of the request to url in src attribute (in your example http://100widgets.com/js_data.php?id=106) and trying to add the corresponding DOM manipulation and scripts to the template in which you desire the widget will appear.
Following is an example showing a page (page1) NOT WORKING (simply added the script tag as you typed) and a second page (page2) whose template has the insertions needed to show up the calendar widget.
PS: due to sandbox restriction this snippet could not work here on SO; try this CodePen version in debug mode: http://codepen.io/beaver71/pen/MyjBoP, or create your own version deployed on your local web server.
(function() {
'use strict';
angular.
module('myApp', ['ui.router']).
config(configRouteProvider).
controller('AppCtrl', AppCtrl);
function AppCtrl($location, $rootScope) {
$rootScope.$on('$stateChangeStart', onStateChangeStart);
function onStateChangeStart(event, toState, toParams, fromState, fromParams, options) {
console.log('From:', fromState.name,
'to:', toState.name);
}
}
function configRouteProvider($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
templateUrl: 'views/home.html'
})
.state('page1', {
url: '/pag1',
templateUrl: 'views/page1.html',
})
.state('page2', {
url: '/pag2',
templateUrl: 'views/page2.html',
});
$urlRouterProvider.otherwise('/');
}
}());
body {
margin: 0px;
}
hr {
margin: 0px;
}
.tabs {
padding: 8px;
background-color: black;
color: white;
}
.tabs a,
.tabs a:visited,
.tabs a:active,
.tabs a:hover {
color: white;
}
.my-tab {
height: 100%;
width: 100%;
position: absolute;
padding: 8px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src='//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<script src='https://code.angularjs.org/1.4.0/angular.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.js'></script>
</head>
<body ng-app="myApp" ng-controller="AppCtrl as app">
<div class="tabs">
Home
Page1
Page2
</div>
<hr />
<div ui-view></div>
<script id="views/home.html" type="text/ng-template">
<div class="my-tab">
<h3>Home</h3>
<p>bla bla bla</p>
</div>
</script>
<script id="views/page1.html" type="text/ng-template">
<div class="my-tab">
<h3>Page1</h3>
<p>Using script type="text/javascript"... NOT WORKING:</p>
<script type="text/javascript" src="http://100widgets.com/js_data.php?id=106"></script>
</div>
</script>
<script id="views/page2.html" type="text/ng-template">
<div class="my-tab">
<h3>Page2</h3>
<p>Workaround</p>
<!--code1-->
<div class="scriptcode">
<!--ecode1-->
<a target='_blank' href='http://100widgets.com/calendars/106-calendar.html'>
<embed align="middle" id="calendar" width="170" height="156.111111111" allowscriptaccess="always" quality="high" salign="lt" wmode="transparent" src="http://100widgets.com/js-files/postit.swf?UTCoffset=0&gid=0&text1=St Valentines is on 14th February 2012&&event_time=&rec=&rnd=0&gha=0&ghb=0&ghf=1&gbc=FFB200&gfc=040244&gtc=F9F9FF&gnu=http://mycalendar.org/widget/&fna=&ims="
type="application/x-shockwave-flash" />
</a>
<!--code2-->
</div>
<!--ecode2-->
<!--below commented cause it's not necessary -->
<!--script type="text/javascript">
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "http://100widgets.com/stat.js.php";
document.body.appendChild(js);
</script-->
</div>
</script>
</body>

Due to safety constraints, Angular does not parse <script> tags inside templates.
Then the widget you are referring to, is utilizing document.write. Document.write is unavailable once your page is done loading.
So, there seems to be no easy way out here.
However, as what you are trying to do, is something that is quite usual in add-script, krux created postscribe. A way around this issue. On my turn I created a small directive that utilizes this library.
In your template it will look something like this:
<div ps-src="http://100widgets.com/js_data.php?id=21"></div>
the directive:
function psScr($document) {
return {
restrict: 'A',
scope: {
psSrc: '#'
},
link: link
}
function link(scope, elm) {
if (typeof postscribe !== 'undefined') {
postscribe(elm[0], `<script src='${scope.psSrc}'></script>`);
} else {
// If postscibe isn't loaded, first load the needed libarary
var script = document.createElement('script');
script.src = 'https://gitcdn.xyz/repo/krux/postscribe/master/dist/postscribe.js';
script.onload = function () {
// once postscibe is in, kick it into action.
link(scope,elm);
};
document.head.appendChild(script);
}
}
}
angular.module('psSrcModule', [])
.directive('psSrc', psScr);
You can see it in action in this plunk
Not all of the widgets behave nicely in combination with postscribe though, some of them seem to display some artefacts in the html. I currently lack the time to find out who is to blame for this (100widgets or postscribe), but if you really need this, this is something that can be worked out.

Related

Why ng-class is not applied as per the current state of ui router?

My application has two different views (page1 and page2), which has been configured with ui-router's stateProvider. I want to apply a css on the body based on what page I am looking at. If I am looking at page1 I want to apply class1 to the body of the page and class2 for page2.
To achieve this I am using the ng-class directive of the angularjs. The value of the ng-class directive is being set as per the current state of the ui-router.
Here is the example code for it, the problem is that the expression inside the ng-class doesn't get evaluated, so my css doesn't get applied.
Can anyone point me what I am doing wrong ?
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.4.2/angular-ui-router.min.js"></script>
<style media="screen">
h2 {
margin: 5px 5px 5px 5px;
}
body {
display: flex;
flex-direction: column;
}
.class1 {
align-items: flex-start;
}
.class2 {
align-items: center;
}
</style>
</head>
<body ng-app="graceApp" ng-class="$state.current.data.bodyClass">
<div ui-view style="display: flex"></div>
<script>
angular
.module('graceApp', [
'ui.router'
])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('page1', {
url: '/page1',
template: '<h2>This is page1</h2> <h2>Item1</h2> <h2>Item2</h2>',
data: {
bodyClass: 'class1'
}
})
.state('page2', {
url: '/page2',
template: '<h2>This is page2</h2> <h2>Item1</h2> <h2>Item2</h2>',
data: {
bodyClass: 'class2'
}
});
$urlRouterProvider.otherwise('/page1');
});
</script>
</body>
</html>
The $state service is not available in HTML. You just have access in the html to the variables set in the scope so a quick fix would be to set the state in the rootScope then you could access it in the HTML.
$rootScope.$state = $state;
I'm not sure whether $state service is accessible in HTML like this. Did you consider to create some function in your controller that will return $state.current.data.bodyClass and then, you link that function into ng-class
function getClass() {
return $state.current.data.bodyClass;
}
HTML:
<body ng-app="graceApp" ng-class="getClass()">
If you are using controllerAs pattern, make sure you make this function public and use it inside HTML with controller alias.
You can't access $state in html directly, so you need to define another scope variable in controller like
<!doctype html>
<html ng-app="graceApp">
<head>
<meta charset="utf-8">
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.4.2/angular-ui-router.min.js"></script>
<style media="screen">
.class1 {
background-color: red;
}
.class2 {
background-color: green;
}
</style>
</head>
<body ng-controller="mainController" ng-class="bodyClass">
<div ui-view></div>
<script>
angular.module('graceApp', ['ui.router']).config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('page1', {
url: '/page1',
template: '<h2>This is page1</h2> <h2>Item1</h2> <h2>Item2</h2>',
data: {
bodyClass: 'class1'
}
})
.state('page2', {
url: '/page2',
template: '<h2>This is page2</h2> <h2>Item1</h2> <h2>Item2</h2>',
data: {
bodyClass: 'class2'
}
});
$urlRouterProvider.otherwise('/page1');
});
angular.module("graceApp").controller("mainController", function($rootScope, $scope) {
$rootScope.$on("$stateChangeSuccess", function(event, current, params) {
console.log(current.data.bodyClass);
$scope.bodyClass = current.data.bodyClass;
});
});
</script>
</body>
</html>

Is it possible to route using HTML tag <button>?

I know how to do Angular ngRoute using <a> tag. I would like to know if it's also possible for me to use <button> tag to navigate between <ng-view>. Let's say I have the following code:
app.js & index.html
var app = angular.module('test', ['ngRoute']);
app.config(function($routeProvider) {
// mapping contacts.html to path /cnt
$routeProvider.when('/cnt', {
templateUrl: "https://www.google.co.za/", // the file to be mapped to
controller: "cctrl" // including the controller for that file
});
// mapping student.html to path /std
$routeProvider.when('/std', { // mapping url
templateUrl: "https://www.facebook.com", // the file to be mapped to
controller: "sctrl" // including the controller for that file
});
});
// creating a controller name cctrl
app.controller('cctrl', function($scope) {
});
// creating a controller name sctrl
app.controller('sctrl', function($scope) {
});
ul.zzz{
list-style: none;
width: 100%;
margin: 10px auto;
}
ul.zzz li{
float: left;
padding: 10px;
}
.mainContent ng-view{
margin: 0;
}
<html ng-app="test">
<head>
<title>Angular Test</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-route.js"></script>
</head>
<body>
<div>
<ul class="zzz">
<li>Student
</li>
<li>Contacts
</li>
</ul>
<br/>
</div>
<div class="mainContent">
<ng-view>
</ng-view>
</div>
</body>
</html>
the code above shows the working html page
the code below is whats running in my mind if i use button
<button name="gotoPage2" ng-click="gotoNext()">Next</button>
<script>
var app = angular.module('test',[ngRoute]);
app.config(function($routeProvider){
$routeProvider.when('button with a name gotoPage2 is click',{
templateUrl:'page2.html'
})
});
// or
app.controller('controllerName', function($scope){
$scope.gotoNext = function(){
app.config(function($routeProvider){
$routeProvider.when('button with a name gotoPage2 is click',
{templateUrl:'page2.html'}
)
});
};
});
</script>
Have you try using ng-click on the button and have a function on the controller to change the route
Html
<button ng-click="changeRoute({view})">Text</button>
Controller
...
$scope.changeRoute(view){
$location.path(view);
}
...
I usually use ui-router, i like it better then ng-route
put the button tag inside <a> it will work
<li><button>name</button>

angular-ui bootstrap tooltip issue

For some reason with the way we structured the page, the tooltip is not working.
main.html
<div class="main-navigation">
<div rt-tool-menus-"menus" selected="selectedMenus" tooltip="{{appController.displayName}}"></div>
</div>
controller.js
angular.module('abc')
.controller('abcController',.....
self.menus=[
{
heading: 'Head1',
active: false,
route: 'head1'
},
{
heading: 'Head2',
active: false,
route: 'head2'
tooltip: 'head2' // tried, doesnt work
}];
self.selectedMenus = []'
self.tooltip = appConfig.displayName; // tried not working
what would be the right approach to show tooltip with the correct header, and location?
Not sure what appConfig is (not visible in your snippet) but you have to add the text you want to show in the tooltip to an instance variable of the controller if you're using controllerAs or a $scope variable.
Please have a look at the code below or in this jsFiddle.
It's not clear what rt-tool-menus is. Is it a custom directive?
angular.module('demoApp', ['ngAnimate', 'ui.bootstrap'])
.config(function($tooltipProvider) {
$tooltipProvider.options({placement: 'bottom'});
})
.controller('mainController', function($scope){
this.displayName = 'Hello there';
});
.main-navigation {
border: 1px solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.4/angular.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-animate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.3/ui-bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.3/ui-bootstrap-tpls.js"></script>
<div ng-app="demoApp" ng-controller="mainController as mainCtrl">
<div class="main-navigation">
<div rt-tool-menus-"menus" selected="selectedMenus" tooltip="{{mainCtrl.displayName}}">Hover me to show tooltip!!!</div>
</div>
</div>

How to do preloader for `ng-view` in Angular JS?

I use <div ng-view></div> on web page.
When I click link in block <div> is loaded HTML template was set in routeProvider. Also together is done request AJAX that returns data that was loaded template.
Now problem is that after click I get HTML template with empty form, still is working AJAX request. After some seconds form HTML is fiiled data from AJAX.
How I can do preloader to page for directory ng-view?
It seems that there are some similar questions here:
Angularjs loading screen on ajax request
Angular JS loading screen and page animation.
Also, there a bunch of modules to work with loading animation at http://ngmodules.org. For example, these:
https://github.com/cgross/angular-busy
https://github.com/chieffancypants/angular-loading-bar (I use this one in my apps)
https://github.com/McNull/angular-block-ui and other.
UPD:
I've written a simple solution based on how the angular-loading-bar works. I didn't test it with ng-view, but it seams to work with ui-view. It is not a final solution and have to be polished.
angular.module('ui')
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('LoadingListener');
}])
.factory('LoadingListener', [ '$q', '$rootScope', function($q, $rootScope) {
var reqsActive = 0;
function onResponse() {
reqsActive--;
if (reqsActive === 0) {
$rootScope.$broadcast('loading:completed');
}
}
return {
'request': function(config) {
if (reqsActive === 0) {
$rootScope.$broadcast('loading:started');
}
reqsActive++;
return config;
},
'response': function(response) {
if (!response || !response.config) {
return response;
}
onResponse();
return response;
},
'responseError': function(rejection) {
if (!rejection || !rejection.config) {
return $q.reject(rejection);
}
onResponse();
return $q.reject(rejection);
},
isLoadingActive : function() {
return reqsActive === 0;
}
};
}])
.directive('loadingListener', [ '$rootScope', 'LoadingListener', function($rootScope, LoadingListener) {
var tpl = '<div class="loading-indicator" style="position: absolute; height: 100%; width: 100%; background-color: #fff; z-index: 1000">Loading...</div>';
return {
restrict: 'CA',
link: function linkFn(scope, elem, attr) {
var indicator = angular.element(tpl);
elem.prepend(indicator);
elem.css('position', 'relative');
if (!LoadingListener.isLoadingActive()) {
indicator.css('display', 'none');
}
$rootScope.$on('loading:started', function () {
indicator.css('display', 'block');
});
$rootScope.$on('loading:completed', function () {
indicator.css('display', 'none');
});
}
};
}]);
It can be used like this:
<section class="content ui-view" loading-listener></section>
You can try something like this(simplest solution):
Set your loader animation/picture:<div class="loader" ng-show="isLoading"></div>
On div element add click event:
Then AJAX request success set isLoading=true
Download javascript and css files from PACE Loader.
Playing around with pace loader using ng-views . Hope this helps someone trying to use PACE.JS with Angular. In this example I am using ng-router to navigate between views.
app.js
var animateApp = angular.module('route-change-loader', ['ngRoute']);
var slowResolve = function(slowDataService){
return slowDataService.getContacts();
};
slowResolve.$inject = ['slowDataService'];
// ROUTING ===============================================
// set our routing for this application
// each route will pull in a different controller
animateApp.config(function($routeProvider) {
$routeProvider
// home page
.when('/route1', {
templateUrl: 'route1.html',
controller: 'slowCtrl',
controllerAs:'ctrl',
resolve: {
contacts:slowResolve
}
})
.otherwise({
templateUrl:'default.html'
});
});
var SlowCtrl = function(contacts) {
this.contacts = contacts;
};
SlowCtrl.$inject = ['contacts'];
angular.extend(SlowCtrl.prototype, {
message:'Look Mom, No Lag!',
contacts: []
});
animateApp.controller('slowCtrl', SlowCtrl);
var SlowDataService = function($timeout){
this.$timeout = $timeout;
};
SlowDataService.$inject = ['$timeout'];
angular.extend(SlowDataService.prototype, {
contacts:[{
name:'Todd Moto',
blog:'http://toddmotto.com/',
twitter:'#toddmotto'
},{
name:'Jeremy Likness',
blog:'http://csharperimage.jeremylikness.com/',
twitter:'#jeremylikness'
},{
name:'John Papa',
blog:'http://www.johnpapa.net/',
twitter:'#John_Papa'
},{
name:'Josh Carroll',
blog:'http://www.technofattie.com/',
twitter:'#jwcarroll'
}],
getContacts:function(){
var _this = this;
return this.$timeout(function(){
return angular.copy(_this.contacts);
}, 1000);
}
});
animateApp.service('slowDataService', SlowDataService);
index.html
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>Test Example</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<link rel="stylesheet" href="pace.css">
<script src="http://code.angularjs.org/1.2.13/angular.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular-route.js"></script>
<script src="app.js"></script>
<script src="pace.js"></script>
</head>
<body ng-app="route-change-loader">
<div class="container">
<div class="masthead">
<ul class="nav nav-tabs">
<li>
Default
</li>
<li>
Slow Loading Controller
</li>
</ul>
</div>
<!-- Jumbotron -->
<div class="row">
<route-loading-indicator></route-loading-indicator>
<div ng-if="!isRouteLoading" class="col-lg-12" ng-view=""></div>
</div>
<!-- Site footer -->
<div class="footer">
<p>by <b>Ritesh Karwa</b> </a>
</p>
</div>
</div>
</body>
</html>
default.html
<h1>Click on the tabs to change routes</h1>
route1.html
<h1>{{ctrl.message}}</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Blog</th>
<th>Twitter</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='contact in ctrl.contacts'>
<td>{{contact.name}}</td>
<td>{{contact.blog}}</td>
<td>{{contact.twitter}}</td>
</tr>
</tbody>
</table>

Page Transition with AngularJS and CSS Animations

I'm working on a basic AngularJS app. This app will have pages that are going to be defined within templates. When a user changes pages, I want to be able to show the other page with a basic animation. Currently, my app is doing this. However, the previous page drops below the new page and sort of flickers. I can't figure out why this is happening. Here is my code:
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<title>My HTML File</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-animate.js"></script>
<style type="text/css">
.myAnimation { height:480px; width:640px; overflow:hidden; }
.myAnimation.ng-enter { animation-duration: .3s; animation-name: fadeIn; animation-timing-function: cubic-bezier(.71,.55,.62,1.57); }
.myAnimation.ng-leave { animation-duration: .3s; animation-name: fadeOut; animation-timing-function: cubic-bezier(.71,.55,.62,1.57); }
#keyframes fadeIn {
from { opacity: 0; transform: scale(.9, .9); }
to { opacity: 1; transform: scale(1, 1); }
}
#keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
</style>
</head>
<body style="margin:0; padding:0;">
<ul style="list-style-type:none; margin:0px; padding:0px;">
<li style="display:inline;"><a href='#page1'>Page 1</a> </li>
<li style="display:inline;"><a href='#page2'>Page 2</a> </li>
</ul>
<div style="display:block; border:1px solid black;">
<div ng-view class="myAnimation"></div>
</div>
<script type="text/javascript">
angular.module('app', ['ngRoute', 'ngAnimate']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/page1', {templateUrl: 'views/page1.html', controller: Page1Ctrl}).
when('/page2', {templateUrl: 'views/page2.html', controller: Page2Ctrl}).
otherwise({redirectTo: '/page1'});
}]);
function Page1Ctrl($scope, $http) {
}
function Page2Ctrl($scope, $http) {
}
</script>
</body>
</html>
How do I prevent the scrollbar from appearing when I change pages? And prevent the older page from dropping below the new page?
Thank you!
It's likely that you need to give the animated divs a position:absolute; style during the animations.
What is happening is that the second template is being loaded into the DOM while the first template is still doing the fade-out animation. Without position:absolute they do what is natural and start block stacking until the first template has fully faded out and Angular removes it.
Keep in mind if you add position:absolute you may also have to play with the "Top" value to position it correctly depending on which parent element has position:relative or absolute.
You can put the position:absolute class on the animated div generally, or just specifically on the animating classes depending on what works better for your app structure.

Resources