I've got a situation where I want to trigger an Angular-based widget iff a query arg is present in the current URL. My approach is to check (on page load) if the arg is present, and if so, append a bit of markup to the <body> that will get the app rolling:
var exampleApp = angular.module('exampleApp', []);
//init
$(function() {
var widgetMarkup = "<div ng-app=\"exampleApp\"><div ng-include=\"'/partials/widget.html'\"></div></div>";
if (window.location.href.indexOf('?arg') > -1) {
$(document.body).append(widgetMarkup);
}
});
PROBLEM: The markup is added to the DOM, but has no effect. But if I add the same markup to the raw HTML file, the app is picked up and the partial is rendered. Do I need to tell Angular to re-scan the page for directives?
what you want is angular.bootstrap. Call it when your DOM is ready and everything has been appended.
angular.bootstrap(document, ['exampleApp']);
This will start angular on the document, as if it had ng-app on it. You can bootstrap it to whatever element you want though.
You have to manually bootstrap your app.
var exampleApp = angular.module('exampleApp', []);
if (window.location.href.indexOf('?arg') > -1) {
angular.element(document).ready(function() {
angular.bootstrap(document, ['exampleApp']);
});
}
More info here: angular docs
Related
I am using the following code in angular JS template to open outlook with some fields pre filled.
OWNER
where -
item.caseOwner = abc#xyz.com;
This works fine when item.caseOwner is a string. But in some scenarios, it comes wrapped in an HTML tag for the highlighting purpose.
item.caseOwner = <span class"highlight">abc#xyz.com<span>;
In this case the outlook opens up with HTML tags and it makes no meaning. Is there a way to extract the string inside the <span></span> before the user clicks the link ?
Create a custom filter to cleanup item.caseOwner.
OWNER
For cleaning up span tags, a search-replace can be implemented like so.
function asEmailFn(emailAddressOrMarkup) {
return emailAddressOrMarkup
.replace(/<[\/]*?span.*?>/g, '');
}
angular.module('App', [])
.filter('asEmail', function() {
return asEmailFn;
})
.controller('TestController', ['$scope', 'asEmail', function($scope, asEmail) {
}]);
Here is how I have done it.
In the main parent controller of application i.e. alias home
home.stripOffHTML = function(itemString){
let localString = itemString;
if(localString){
return localString.replace(/<\/?[^>]+(>|$)/g, "");
}
};
In the HTML code:
OWNER
I have an input element and, on DOM ready, I attach to this a jQuery plugin:
<input id="classic-input" type="text">
<script type="text/javascript">
$(document).ready(function () {
$("#classic-input").tokenInput("http://jquery-tokeninput-demo.herokuapp.com/");
});
</script>
This works pretty well and since I need to use this in various part of my application I've created template.html and inserted in it the previous snippet of code, than I created this:
(function() {
'use strict';
angular
.module('app', [])
.directive('myDir', myDir);
function myDir() {
return {
templateUrl : "template.html"
};
}
})();
When I insert this directive in the page the form is shown, but nothing happen when I interact with it. I'm pretty sure that ready handler is the root of all evil. Am I wrong? If I'm right how can edit this directive?
Here is a plunk with examples.
Your template is rather small, besides you could skip script part - just put your code in directive, like this:
function myDir() {
return {
link: function (scope, element) {
$(element).tokenInput("http://jquery-tokeninput-demo.herokuapp.com/");
}
};
}
And apply it to input tag like this:
<input type="text" my-dir>
This way you don't have id hard-coded in your template, which will become a problem in case there multiple such directives on page.
See updated demo.
I setup a single-page app with AngularJS and used Skrollr on the home page. I have not used Skrollr before, so I wanted to check with others about the proper 'Angular' way to integrate it with AngularJS, before I start to dive into using more features
What I did in Angular was create a service to load the script onto the page and call skrollr.init() and return it as a promise. Then injected the service to a directive which calls refresh as needed. If a page needs skrollr, I can use this directive on the page somewhere and set the data attributes per skrollr documentation.
ie this works:
<div class="main" skrollr-tag>
<div data-0="color:rgb(0,0,255);" data-90="color:rgb(255,0,0);">WOOOT</div>
</div>
It seems elements added to DOM later on, such as by ngRepeat, skrollr doesn't know about, so I need to include this directive on all elements generated dynamically w/ skrollr data attributes for it to work.
<div class="main" skrollr-tag>
<!-- this heading will animate all the time -->
<h1 data-0="opacity: 1" data-50="opacity: 0">WOOT!</h1>
<div data-ng-repeat="item in items" class="had-to-add-skrollr-again" skrollr-tag>
<!-- skrollr animates this only on page refresh, unless skrollr-tag duplicated above -->
<div data-0="color:rgb(0,0,255);" data-90="color:rgb(255,0,0);">{{item.name}}</div>
</div>
</div>
So, to recap, skrollr is 'aware' of these dynamic elements on the 1st load after refresh, but then after navigating to a different route then back again they no longer get animated unless you refresh page again, or add skrollr-tag directive to the dynamic elements themselves.
Is this a bad idea for performance reasons to include this directive on each dynamic element needing skrollr, thus calling refresh() again for each one? Ideally solution would be load skrollr-tag directive once per page, and it's aware of dynamic elements. I am open to any completely different cleaner more simple way to integrate skrollr to angular.
The angular code is here:
service:
.service('skrollrService', ['$document', '$q', '$rootScope', '$window',
function($document, $q, $rootScope, $window){
var defer = $q.defer();
function onScriptLoad() {
// Load client in the browser
$rootScope.$apply(function() {
var s = $window.skrollr.init({
forceHeight: false
});
defer.resolve(s);
});
}
// Create a script tag with skrollr as the source
// and call our onScriptLoad callback when it
// has been loaded
var scriptTag = $document[0].createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.async = true;
scriptTag.src = 'lib/skrollr/dist/skrollr.min.js';
scriptTag.onreadystatechange = function () {
if (this.readyState === 'complete') onScriptLoad();
};
scriptTag.onload = onScriptLoad;
var s = $document[0].getElementsByTagName('body')[0];
s.appendChild(scriptTag);
return {
skrollr: function() { return defer.promise; }
};
}
]);
directive:
.directive('skrollrTag', [ 'skrollrService',
function(skrollrService){
return {
link: function(){
skrollrService.skrollr().then(function(skrollr){
skrollr.refresh();
});
}
};
}
])
I'm currently having the same issue trying to integrate Skrollr into AngularJS.
The problem is basically this directive, it works when the page loads for the first time but then nothing is happening even though its being called when new html elements are created - or when you change views.
.directive('skrollrTag', [ 'skrollrService',
function(skrollrService){
return {
link: function(){
skrollrService.skrollr().then(function(skrollr){
skrollr.refresh();
});
}
};
}
])
I think the reason is the way angularJS injects new html content. By the time skrollr does "refresh" its not yet rendered or some sort of conflict.
Maybe the only solution is to modify skrollr script.
This answer should help: AngularJS watch DOM change. Try updating your directive to watch for child node changes. This way, it'll automatically refresh whenever new nodes are added.
.directive('skrollrTag', [ 'skrollrService',
function(skrollrService){
return {
link: function(scope, element, attrs){
skrollrService.skrollr().then(function(skrollr){
skrollr.refresh();
});
//This will watch for any new elements being added as children to whatever element this directive is placed on. If new elements are added, Skrollr will be refreshed (pulling in the new elements
scope.$watch(
function () { return element[0].childNodes.length; },
function (newValue, oldValue) {
if (newValue !== oldValue) {
skrollrService.skrollr().then(function(skrollr){
skrollr.refresh();
});
}
});
}
};
}
]);
EDIT
Updated to account for the promise you're using (that would already be resolved), and added a comment to further explain the solution.
i made a directive for skrollr
(function () {
'use strict';
angular.module('myApp', [])
.directive('skrollr', function () {
var obj = {
link: function () {
/* jshint ignore:start */
skrollr.init().refresh();
/* jshint ignore:end */
}
};
return obj;
});
})();
and use like this
<div skrollr data-0="background-color:rgb(0,0,255);" data-500="background-color:rgb(255,0,0);">WOOOT</div>
I've just started using Onsen UI by implementing the bootstrap example and I've been trying to get the view to update when I change the page attribute.
<body ng-controller="PageLoadingCtrl">
<ons-screen page="{{loadedPage}}"></ons-screen>
</body>
My controller's code:
app.controller('PageLoadingCtrl', ['$scope', '$timeout', 'notificationService',
function($scope, $timeout, sharedService){
$scope.loadedPage = "index.html";
$scope.updatePage = function(page){
$timeout( function (){
$scope.loadedPage = page;
$scope.$apply();
}, 500);
};
$scope.$on('changePage', function (event, message){
$scope.updatePage(message);
});
}
]);
As you can see I'm using a controller on the body object so that I can update the loadedPage variable however, when I fire the changePage event, the view doesn't change.
After checking the DOM elements with web inspector I can see that page attribute is equal to whatever I pass to the updatePage function.
So far I tried to force a refresh with $apply and $digest but that still doesn't do the trick.
Cheers!
Because ons-screen need to maintain page stack. it's not intuitive to use binding for the page attribute.
Using ons.screen.presentPage()/dismissPage()/resetPage() is the preferred way.
http://plnkr.co/edit/GRVZl35D1cuWz1kzXZfF?p=preview
In the custom fancybox (aka lightbox, a dialog) I show contents with interpolated values.
in the service, in the "open" fancybox method, i do
open: function(html, $scope) {
var el = angular.element(html);
$compile(el)($scope); // how to know when the $compile is over?
$.fancybox.open(el); // the uncompiled is shown before the compiled
}
The problem is that the content in the dialog is loaded before the end of the $compile, so after less than a second i got a refresh of the dialog content with the values.
The plunkr works, but i want to avoid that the "el" is shown before it gets totally compiled: i want to show it only after the $compile has finished his job
Is there a way to know when the $compile it's over so i'll show the content on fancybox only after that?
You can't inject $scope into services, there is nothing like a singleton $scope.
So instead of $compile(el)($scope); try:
var compiledEl = $compile(el);
....
The $compile returns compiled data.
as a side note
I would provide service to directive and compile it into directive instead. I think it's the right way.
I've had the same problem with the ngDialog modals and popup provider. I needed to position the dialog based on its height. But the height depended on the compiled DOM.
I eventually found a solution using $timeout, like described in that post: http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering/
For your code, it would give something like that:
open: function(html, $scope) {
var el = angular.element(html);
$compile(el)($scope);
$timeout(function() {
$.fancybox.open(el);
}, 0);
}