Angular, trouble getting directive to work - javascript

I seem to be having trouble getting my directive to work, I think I may be registering it incorrectly, but I can't seem to figuire out how. Possibly the naming convention?
Here's what I have -
My Directive -
'use strict';
angular.module('demoApp')
.directive('packeryAngular', ['$rootScope', '$timeout',
function($rootScope, $timeout) {
return {
restrict: 'A',
scope: true,
link: function(scope, element, attrs) {
};
}
]);
On my div, I just call it like so
<div packeryAngular>
However, when I call it just like this, nothing seems to be happening. My first assumption was that I needed to register on my app like so
angular.module('demoApp', [ 'packeryAngular ']
However, when I do that I get the error in the console of :
Error: [$injector:modulerr] Failed to instantiate module packeryAngular due to:
Error: [$injector:nomod] Module 'packeryAngular' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
So, I'm pretty new to this but I'm not entirely sure what I'm doing wrong here, I've tried to copy other working examples and can't seem to get it working. Would appreciate any help, and thank you very much for reading!

You dont need to inject the directive , you just need to use it in html .Second Word of Directive with capital first letter becomes small proceeding with a hyphen,So on all next Capital first letters will become small and will be preceded with hyphen like yourCustomDirective becomes your-custom-directive
<div packery-angular>
get rid of injecting it in module
angular.module('demoApp', []);

You must put the following attribute on your node:
<div packery-angular>
When registering your directives, your names must use the lower camel case form. But HTML does not deal well with upper case letters in attribute names, so AngularJS expects upper case letters to be replaced by a dash followed by their corresponding lower case letter.
So your directive name in javascript code is yourDirectiveName, but in the HTML code, it must be referred as your-directive-name.

Related

Why does ng-click not work?

Moved this question to this post - Angular events, ng-click, do not work with other libraries Dojo, Knockout, KendoUI, ESRI JSAPI
No matter what I try, ng-click does not work. However, onclick works. Inside the scope, wiring up an on click event dos not work. What is going on?
EDIT: I can call the $scope.myClick() from the command line. But it will not hit the breakpoint. It will show an alert window, but if this is called from within HTML directive, the function is not hit.
EDIT 2: heres a plunker - https://plnkr.co/edit/kK3NmWB9wfOopG7m5MYv?p=preview
EDIT 3: Ok, so the plunker works, but the horrible application I need to add angular to must messing with something in angular. Any ideas what could be breaking Angular in an existing app? This other app uses dojo, dojo loaders, and require.js. Everything works, except for the ng-click event.
EDIT 4: I commented out an Init() call from this application which loads Dojo, Kendo UI, Knockout, and ESRI JSAPI components, and this Angular code with ng-click works. My gut feeling is knockout is messing with this. Is it possible to completely isolate Angular from the rest of this application? Any suggestions?
here is the app:
var myApp = angular.module('myApp ', []);
directive:
myApp.directive('rapidReport',
function () {
return {
restrict: 'E'
}
});
<div class="ls-rapidReports">
<div ng-app="myApp">
<div id="rapidreportCtrl" ng-controller="rrController">
<button type="button" ng-click="myClick()">hehe</button>
</div>
</div>
</div>
controller:
myApp.controller('rrController',
function rrController($scope) {
$scope.myClick = function () {
debugger;
};
});
Here is the problem:
var myApp = angular.module('myApp ', []);
which should be:
var myApp = angular.module('myApp', []); //myApp without space before '
-
EDIT: Now I see it fixed in the plnkr. If you infact try to add the space again in myApp declaration you will see the following error message in the console.
Uncaught Error: [$injector:modulerr] Failed to instantiate module myApp due to:
Error: [$injector:nomod] Module 'myApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
As you can deduct from the error log, the app declaration in the script.js wasn't matching with the one in the ng-app directive in index.html, so the app wasn't working.

Dynamic directive controller using name property

I'm trying to implement directive with dynamic controller so that I can bind controller based on some condition just like Todd Motto has shown it here
Everything works fine expect that I can't send object property as a name to the directive like,
<directive-with-dynamic-controller ctrl="someObj.prop"></directive-with-dynamic-controller>
I've even tried this, but to no avail:
<directive-with-dynamic-controller ctrl="{{someObj.prop}}"></directive-with-dynamic-controller>
It gives error like this:
Argument is not a function, got undefined
Any ideas how I can solve this? Or any other way?
Thanks!
Any ideas how I can solve this? Or any other way? Thanks!
The problem is in the order of execution. Something that was left out in the mentioned article is the fact that you cannot pass an expression in the case of setting a "dynamic" controller (so much for dynamic).
If we look at the notation of a directives compile step, you would notice that there is no access to the current $scope.
This is because DOM compilation and controller initialisation happens before the angular parser kicks in and evaluates your expression(s).
As such, you cannot pass a $scope expression into the ctrl attribute, as it is simply a regular DOM attribute at this point in time. In essence, you are passing the raw string into the ctrl attribute.
<my-custom-dir ctrl="foo.bar"></my-custom-dir>
// Error: "foo.bar" is not a controller // is not a function // $minErrObscureStuffThatDoesnHelpYou.
I've been trying to figure out a slick way to get deferred directive compilation running for some time now, but to no avail...
One possible way of getting around this problem (ymmv):
.directive('...', function ($controller) {
controller: function ($scope, $element, $attrs) {
$attrs.$observe('ctrl', function (n, o) {
return $controller(n, {
$scope: $scope,
$element: $element,
$attrs: $attrs
});
});
}
});
Effectively, you would replace a pre-initialised controller (that does nothing) with the controller matched by the name you passed through your attrs.ctrl attribute. However, this would execute post-compilation - so I wouldn't seriously recommend it.
jsfiddle showing the order of execution
tl;dr There is currently no slick way to define a controller for a directive, based on a $scope expression. It has to be a raw string because compilation isn't scoped on a per-component basis, but more so in a 'global' order of execution.
DOM Compilation > Controller initialisation > Scope linkage fiddle

How to fix injector error after Angular minification build?

Before speaking, I read about it made ​​recommendations but still causing error. Look the short code:
function IndexController($scope, $route, $routeParams, $location){
$scope.sfv = project.version.name;
}
angular.module("TkwebMobile", ['ngRoute', 'ngCookies'])
.controller('IndexController', ['$scope', '$route', '$routeParams', '$location', IndexController]);
Only this and the error persists. I'm using grunt to "uglify", and I'm also using the "concat" to unite the codes in a "lib". Even I using "injection" recommended in the Angular documentation.
Uncaught Error: [$injector:modulerr] Failed to instantiate module TkwebMobile due to:
Error: [$injector:unpr] Unknown provider: a
Is it problem of grunt concat? (grunt-contrib-concat)
This is due to your minification, specifically options to minify and mangle your variable names.
Angular determines what value to inject into your functions from the name of the parameters. For example...
angular.factory('MyFactory', function($location) {...});
...will cause angular to look for whatever dependency is named '$location' and then call your function with the $location value passed as it's parameter.
When you minify your javascript, with an option called mangle turned on, then the variable names get mangled. The previous function will turn into this...
angular.factory('MyFactory', function(a) {...});
Angular no longer has the correct parameter name in your source code, as $location is now a. This saves on size of your javascript but totally destroys Angular's implicit dependency resolution. You can solve this in one of two ways.
The first is a feature that angular provides for you.
angular.factory('MyFactory', ['$location', function(a) {...}]);
You provide the names of the parameters in an array, with the last element of the array being the function to inject the parameters into. This way, it doesn't matter what you call your parameters in the code, and the minifier will never change a string literal so Angular always knows what you're wanting.
The other way if you don't want to lose the convenience of not having to use the array notation is to turn off the mangle setting on your minifier. This obviously means you don't minify to the same degree, but ask yourself if it's really worth those extra bytes.
A halfway house is to use something like ngMin, to allow annotation of the array notation into your code and then continue with the minification. This is the best of both world's imo, but increases the complexity of deploying your clientside js.
EDIT
The correct settings to turn off the mangle behaviour in grunt would be this...
uglify: {
options: {
report: 'min',
mangle: false
}
}
But the ngAnnotate package can avoid this. See here for more info. (ngAnnotate is the package that has taken over ngMin)
I've had a similar problem. It turned out that I was not properly identifying and formatting the dependencies for controllers and services, etc. I believe I discovered this by looking at the minification output. (It was rough, let me tell you.)
Basically I had to look through all my files and verify that the dependency list matched what I was using in my controllers and services. It's strange because it worked without the changes.
Here is an example of what I had to do:
Original:
angular.module('FootCtrl', []).controller('FooterController', function($scope) {
$scope.footer = 'Copyright \u00A9 ' + new Date().getFullYear() + name;
});
Fixed
angular.module('FootCtrl', []).controller('FooterController', ["$scope", function($scope) {
$scope.footer = 'Copyright \u00A9 ' + new Date().getFullYear() + name;
}]);
Maybe take note of where I use single quotes vs. double quotes as well.
Hopefully this helps a bit. I'm not sure if you have more code than what is shown- if not, I'm not too sure.
I had this problem and it took me a lot of time to figure out what the issue was because I tried disabling mangling of variable names, using $inject array instead of just passing the services and provider names to function definitions while relying on angular implicit dependency injection but still the problem persisted. It turned out that in one of my controller which uses IIFE, was missing semicolon at the end. Look at the code below.
Before:
(function(){
})()
The above code works okay before minification due to automatic semicolon insertion but it breaks after minification because the absence of semicolon screw things up. So after correction it looked like below.
Correct:
(function(){
})();
This fixed my problem. I hope this might help some one
Note: I use grunt useminPrepare, usemin, copy, uglify and ngAnnotate.

Adding directives to an element using the compile function

I'm trying to create a simple directive to avoid having bulky elements for hovering. This is what I have:
app.directive('ngHover', function(){
return {
compile: function($element,attr) {
$element.attr('ng-mouseenter',attr.ngHover + ' = true')
.attr('ng-mouseleave',attr.ngHover + ' = false')
.removeAttr('ng-hover');
}
}
})
The resulting element is what I would have wrote (and would have worked) but it doesn't seem to be added before angular uses $compile. I could use $compile manually but I want to understand why this doesn't work.
DEMO
This is how the compiler of Angular works.
Compiler is an Angular service which traverses the DOM looking for
attributes. The compilation process happens in two phases.
Compile: traverse the DOM and collect all of the directives. The
result is a linking function.
Link: combine the directives with a scope and produce a live view...
Which means that by the time when you are inside of compile function where you add the attributes, it's over and compiler will never discover and recognise ng-mouseenter and ng-mouseleave as directives. In order to achieve this you will need to trigger another round of compilation with $compile, as you said.
Also see this message and the whole thread. There you can see that it would work if you were setting extra directives on children of the current element, but not on itself.

Using AngularJS with innerHTML

I have some AngularJS stuff that holds a bunch of arrays and data. Once a user uploads a file, the file gets parsed up and saved into the scope with its different arrays. However, after all of this and the file is held in the scope, I try to update the innerHTML, but the AngularJS code does not work. I use ng-repeat to create a table based on the arrays, but it remains a single cell with content looking like {{column}} and the like.
I have had extreme difficulty using directives and templates because my index.html says that app and module, etc, are undefined when I do something such as app.directive(...
The significant parts of my index.html file include:
<html ng-app>
... //once a file is uploaded, total.js is called;
//this was so the app didn't try to run before a file was uploaded
<div id="someStuff">This will become a table once a file is uploaded</div>
This is a simple example of how my scope is set up in total.js:
function sheet($rootScope, $parse){
$rootScope.stuff = text;
//text is a variable that contains the file's contents as a string
};
document.getElementById('someStuff').innerHTML="<div ng-controller='sheet'>{{stuff}}</div>";
The HTML changes but instead of printing the file's contents, it only prints {{stuff}}.
How can I get the innerHTML to understand that it contains AngularJS, preferably without using a partial or a directive, unless you can thoroughly explain where I'd input it and the syntax of it.
Edit 1:
I have tried using $compile but it is marked as undefined. I looked at this to figure out the compile problem, but I don't understand rtcherry's syntax, and how I should apply it to my own code.
Edit 2:
I still receive $compile undefined errors when I include it like so:
function sheet($rootScope, $parse, $compile){...};
document.getElementById('someStuff').innerHTML=$compile("<div ng-controller='sheet'>
{{stuff}}</div>")(scope);
Edit 3:
While itcouldevenbeaboat's comment was extremely unhelpful, I decided I should perhaps show you the directive way I attempted to do it.
I included this code under my sheet function:
var app = angular.module('App', []);
app.directive('spreadsheeet', function($compile){
return{
templateUrl: innerHTML.html
}
});
Where innerHTML contains <div ng-controller='sheet'>{{stuff}}</div>and on index.html I've included <div spreadsheet></div>
With this, I receive no errors, but the text does not show up, neither as {{stuff}} or as the file's contents. Even when I do something simple, such as provide template: "<h2>Hello!</h2>" instead of a templateUrl, I cannot get Hello! to print.
It works for me
document.getElementById('someStuff').innerHTML = "<div ng-controller='sheet'>{{stuff}}</div>";
$compile( document.getElementById('someStuff') )($scope);
Simple solution (it will work 100%):
In controller, don't forget to inject $document in controller as a dependency, like this:
App.controller('indiaController', function ($scope, $document,$filter, $location) {
var text_element = angular.element($document[0].querySelector('#delhi');
text_element.html('your dynamic html placed here');
}

Categories

Resources