Angular complains about unknown provider with this simple directive? - angularjs

I am adding this my-resize directive:
<body ng-controller="MainCtrl" my-resize="resize">
<p>width:{{width}} height:{{height}}</p>
</body>
,where resize is a function defined in the MainCtrl controller (see: http://plnkr.co/edit/Z8ckbLbRcA6P6XqzU1fx)
The directive is very simple:
app.directive('myResize', function($scope){
return{
restrict: 'A',
scope: {
ngResize: '&'
},
link: function($scope, $elem, $attr){
$scope.$on('resize', ngResize);
}
};
});
Yet I am getting Error: $injector:unpr Unknown Provider and I have no idea why.

var app = angular.module('app', []);
app.directive('myResize', ['$window', function($window) {
return {
link: function(scope, elem, attrs) {
scope.onResize = function() {
scope.height = $window.innerHeight;
scope.width = $window.innerWidth;
}
scope.onResize();
angular.element($window).bind('resize', function() {
scope.onResize();
scope.$apply();
})
}
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div my-resize>
window height: {{height}}
window width:{{width}}<br />
</div>
</div>
$scope can not be injected to directive. You have change the code to inject $scope in controller of directive.
$scope is not a service($scopeProvider is not exist in angular js) it is something special that is injected by angular itself into the controller as a child of $rootScope.
so you cannot explicitly inject it in service,directive...etc.
you can inject it explicitly in the controller of direcitve (not directly to the directive).
http://plnkr.co/edit/mxEMXxIKOrJtRqQa4IQb?p=preview

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>

Angular 1.X - Accessing controller variable through directive using $watch

Within a controller I have an ajax call that populates $scope.main_data. I want to within a directive get that data when it populates. However the two issues I'm having is:
I cannot seem to access $scope.main_data from the directive.
$scope.watch doesn't seem to work because of this.
How can I print the data from the directive once the data arrives?
CONTROLLER:
app.controller('myCtrl', ['$scope','$http', function($scope, $http) {
$scope.main_data = [];
$http.get("some_url").success( function(data) {
$scope.main_data = data;
});
}]);
DIRECTIVE:
app.directive('myDir', function($compile, $rootScope) {
return {
restrict: 'A',
scope: {
items: '=items'
},
link: function($scope, element, attrs, ctrl) {
$scope.$watch($scope.main_data ,function(newValue,oldValue){
if (newValue){
console.log($scope.main_data);
}
});
}
};
});
HTML:
<div ng-controller="myCtrl">
<div my-dir>
<div>
</div>
The directive in the html exists within the controller but for some reason I can't access the controller scope through $scope but $scope.$parent.
You've almost got it.
app.directive('myDir', function($compile, $rootScope) {
return {
restrict: 'A',
scope: {
items: '=items'
},
link: function($scope, element, attrs, ctrl) {
$scope.$watch("items",function(newValue,oldValue){
if (newValue){
console.log($scope.main_data);
}
});
}
};
});
And html:
<div ng-controller="myCtrl">
<div my-dir items="main_data">
</div>
</div>
Even though you can "hack" around and access main_data using series of$parent calls on your scope and using some other methods, just pass it in to your directive with = binding, so it will be updated when controller scope is updated. In fact you don't even need $watch in this case, you will always have actual data.

How do I access ng-controller from directive?

I've written my own directive from which I want to call a method on the parent ng-controller. I've tried doing that by adding require:'ngController' in my directive. However the returned controller is just an empty object. I'm running 1.4.8.
<div ng-controller="myController">
<div my-directive></div>
</div>
app.directive('myDirective', function() {
return {
restrict: 'A',
scope: false,
require: '^ngController',
link: function (scope, $element, attrs, controller) {
//controller is empty object..
};
}
});
Update: My misstake was that I added a method to the controllers scope when I should add it directly to the controller instead.
app.controller('myController', function($scope) {
$scope.myMethod = function() {}; //Not callable from controller passed to directive
this.myMethod = function() {}; //IS callable from controller passed to directive
});
You can call the parent controller via scope if your directive doesn't have an isolated scope, and you don't need to require ngController.
angular.module('app', [])
.controller('controller', function($scope) {
$scope.greet = function() {
return 'hi'
};
})
.directive('testDirective', function() {
return {
restrict: 'E',
template: '<h1>{{ greet() }}</h1>'
};
});
Output:
hi
Plnkr: http://plnkr.co/edit/uGXC5i1GjphcZCPDytDG?p=preview
The function needs to be available in the directive's scope. So if your directive is in the scope of the controller it is:
scope.fn();

how to inject locals to a controller dynamically?

We mostly write our controllers in this fashion:
app.controller('MyCtrl', function($scope, $location) {
//do something with $scope & $location
});
I am writing a directive, and I am faced with a scenario where I have to render a view based on a certain controller instance. The directive will be called as follows:
<my-directive src="srcVar" controller="myctrl"></my-directive>
This directive takes care of loading the template specified by srcVar and instancing the controller using the $controller service. So there are few lines in my code that does like:
$controller(çtrlExp, {'$scope' : scope.$new() });
The above works for simple cases where the controller has only one argument. For the above controller example, you can stuff work the following manner:
var locals = { '$scope' : $scope.$new(), '$location' : $injector.get('$location') };
$controller('MyCtrl', locals);
Now how to write it for a generic case, where the user's controller can include any number of injectable constructs like services, values, constants, etc, all of which are usually defined during module creation.
Ps: if you are looking for workable code...refer my github repo: https://github.com/deostroll/ngFrame/blob/master/app/scripts/viewutils.js . This is still a work in progress sort of library.
You only need to add the $scope in your locals the rest will be done automatically by Angular's DI.
Please have a look at the demo below or this jsfiddle.
angular.module('demoApp', [])
.controller('mainController', MainController)
.directive('myDirective', MyDirective);
function MainController($scope, $location) {
this.testTemplate = 'testTemplate.html';
console.log($location, $scope); // everything available here!
}
MainController.$inject = ['$scope', '$location'];
function MyDirective($compile) {
return {
restrict: 'E',
template: 'hello',
controller: function($scope, $attrs, $controller){
console.log($attrs.controller, $scope);
$controller($attrs.controller, {'$scope': new $scope.$new()});
},
compile: function(element, attrs, $scope) {
var template = angular.element(document.getElementById(attrs.src))
.html();
console.log(template);
element.replaceWith(template);
return function(scope, element, attrs) {
//template is compiled now and in DOM, add scope variable
scope.hello='Hello from directive';
};
}
};
}
MyDirective.$inject = ['$compile'];
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp">
<script type="text/ng-template" id="testTemplate.html">
<div>
<h1>Test template</h1>
{{hello}}
</div>
</script>
<my-directive src="testTemplate.html" controller="mainController"></my-directive>
</div>

AngularJS: include not working from a custom directive

I have a custom directive and I would like to use it to include an html content to the document after clicking on it.
Plunker: http://plnkr.co/edit/u2KUKU3WgVf637PGA9A1?p=preview
JS:
angular.module("app", [])
.controller("MyController", function ($scope) {
})
.directive('addFooter', ['$compile', '$rootScope', function($compile, $rootScope){
return {
restrict: 'E',
template: '<button>add footer</button>',
controller: 'MyController',
link: function( scope, element, attrs, controller) {
element.bind( "click", function() {
scope.footer = "'footer.html'";
})}
};
}])
HTML:
<body ng-app="app">
<script type="text/ng-template" id="footer.html">
FOOTER
</script>
<div ng-controller="MyController">
<add-footer></add-footer>
<div ng-include="footer"></div>
</div>
</body>
Not sure why it is not working, as it worked fine before it was moved into the directive. Outside the directive, I was also referencing to $scope.footer with some link. I tried using $rootScope, but also no effect. Any tips please?
First. Remove unnecessary quote symbols:
element.bind( "click", function() {
scope.footer = "footer.html"; // not "'footer.html'"
});
Second. You should notify angularjs that you have asynchronously updated scope values:
element.bind("click", function() {
scope.$apply(function() {
scope.footer = "footer.html";
});
});
Or like that
element.bind("click", function() {
scope.footer = "footer.html";
scope.$apply();
});

Resources