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.
Related
It seems my li elements in angularjs directive not responding clicking event.
HTML:
<my-selbg>
<ul>
<li ng-repeat="bgimage in bgimages"><img src={{bgimage}} width="85" height="82" dir={{bgimage}}></li>
</ul>
</my-selbg>
JS:
var mlwcApp = angular.module('mlwcApp', [])
.controller('BgImagesListController', function($scope, $http) {
$http.get("http://localhost:8080/webcontent/bg_images").success(function(response) {
$scope.bgimages = response;
});
})
.directive('myselbg', function(){
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs){
var elementOne = angular.element(element.children[1]);
var elementTwo = angular.element(element.children[2]);
var elementThree = angular.element(element.children[3]);
setUpBGImg = function(){
console.log('link function');
};
$(elementOne).on('click', setUpBGImg);
$(elementTwo).on('click', setUpBGImg);
$(elementThree).on('click', setUpBGImg);
}
};
});
I have 3 li elements and clicking any of them dose not hit the code in link function. Anyone has idea?
You're new to angular, by the looks of it.
First off, before going any further - your directive will not even bind at all in the state it is in. You've got an element directive (which is fine, though if I were you I'd make it an attribute directive by restricting on A, which allows you to then apply it to the list rather than an element above it) named myselbg in your code. However, your markup is set as my-selbg, which would then look for the angular directive mySelbg, which does not exist.
In addition to this, your directive will evaluate before the list is rendered (thanks to the order of priority in execution). You have two choices to go around this:
You can do something like this: https://jsfiddle.net/a01n3srw/1/ . Really not recommended - I am using $timeout in order to evaluate code after the current refresh cycle is done, at which point the list fully exists
You can use the simple ngClick angular core directive in order to make this easy. Added bonus, when your function that you evaluate starts modifying scope, you won't shoot yourself in the foot using the previous method and having to use $apply
I'm looking to have a function run every time an angular directive updates. In my case, I have an array of modal configurations that get used on a modal markup template.
Every time the template is used to generate a modal due to a change in the model, I want to run a positionModal() method.
scope.$watch in the link function doesn't seem to notice when I change the model, and I cant think of any other way of doing this. I tried a post-link function thinking that the compile function would get called when the directive was applied, but that doesn't seem to work either. Here is my example controller:
MyApp.controller("ModalController", function () {
//Define scope vars
$scope.modals = [];
$scope.$on("modalTrigger", function (event, settings) {
$scope.modals.push(settings);
});
});
Note: I've simplified the controller here- know that it DOES work.
Here is the template code:
<div class="modalParent" ng-controller="ModalController">
<div id="{{modal.id}}" class="modal" ng-class="modal.type" ng-repeat="modal in modals">
<div class="content">
<h2 ng-show="modal.title">{{modal.title}}</h2>
<p>{{modal.message}}</p>
<button>{{modal.button}}</button>
</div>
</div>
</div>
The directive is currently like this:
MyApp.directive("modalParent", function () {
var positionModals = function (element) {
element.find(".modal .content").each(function () {
//position logic here
});
};
return {
restrict: "C",
compile: function (tElement) {
positionModals(tElement);
}
};
});
Note: Also simplified for the purposes here.
The positionModals() call works when the first modal gets pushed to the array. After that, it stops working.
I've tried using the linking function as well, same result. scope.$watch(modals, function(){...}) does not work.
Can somebody help me figure out what I'm doing wrong?
Figured it out!
I was applying the directive to the parent, ".modalParent".
The ng-repeated element in this case is the modal itself ".modal".
You would want the directive to run on elements that get updates as the model changes, because then the linking function will get called each time the element is instantiated, rather than sitting and watching the parent and trying to update from there.
Hope this helps somebody.
Instead of calling like this, my approach is to write this in the services and inject that services in the controller wherever you want to get that function or the data to be notified as,
Services.js
as.service("xyzservice",function(factoryname){
//here the code goes...
})
Now inject in Controller,
ac.controller("controllername",function(xyzservice){
})
ac.controller("controllername",function(servicename){
})
ac.controller("controllername",function(xyzservice){
})
Here we have injected it in the two controller, we can get it.
I have a JSON file that contains the page content I wish to load. In it, I have a link that looks like this:
<a data-ng-click='foo()'>Bar</a>
When I load the page content into the page as HTML:
<p class="body" ng-bind-html="jsonText | raw"></p>
using this filter:
app.filter('raw', function ($sce) {
return function (input) {
return $sce.trustAsHtml(input);
}
});
the link does not trigger foo() call on click. Is what I'm trying to do impossible or am I doing something wrong?
Using filter for this is not a good idea, because you need to $compile the loaded HTML and for that you need $scope. So, you either need to $compile it manually and put the result in $scope yourself, or create a directive instead of a filter:
.directive('dynamicHtml', ['$compile', function ($compile) {
return {
link: function ($scope, $element, $attrs) {
$scope.$watch($attrs.dynamicHtml, function (html) {
$element.empty().append($compile(html)($scope));
});
}
};
}]);
and use it instead of ngBindHtml:
<p dynamic-html="jsonText"></p>
Just be aware that by compiling the HTML yourself, you're completely bypassing contextual escaping, so you should only do it with your own, secure content.
The problem is that your adding a plain text into DOM. Your filter will just ad an piece of html text which includes the ng-click directive which as far as the browser is concerned, it is just an attribute it cannot understand.
You need to compile the template using the $compile service before injecting it into the dom
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 am developing an app using Angularjs and adding HTML using $sce.trustAsHtml() in my page. I want to call a function in above dynamically added content. My html and script as below.
HTML
<div ng-app="ngBindHtmlExample">
<div ng-controller="ngBindHtmlCtrl">
<p ng-bind-html="myHTML"></p>
</div>
</div>
Javascript
angular.module('ngBindHtmlExample', ['ngSanitize'])
.controller('ngBindHtmlCtrl', ['$scope','$sce', function ngBindHtmlCtrl($scope, $sce) {
$scope.myHTML =$sce.trustAsHtml(
'I am an <code>HTML</code>string with links! and other <em>stuff</em>');
$scope.removeExp = function (){
console.log('dfdfgdfgdfg');
}
}]);
jsfiddle
Click Here to see
It's a bit tricky because ng-bind-html will simply insert plain old html and not bother compiling it (so any directives in the html will not be processed by angular.
The trick is finding a way to compile whenever the template changes. For example, you could create a directive that does this. It would look something like:
.directive('compileTemplate', function($compile, $parse){
return {
link: function(scope, element, attr){
var parsed = $parse(attr.ngBindHtml);
function getStringValue() { return (parsed(scope) || '').toString(); }
//Recompile if the template changes
scope.$watch(getStringValue, function() {
$compile(element, null, -9999)(scope); //The -9999 makes it skip directives so that we do not recompile ourselves
});
}
}
});
You can then use it like this:
<p ng-bind-html="myHTML" compile-template></p>
See the working example here:
http://jsfiddle.net/3J25M/2/