Apply AngularJS directive (ui-scrollfix) conditionally - angularjs

I'm using ui-scrollfix directive from UI.Utils to make so called sticky header. It works fine.
There are two headers in the app. Main one is constantly on the page and second one appears only in certain pages.
<div id="main-header" ui-scrollfix></div>
<div id="second-header" ui-scrollfix></div>
What I need to do is that ui-scrollfix directive was added or applied to main-header, if second-header is present.
Is that possible to achieve?
Thank you!

I think should do this by wrapping both headers in a directive say headers. And then in the said directive query for the second header, if it exists then apply the ui-scrollfix directive to it.
HTML
<div ng-app='app' ng-controller="aController">
<div headers>
<div id="main-header"> main header </div>
<div id="second-header" ui-scrollfix> second header </div>
</div>
</div>
JS
var app = angular.module('app', []);
app.controller('aController', ['$scope', function (scope) {
}]).directive('uiScrollfix', [function () { // this is just to check that the directive is applied to the element
return {
restrict: 'A',
link: function (scope, el, attrs) {
el.on('click', function () {
console.log(el[0].textContent);
});
}
}
}]).directive('headers', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, el) {
var directiveEl = el[0],
mainHeaderEl = directiveEl.querySelector('#main-header'),
secondHeaderEl = directiveEl.querySelector('#second-header'),
$mainHeaderEl = angular.element(mainHeaderEl);
if (secondHeaderEl) {
$mainHeaderEl.attr('ui-scrollfix', '');
$compile($mainHeaderEl)(scope);
}
}
}
}]);
JSFIDDLE

Related

How can I replace link hrefs with ngClicks in dynamically generated HTML within AngularJS?

I'm consuming dynamically generated HTML from an API which may contain hyperlinks, and I'm wanting to replace the hrefs within them with ngClicks. The following directive appears to modify the HTML as intended when I check it in a DOM inspector, but clicking it does nothing. What am I doing wrong?
app.directive('replaceLinks', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function(scope) {
return scope.$eval(attrs.replaceLinks);
}, function(value) {
element.html(value);
angular.forEach(element.contents().find("a"), function(link) {
link.removeAttribute("href");
link.removeAttribute("target");
link.setAttribute("ng-click", "alert('test')");
});
$compile(element.contents())(scope);
});
}
};
}]);
Instead of removing the href please set it to blank (this will preserve the link css), also the ng-click calling the alert can be done by calling the alert('test') inside of a scope method, why the alert didn't fire, is explained in this SO Answer, please refer the below sample code!
// <body ng-app='myApp' binds to the app being created below.
var app = angular.module('myApp', []);
app.directive('replaceLinks', ['$compile', function($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
angular.forEach(element.find("a"), function(link) {
link.setAttribute("href", "");
link.removeAttribute("target");
link.setAttribute("ng-click", "scopeAlert('test')");
});
$compile(element.contents())(scope);
}
};
}]);
// Register MyController object to this app
app.controller('MyController', ['$scope', MyController]);
// We define a simple controller constructor.
function MyController($scope) {
// On controller load, assign 'World' to the "name" model
// in <input type="text" ng-model="name"></input>
$scope.name = 'World';
$scope.scopeAlert = function(name) {
window.alert(name);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller='MyController' ng-app="myApp">
<div replace-links>
test 1
test 2
test 3
</div>
</div>

Can't add ngClick to attribute directive

I want to dynamically add ngClick to an attribute directive.
javascript
angular.module('app')
.directive('myDirective', ['$log', function ($log) {
return {
restrict: 'A', // PAY ATTENTION TO THIS LINE
compile: function (tElement) {
tElement.attr('ng-click', 'onClick()');
return function postLink(scope) {
scope.onClick = function () {
$log.debug('myDirective is clicked');
}
}
}
}
}]);
markup
<button my-directive>Click Me</button>
From the element inspector of Chrome, I can see that the ng-click attribute is added to the button.
I expect to see the text "myDirective is clicked." in the console when the button is clicked, but actually there's nothing printed. No error is raised. Anyone can help? Thanks in advance.
Rather than using link inside compile use the link function directly as shown below
link: function(scope, element, attrs) {
element.onClick(function(){
$log.debug('myDirective is clicked');
});
}
You can directly add the click handler to the element, you need not bind ng-click directive inside your directive.
Hello please try this one,
HTML:
<div ng-app="angularApp">
<div ng-controller="dirCtrl1">
<button ng-click="clickFun('clicked')">Button</button>
<button my-directive="directive">With directive</button>
</div>
</div>
JS:
.controller('dirCtrl1', function ($scope) {
$scope.clickFun = function (msg) {
console.log(msg);
};
})
.directive('myDirective', function(){
return{
restrict: 'A',
link: function(scope, ele, attr){
var eventName = attr.evetName || 'click';
var mas = attr.myDirective || 'just console';
ele.on(eventName, function(){
console.log(mas);
});
}
};
});

Angular.js passing parameter to directive

I'm a newbie at Angular, so don't be surprise if the answer to this question is pretty basic.
I'm trying to encapsulate a map in a directive, the map will have some custom behavior: I want it to communicate with a Service to retrieve all the points related to a merchant.
So I want to pass the merchant as a parameter to the directive:
This is the HTML:
<div ng-app="myApp">
<div ng-controller="Ctrl1">
<p>Ctrl 1: {{merchant1}}</p>
<map merchantDetails="{{merchant1}}"></map>
</div>
</div>
This is the javascript:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl1', function ($scope) {
$scope.merchant1 = "foo"
});
myApp.controller('Ctrl2', function ($scope) {
$scope.merchant2 = "bar"
})
.directive('map', function () {
return {
restrict: 'E',
link: function (scope, element, attrs, controller) {
scope.merchant2 = attrs.merchantDetails;
},
scope: {
merchantDetails: "#"
},
template: 'Ctrl2: {{merchant2}}'
}
});
The problem is that scope.merchant2 at the template never gets updated.
I would like it to have "foo", or at worst "bar", not blank.
When I debug this in Chrome, controller Ctrl2 initialization is never executed. Why? I would expect it to be done before the link phase.
How do I do to get the "foo" value passed to Ctrl2?
The jsfiddle is available here.
You actually don't need the second controller.
I update the fiddler, please check if it's what you need:
https://jsfiddle.net/e7cfcakv/7/
<div ng-app="myApp">
<div ng-controller="Ctrl1">
<p>Ctrl 1: {{merchant1}}</p>
<map merchant-details="{{merchant1}}"></map>
</div>
</div>
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl1', function ($scope) {
$scope.merchant1 = "foo"
});
myApp.directive('map', function () {
return {
restrict: 'E',
link: function (scope, element, attrs, controller) {
scope.merchant2 = scope.merchantDetails;
},
scope: {
merchantDetails: "#"
},
template: 'Ctrl2: {{merchant2}}'
}
});
You were almoust there
just change de directive attribute :
<map merchant-details="{{merchant1}}"></map>
Angular expects "-" before uppercase
Follow the angular js naming convention
just change the attribute "merchantDetails" to "merchant-details"
<map merchant-details="{{merchant1}}"></map>

Angular directives in cached template not binding properly

Given this directive:
myApp.directive('someDirective', ['$compile', '$templateCache', function($compile, $templateCache) {
return {
restrict: 'A',
link: function(scope, element, attr) {
// Load a template.
var template = $templateCache.get("partials/foo.html");
$compile(template)(scope);
$(element).append(template);
}
};
}]);
and given foo.html:
<div data-foo>Some Text</div>
and the foo directive:
myApp.directive('foo', [function() {
return {
restrict: 'A',
link: function(scope, element, attr) {
console.log('foo1');
$(element).on('click', function() {
console.log('foo2');
});
}
};
}]);
I will always see foo1 in my console but never foo2 when I click on the added element. In fact, though I can see the added elements just fine and can console.log() them just fine, I am unable to ever bind any handlers to them. What am I missing?
A div will have no height unless you put something in it. Change your div to this:
<div data-foo>Some content</div>
Also you need to make the template an element, do this as well right after you create it:
template = angular.element(template);
Here's a jsFiddle showing you: http://jsfiddle.net/c3Wuu/
As a side note, you should not be wiring up a click event, or building a template like this. Have a look at some videos and do angular tutorial unless you know what you're doing.
Edit
This is how you would wire it up using angular practices
in foo directive html:
<div data-foo>
<span data-ng-click="clickOne()">Click One</span>
<span data-ng-click="clickTwo()">Click Two</span>
</div>
In foo link function:
scope.clickOne = function(){
console.log("click one");
};
scope.clickTwo = function(){
console.log("click two");
};
By wiring up your own click function you are circumventing the point of angular.

AngularJs how to call prettyprint?

I'm trying to use prettyprint plugin for my angularjs app.
But cannot make it works. I create a simple directive and call method prettyPrint(), but the code is not formatted.
FIDDLE: http://jsfiddle.net/Tropicalista/yAv4f/2/
App.directive('test', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$(element).prettyPrint()
}
};
});
I modified your code and i'll update here:
http://jsfiddle.net/yAv4f/6/
html:
<div ng-app="Knob" ng-controller="myCtrl">
<pre class="prettyprint linemus"></pre>
<pre class="prettyprint linemus"><!DOCTYPE html><html lang="en"></html></pre>
</div>
javascript:
var App = angular.module('Knob', []);
App.controller('myCtrl', function($scope) {
$scope.dom = '<!DOCTYPE html><html lang="en"></html>'
})
App.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(scope.dom));
}
};
});
Basically, you need to use the file prettify.js to control the execution of the prettify() function, with prettyPrintOne() you can execute it in a specific html text.
And to simplify the use of the directive, like prettify stlyle, i'll suggest restric to 'C' a class and change the the directive name to 'prettyprint'
I've expanded on the previous answers and created a jsfiddle with a working directive that responds in realtime to model changes:
http://jsfiddle.net/smithkl42/cwrgLd0L/27/
HTML:
<div ng-app="prettifyTest" ng-controller="myCtrl">
<div>
<input type="text" ng-model="organization.message" />
</div>
<prettify target="organization"><pre><code class="prettyprint">console.log('{{target.message}}');
</code>
</pre>
</prettify>
</div>
JS:
var App = angular.module('prettifyTest', []);
App.controller('myCtrl', function ($scope) {
$scope.organization = {
message: 'Hello, world!'
};
});
App.directive('prettify', ['$compile', '$timeout', function ($compile, $timeout) {
return {
restrict: 'E',
scope: {
target: '='
},
link: function (scope, element, attrs) {
var template = element.html();
var templateFn = $compile(template);
var update = function(){
$timeout(function () {
var compiled = templateFn(scope).html();
var prettified = prettyPrintOne(compiled);
element.html(prettified);
}, 0);
}
scope.$watch('target', function () {
update();
}, true);
update();
}
};
}]);
h/t to #DanielSchaffer (see Template always compiles with old scope value in directive).
Angular already has this filter built-in for JSON:
<pre>
{{data | json}}
</pre>
If you want to make your own directive, you can use the JSON object directly:
app.filter('prettyJSON', function () {
function syntaxHighlight(json) {
return JSON ? JSON.stringify(json, null, ' ') : 'your browser doesnt support JSON so cant pretty print';
}
return syntaxHighlight;
});
With markup
<pre>
{{data | prettyJSON}}
</pre>
I would like to make a small addition to the directive by #carlosmantilla
You can achieve the same thing without creating the scope variable. I have added this correction on github
This should work properly I assume.
http://jsfiddle.net/yAv4f/143/
var App = angular.module('Knob', []);
App.controller('myCtrl', function($scope) {
$scope.text = "function f1(){int a;}";
})
function replaceText(str)
{
var str1 = String(str);
return str1.replace(/\n/g,"<br/>");
}
app.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(replaceText(element.html()),'',true));
}
};
});
I struggled with this issue for quite a while and wanted to chime in here, albeit much later than everyone else (for real though, who's still using AngularJS in late 2017? This guy.) My specific use-case was where I have code (xml) being dynamically loaded on the page which needed to be pretty printed over and over again.
This directive will take in your code as an attribute, remove the prettyprinted class that's added to the element right after you run prettyPrint(). It will watch for changes on the inputted code from the parent's scope and run the code again when changes occur.
Only dependency is that you have Google's code-prettify. I had it self-hosted, hence the PR.prettyPrint() (as instructed in the docs as of sept 2017).
The directive fully encapsulates the needed Google code-prettify functionality for dynamic content.
angular.module('acrSelect.portal.directives')
.directive('prettyPrint', ['$timeout', function($timeout) {
return {
restrict: 'E',
scope: {
'code': '=',
},
template: '<pre ng-class="{prettyprint: code}">{{ code }}</pre>',
link: function (scope, element, attr) {
scope.$watch('code',function(){
$timeout(function() {
//DOM has finished rendering
PR.prettyPrint();
element.find(".prettyprint").removeClass("prettyprinted");
});
});
}
}
}
]);
The html in the parent template might look
<pretty-print code="selectedCode" ng-show="codeIsSelected"></pretty-print>
Hope this helps another poor soul!

Resources