AngularJS post process links - javascript

I have a laravel app that spits out the default pagination, however it adds "page" to every link and ignores what is currently in the query string. This is not a laravel question though just want to explain the issue.
Basically I need to add some sort of handler for the pagination links. The rest of the query string is handled via controls I made by creating a directive, so they are easily accessible. Through the controller I can easily create the "proper" url however I want to figure out the best way to do it.
I hear a lot that doing anything with the DOM itself is best avoided in angularJS because its not jQuery, so what is the best practice way to handle this issue in angularjs?
in jQuery I would do this.
jQuery('.pagination-wrap a').on('click', function(event) {
event.preventDefault();
var link = generateProperLink(jQuery(this).attr('href'));
window.location.href = link;
});
But how would I do that in an angular controller (minus the obvious use jQuery).

Nothing wrong with using jQuery in AngularJS, you just need to let AngularJS know if it needs to do anything. That could be as simple as a $scope.$apply() in the callback of jQuery event handler (or any non-angular construct), or something more complex, like compiling DOM changes in the context of a scope.
In your case, does angular need to know the "proper link"? If not, you could wrap your code in an attribute directive.
The following is illustrative, I haven't tested it
(function(app, $) {
app.directive('fixUrl', fixUrl);
function fixUrl() {
return {
restrict: 'A',
link: link
};
function link(scope, element, attributes) {
$(element).on('click', function(event) {
event.preventDefault();
var link = generateProperLink(element);
window.location.href = link;
});
}
function generateProperLink(element){
return $(element).attr('href');
}
}
}(angular.module('app'), jQuery));
Then...
<a class='pagination' fix-url>Whatever</a>

I think you can use angular.element jqlite which is subset of jquery and it has most of the jquery methods u can use , refer https://docs.angularjs.org/api/ng/function/angular.element
You can use directive for the reusable components
<a id="someID" urlUsage><a>
//directive
App.directive('urlUsage',function(){
var generateProperLink = function(ele){
return ele.attr('href');
}
return {
restrict: 'A',
link:function(scope,ele,attrs) {
ele.on('click',function(){
event.preventDefault();
var link = generateProperLink(ele);
window.location.href = link;
})
}
}
})

Related

Angular & D3.js: Why is element[0] undefined?

I am working on an angular application and just starting to work with D3.js.
I am following along on a tutorial to create custom directives.
<my-chart></my-chart>
I am doing this:
...
.directive('myChart', function () {
function link() {
var svg = d3.select(element[0]).append('svg');
...
return {
'link': link,
'restrict': 'E'
}
})
...
But I am always getting back ReferenceError: element is not defined
If I give my directive an id, then everything works:
...
var svg = d3.select(document.getElementById('myChart')).append('svg');
...
I am not sure why as since I am able to talk to the element when it has an id, I don't think it's a timing problem. But, I don't know what else it could be. Any suggestions are greatly appreciated!
Currently you haven't specified element in link function, and you were looking for element which is undefined. You should have element in your link function to get access to angular compiled DOM.
function link(scope, element, attrs) {

Directive at angularjs and custom method/html

I have this code:
<body ng-controller="testController">
<div test-directive transform="transform()">
</div>
<script type="text/ng-template" id="testDirective.html">
<div>
<p>
{{transform()}}
</p>
</div>
</script>
<script>
angular.module("Test", [])
.directive("testDirective", function() {
return {
templateUrl: "testDirective.html",
scope: {
transform: "&"
},
link: function(scope) {
}
};
})
.controller("testController", function($scope) {
$scope.transform = function() {
return "<a ng-click='somethingInController()'>Do Something</a>";
};
$scope.somethingInController = function() {
alert("Good!");
};
});
</script>
</body>
So basically what I want to accomplish is to create a directive with a method that will be called from the controller. And that method will do something with the values passed (in this example it does not receives nothing, but in the real code it does).
Up to that point is working. However, the next thing I want to do is create an element that will call a method in the controller. The directive does not knows what kind of element will be (can be anything) nor what method will be. Is there any way to do it?
Fiddle Example:
http://jsfiddle.net/abrahamsustaita/C57Ft/0/ - Version 0
http://jsfiddle.net/abrahamsustaita/C57Ft/1/ - Version 1
FIDDLE EXAMPLE WORKING
http://jsfiddle.net/abrahamsustaita/C57Ft/2/ - Version 2
The version 2 is now working (I'm not sure if this is the way to go, but it works...). However, I cannot execute the method in the parent controller.
Yes. However there is a few problems with your code. I will start by answering your question.
<test-directive transform='mycustommethod'></test-directive>
// transform in the directive scope will point to mycustommethod
angular.module('app').directive('testDirective', function() {
return {
restrict: 'E',
scope: {
transform: '&'
}
}
});
The problem is that printing the html will be escaped and you will get < instead of < (etc.). You can use ng-bind-html instead but the returned html will not be bound. You will need to inject the html manually (you can use jquery for this) in your link method and use var compiled = $compile(html)(scope) to bind the result. Then call ele.after(compiled) or ele.replace(compiled) to add it to your page.
I finally get to get it working.
The solution is combined. First of all, I needed to add another directive to parse the element I wanted:
.directive("tableAppendElement", function ($compile) {
return {
restrict: "E",
replace: true,
link: function(scope, element, attrs) {
var el = angular.element("<span />");
el.append(attrs.element);
$compile(el)(scope);
element.append(el);
}
}
})
This will receive the element/text that will be appended and then will registered it to the scope.
However, the problem still exists. How to access the scope of the controller? Since my directive will be used by a lot of controllers, and will depend on the model of the controller, then I just set scope: false. And with that, every method in the controller is now accessible from the directive :D
See the fiddle working here. This also helped me because now, there is no need to pass the transform method, so the controller can be the one handling that as well.

angularjs - Reload all directives on page after dynamically updating them

I have a bunch of directives in an html page, let's say the directive is <my-directive></my-directive>. These directives are added or changed outside of AngularJS, e.g. a jQuery function will simply append a new <my-directive></my-directive> to the page.
Here's my question - after appending the new <my-directive></my-directive>, the directive doesn't actually do anything - it's just a tag without any functionality.
How can I force Angular to recognize that a new tag has been added? I've tried mucking around with scope.apply but haven't had any luck.
Thanks!
I think you're looking for angulars $compile method:
myApp.directive('addonclick', function($compile){
return {
restrict: 'A',
link: function(scope, element, attrs){
var html = attrs.addonclick; //or something else
element.on("click", function(){
$(element).append($compile(html)(scope));
})
};
};
});
EDIT:
Try getting the scope first:
var scope = angular.element("yourElement").scope();
Then get and call the compile service:
var compile = angular.element("yourElement").injector().get("$compile"); //or
var compile = angular.injector(["moduleName"]).get("$compile");
$("yourElement").append(compile("<my-directive/>")(scope));

What are the best practices for using angular DI with libraries like Modernizr, lodash, and jquery plugins?

I've noticed that it is not common to wrap third party scripts in a angular js provider for dependency injection and I am not sure why. I am new to angular and trying to figure out what the best way to leverage the DI with jquery plugins, lodash, modernizr, etc...
Consider this example I found:
var App = angular.module('Toolbar', []);
App.directive('toolbarTip', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$(element).toolbar(scope.$eval(attrs.toolbarTip));
}
};
});
http://jsfiddle.net/codef0rmer/TH87t/
This seems to work totally fine, but would it be better to create a value, constant or other provider for the toolbar tip jQuery plugin than inject that into the directive? Also, should this directive require jQuery (not jQlite), should that be wrapped in a provider and injected as well?
Similarly with modernizr and lodash, would this be considered the best way to appraoch DI with those libraries?
With Modernizr:
angular.module('someApp', [])
.provider('Modernizr', function () {
this.$get = function () {
return Modernizr || {};
};
})
.factory('cgEvents', function(Modernizr) {
return {
buttonPressedEvent : function() {
if ( Modernizr.touch ) {
return 'touchstart';
} else {
return 'click';
}
}
};
})
With lodash:
angular.module('someApp', [])
.factory('_', function() {
return window._; // assumes lodash has already been loaded on the page
});
.directive('cgFillViewPort', function (_) {
return {
restrict: 'A',
link: function(scope, $elm) {
var resizer = function() {
//code fired on resize...
};
$(window).resize( _.throttle( resizer, 100 ));
}
};
})
Is this the a valid way of using dependency injection? Any insight on this would be greatly appreciated!
I think you're on the right path. So far I've been doing the same and worked ok(but as my application gets bigger I will refactor my code to use requirejs. Keep in mind, though, that jquery can cause memory leaks in angular. Since AngularJS apps are single-page apps and jquery objects are outside the angular scope, they don't get destroyed even though the route gets changed! So be careful to delete your jquery references in directives when the $destroy event is fired.
More details on this issue here.

Call AngularJS function on document ready

Is there a way to call an Angular function from a JavaScript function?
function AngularCtrl($scope) {
$scope.setUserName = function(student){
$scope.user_name = 'John';
}
}
I need the following functionality in my HTML:
jQuery(document).ready(function(){
AngularCtrl.setUserName();
}
The problem here is my HTML code is present when page is loaded and hence the ng directives in the html are not compiled. So I would like to $compile(jQuery("PopupID")); when the DOM is loaded.
Is there a way to call a Angular function on document ready?
Angular has its own function to test on document ready. You could do a manual bootstrap and then set the username:
angular.element(document).ready(function () {
var $injector = angular.bootstrap(document, ['myApp']);
var $controller = $injector.get('$controller');
var AngularCtrl = $controller('AngularCtrl');
AngularCtrl.setUserName();
});
For this to work you need to remove the ng-app directive from the html.
The answer above although correct, is an anti-pattern. In most cases when you want to modify the DOM or wait for the DOM to load and then do stuff (document ready) you don't do it in the controller but in he link function.
angular.module('myModule').directive('someDirective', function() {
return {
restrict: 'E',
scope: {
something: '='
},
templateUrl: 'stuff.html',
controller: function($scope, MyService, OtherStuff) {
// stuff to be done before the DOM loads as in data computation, model initialisation...
},
link: function (scope, element, attributes)
// stuff that needs to be done when the DOM loads
// the parameter element of the link function is the directive's jqlite wraped element
// you can do stuff like element.addClass('myClass');
// WARNING: link function arguments are not dependency injections, they are just arguments and thus need to be given in a specific order: first scope, then element etc.
}
};
});
In all honesty, valid use of $document or angular.element is extremely rare (unable to use a directive instead of just a controller) and in most cases you're better of reviewing your design.
PS: I know this question is old but still had to point out some best practices. :)

Categories

Resources