AngularJs share $scope between controller and controller - javascript

I can't understand why I can't access to $scope properties from the directive, the documentation says that directives doesn't create new scopes, so why can't I acess the $scope properties?
at app.js
'use strict';
var climbingApp = angular.module('climbingApp', []);
climbingApp.controller('ctrl', function($scope) {
$scope.initLocation = {
lat: -54.798112,
lng: -68.303375
};
at google-map.js
'use strict';
climbingApp.directive('gmap', function() {
return {
restrict: 'E',
replace: true,
template: '<div id="map-canvas"></div>',
link: function(scope, iElement, attrs) {
console.log(scope.initLocation);
},
}
})
at index.html
<html ng-app="climbingApp">
<body>
<gmap></gmap>
</body>
</html>
<script src="//code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
<script src="app/app.js"></script>
<script src="app/dependencies/google-maps.js"></script>
The console.log always returns undefined.

You need to inject the scope into the controller
climbingApp.controller('ctrl', function($scope) {

You have to associate the controller with the view (if you use ngRoute) or an element using ng-controller. Something like this:
<body ng-controller="ctrl">...</body>

Inject the $scope in your controller like that :
var climbingApp = angular.module('climbingApp', ['ngRoute']);
climbingApp.controller('ctrl', function($scope) {
$scope.initLocation = {
lat: -54.798112,
lng: -68.303375
};
$scope.markers = [
{
'title' : 'prueba',
'position' : [-54.798112, -68.303375]
},
{
'title': 'prueba',
'position': [-54.798212, -68.303375]
}
];
});
'use strict';
climbingApp.directive('gmap', function() {
return {
restrict: 'E',
replace: true,
template: '<div id="map-canvas"></div>',
link: function(scope, iElement, attrs) {
console.log(scope.initLocation);
},
}
})
working plunkr here : http://plnkr.co/edit/fiK5viToLOVGobnAKZtB?p=preview
The log appears.
$scope or $location have to be pass in the function of your controller as param.

Related

pass dynamic controller to angular directive

i am familiar with the syntax of controller & name but i'm trying to create a generic directive that will get a list of items and for each item i need to specify a controller.
This is my main directive:
function controlPanel() {
var directive = {
restrict: 'E',
replace: true,
scope: {
controlPanelItems: "=sbControlPanelItems"
},
templateUrl: 'control-panel.html',
link: link
};
return directive;
function link(scope, element) {
}
}
Here is the directive template:
<sb-control-panel-item ng-repeat="controlPanelItem in controlPanelItems"
sb-title="controlPanelItem.title"
sb-template-url="controlPanelItem.templateUrl"
sb-control-panel-item-controller="controlPanelItem.controller"></sb-control-panel-item>
My issue is with the sb-control-panel-item-controller attribute.
Angular throws exception when i'm passing variable, it work's great when i'm passing simple string (the name of the controller).
Here is the code of the control-panel-item directive:
function controlPanelItem() {
var directive = {
restrict: 'E',
replace: true,
scope: {
title: '=sbTitle',
templateUrl: '=sbTemplateUrl'
},
templateUrl: 'control_panel_item.html',
controller: '#',
name: 'sbControlPanelItemController',
link: link
};
return directive;
function link(scope, iElement, iAttributes, controller) {
}
}
Maybe there is a way to inject the controller through the link function and then i'll just pass it through the scope?
You can use the $controller service to instantiate whatever controller dynamically inside the directive, check this plunkr.
Just bear in mind that if you wanted to specify a controller statically now, you would need to enclose it in single quotes.
Basically the code would be like:
function MainCtrl() {
this.firstCtrl = 'FirstCtrl';
this.secondCtrl = 'SecondCtrl';
}
function FirstCtrl() {
this.name = 'First Controller';
}
function SecondCtrl() {
this.name = 'Second Controller';
}
function fooDirective() {
return {
scope: {
ctrl: '='
},
template: '<div>{{foo.name}}</div>',
controller: ['$controller', '$scope', function($controller, $scope) {
var foo = $controller($scope.ctrl, {$scope: $scope});
return foo;
}],
controllerAs: 'foo',
link: function ($scope, $element, $attrs, $ctrl) {
console.log($scope.ctrl);
}
};
}
angular
.module('app', [])
.directive('fooDirective', fooDirective)
.controller('MainCtrl', MainCtrl)
.controller('FirstCtrl', FirstCtrl)
.controller('SecondCtrl', SecondCtrl);
and this would be the HTML
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.5.8" data-semver="1.5.8" src="https://code.angularjs.org/1.5.8/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="MainCtrl as main">
<h1>
Test
</h1>
<foo-directive ctrl="main.firstCtrl">
"name: " {{foo.name}}
</foo-directive>
<foo-directive ctrl="main.secondCtrl">
{{foo.name}}
</foo-directive>
</body>
</html>
========================================================================
WRONG OLD ANSWER
From this blog entry seems to be an undocumented property that allows you to do exactly what you need.
function FirstCtrl() {
this.name = 'First Controller';
}
function SecondCtrl() {
this.name = 'Second Controller';
}
function fooDirective() {
return {
scope: {},
name: 'ctrl',
controller: '#',
controllerAs: 'foo',
template: '<div></div>',
link: function ($scope, $element, $attrs, $ctrl) {
}
};
}
angular
.module('app', [])
.directive('fooDirective', fooDirective)
.controller('FirstCtrl', FirstCtrl)
.controller('SecondCtrl', SecondCtrl);
So all you need to do in your directive is add a property name linked to the attribute you will use with the name of your controller.
<foo-directive ctrl="FirstCtrl"></foo-directive>
<foo-directive ctrl="SecondCtrl"></foo-directive>
If your directive, as per your question, needs to be from a property rather than a string, use {{}} notation:
<sb-control-panel-item ng-repeat="controlPanelItem in controlPanelItems"
sb-title="controlPanelItem.title"
sb-template-url="controlPanelItem.templateUrl"
sb-control-panel-item-controller="{{controlPanelItem.controller}}"></sb-control-panel-item>

List of directives inside of an ng-repeat

I'm working on a page that is made up of 5 directives, for example:
<directive-one></directive-one>
<directive-two></directive-two>
<directive-three></directive-three>
<directive-four></directive-four>
<directive-five></directive-five>
I would like to be able to re-order these on demand so that a user can control how their page looks. The only way I could think of doing that was putting them in an ng-repeat:
$scope.directiveOrder = [{
name: "directive-one",
html: $sce.trustAsHtml('<directive-one></directive-one>'),
order: 1
}, ...
HTML:
<div ng-repeat="directive in directiveOrder" ng-bind-html="directive.html">
{{directive.html}}
</div>
This will give me the right tags, but they aren't processed as directives by angular. Is there a way around that? I'm assuming it's something to do with $sce not handling it, but I might be way off?
Try creating a new directive and using $compile to render each directive:
https://jsfiddle.net/HB7LU/18670/
http://odetocode.com/blogs/scott/archive/2014/05/07/using-compile-in-angular.aspx
HTML
<div ng-controller="MyCtrl">
<button ng-click="reOrder()">Re-Order</button>
<div ng-repeat="d in directives">
<render template="d.name"></render>
</div>
</div>
JS
var myApp = angular.module('myApp',[])
.directive('directiveOne', function() {
return {
restrict: 'E',
scope: {},
template: '<h1>{{obj.title}}</h1>',
controller: function ($scope) {
$scope.obj = {title: 'Directive One'};
}
}
})
.directive('directiveTwo', function() {
return {
restrict: 'E',
scope: {},
template: '<h1>{{obj.title}}</h1>',
controller: function ($scope) {
$scope.obj = {title: 'Directive Two'};
}
}
})
.directive("render", function($compile){
return {
restrict: 'E',
scope: {
template: '='
},
link: function(scope, element){
var template = '<' + scope.template + '></' + scope.template + '>';
element.append($compile(template)(scope));
}
}
})
.controller('MyCtrl', function($scope, $compile) {
$scope.directives = [{
name: 'directive-one'
}, {
name: 'directive-two'
}];
$scope.reOrder = function () {
$scope.directives.push($scope.directives.shift());
console.log($scope.directives);
};
});
I hope You can easily done it.
var myApp = angular.module('myApp',[])
.directive('directiveOne', function() {
return {
restrict: 'E',
scope: {},
template: '<h1>{{obj.title}}</h1>',
controller: function ($scope) {
$scope.obj = {title: 'Directive One'};
}
}
})
.directive('directiveTwo', function() {
return {
restrict: 'E',
scope: {},
template: '<h1>{{obj.title}}</h1>',
controller: function ($scope) {
$scope.obj = {title: 'Directive Two'};
}
}
});
myApp.controller('ctrl',function($scope){
$scope.data = [{name:'directive-one'},{name:'directive-two'}];
});
<html ng-app='myApp'>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js"></script>
</head>
<body ng-controller='ctrl'>
<div ng-repeat='item in data'>
<item.name></item.name>
<directive-one></directive-one>
</body>
</html>

AngularJS: Access directive from controller

Background
I have a top level directive which needs to be accessed by a controller. Please consider this Plunk.
Directive
app.directive('topDirective', ['$compile', function($scope){
return {
scope: {},
restrict: 'E',
template: '<h3>Top Directive</h3><p><button ng-click="CallMe()">Click Me</button></p>',
controller: function($scope) {
var self = {};
$scope.CallMe = function(){
alert('Call Me');
};
},
link: function($scope, $elem, $attrs, $ctrl) {
}
};
}]);
Controller that needs access
app.controller('subController', [
'$scope',
function($scope){
var self = {};
$scope.CallDirective = function() {
alert('>>> Replace by call to directive function CallMe (somehow) <<<')
};
}]);
Question
What do I need to do to replace this line:
alert('>>> Replace by call to directive function CallMe (somehow) <<<')
by an actual call to the CallMe() function in the directive?
If not possible directly, is there a way to share functionally that both the directive and controller can use? My first thought would be a service, but it would need to do DOM manipulation in the real scenario, so that's not an option.
Any suggestions?
in Controller emit the event
app.controller('subController', [
'$scope','$rootScope',
function($scope,$rootScope){
var self = {};
$scope.CallDirective = function() {
var data ='This is new data';
$rootScope.$emit('callDirective',data);
};
}]);
and in directive you can do it like
app.directive('topDirective', ['$compile', function($scope){
return {
scope: {},
restrict: 'E',
template: '<h3>Top Directive</h3><p><button ng-click="CallMe()">Click Me</button></p>',
controller: function($scope,$rootScope) {
var self = {};
$scope.CallMe = function(data){
alert(data);
};
$rootScope.$on('callDirective',function(type,data){
$scope.CallMe(data);
});
},
link: function($scope, $elem, $attrs, $ctrl) {
}
};
}]);

Confuse about the scope inherit between directive in AngularJS

I am wondering how to implement the scope inherit between directives.
For example:
<html ng-app="app">
<head>
<title>TEST DRAG</title>
</head>
<body ng-controller="main">
<dragcont>
<dragitem></dragitem>
</dragcont>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script type="text/javascript">
(function(){
var app = angular.module("app", []);
app.controller("main", function($scope){
$scope.name = "Hello";
})
.directive("dragcont", function(){
return {
restrict: "AE",
scope: {
},
controller: function($scope){
$scope.name = "dragcont";
},
link: function(scope, EL, attrs){
}
}
})
.directive("dragitem", function(){
return {
restrict: "AE",
controller: function($scope){
console.log($scope.name);
},
link: function(scope, EL, attrs){
}
}
})
})()
</script>
</body>
</html>
When I run this, it always prints Hello. It seems that dragitem can inherit the scope from main controller, but what if I want it to inherit from dragcont?
Isolate scope is used to "isolate" the inner workings of a directive from its usage. As such, the scope neither inherits from its parent, nor can be inherited from by the child directives and expressions.
So, for the isolate foo directive:
.directive("foo", function(){
return {
scope: {},
link: function(scope){
scope.inner = "hidden from outside";
}
}
})
the child directives and expression will not inherit its isolate scope.
<foo>
<span>{{inner}} will be undefined</span>
</foo>
Using a template:
On the other hand, a template of a directive foo is known to the author of the directive, and so it does use the isolate scope. The following would have worked, if foo had a template:
scope: {},
template: '<span>{{inner}}</span>',
link: function(scope){
scope.inner = "hidden from outside";
}
Using manual "transclusion":
Occasionally, it makes sense to allow the user of the directive to specify a custom template. The author of the directive may also want to expose special "magic" variables to use in the custom template, not unlike $index, $first, etc.. of ng-repeat.
This can be done with a manual transclusion:
scope: {},
transclude: true,
template: '<div>{{header}}</div>\
<placeholder></placeholder>',
link: function(scope, element, attrs, ctrls, transclude){
scope.header = "I am foo"; // still only visible in the template
// create a new scope, that inherits from parent, but a child of isolate scope
var anotherScope = scope.$parent.$new(false, scope);
anotherScope.$magic = "magic";
// transclude/link against anotherScope
transclude(anotherScope, function(clonedContents){
element.find("placeholder").replaceWith(clonedContents);
}
}
Now, you can have access to $magic variable inside the transcluded contents and to the outer scope (assuming it has $scope.name = "John")
<foo>
<div>I can see {{name}} and {{$magic}}</div>
</foo>
The resulting DOM will be:
<foo>
<div>I am foo</div>
<div>I can see John and magic</div>
</foo>
It looks like you are still missing some work to be able to make a directive inherit from another.
I think this code will help you:
http://codepen.io/anon/pen/EaPNqp?editors=101
Also, you might want to read:
http://david-barreto.com/directive-inheritance-in-angularjs/
CODE:
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.data1 = "1";
$scope.data2 = "2";
})​var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.data1 = "1";
$scope.data2 = "2";
})
.directive('myWrapper', function() {
return {
restrict: 'E'
, transclude: true
, scope: true
, template: '<h1>{{ title }}</h1><ng-transclude></ng- transclude><h2>Finished wrapping</h2>'
, controller: function($scope, $element, $attrs){
$scope.title = $attrs.title;
$scope.passdown = $attrs.passdown;
}
};
})
.directive('myInner1', function() {
return {
restrict: 'E'
, require: 'myWrapper'
, template: 'The data passed down to me is {{ passdown }}'
};
})
.directive('myInner2', function() {
return {
restrict: 'E'
, require: 'myWrapper'
, template: 'The data passed down to me is {{ passdown }}'
};
});
.directive('myWrapper', function() {
return {
restrict: 'E'
, transclude: true
, scope: true
, template: '<h1>{{ title }}</h1><ng-transclude></ng- transclude><h2>Finished wrapping</h2>'
, controller: function($scope, $element, $attrs){
$scope.title = $attrs.title;
$scope.passdown = $attrs.passdown;
}
};
})
.directive('myInner1', function() {
return {
restrict: 'E'
, require: 'myWrapper'
, template: 'The data passed down to me is {{ passdown }}'
};
})
.directive('myInner2', function() {
return {
restrict: 'E'
, require: 'myWrapper'
, template: 'The data passed down to me is {{ passdown }}'
};
});
which is found very useful. Make sure you read the comments below the article as well.
Pay attention to the "require" property.
Regards.

Custom Element Directives and attributes

myPage.html
<div ng-controller="MyPageCtrl">
<my-custom-directive arg1="{{currentObj.name}}"></my-custom-directive>
<div>
in myPageCtrl.js (Controller)
app.controller("MyPageCtrl", ["$scope", function ($scope) {
$scope.currentObj = {"name":"Collin"};
}]);
And this is how my directive code looks like
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl"
};
}]);
Finally here's my directive's controller,
app.controller("MyCustomDirCtrl", ["$attrs", function ($attrs) {
var arg = $attrs.arg1;
alert('Arg '+arg);
}]);
The alert just displays "{{currentObj.name}}" and not the value of the name property of currentObj.
Please can you suggest me ways to figure this out.
Thanks.
Not sure why did you use $attrs for a controller. Just use a normal $scope.
myPage.html
<div ng-controller="MyPageCtrl">
<my-custom-directive arg1="{{currentObj.name}}"></my-custom-directive>
<div>
myPageCtrl.js (Controller)
app.controller("MyPageCtrl", ["$scope", function ($scope) {
$scope.currentObj = {"name":"Collin"};
}]);
myCustomDirective
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl"
};
}]);
Directive's controller (change $attrs to $scope here),
app.controller("MyCustomDirCtrl", ["$scope", function ($scope) {
var arg = $scope.arg1;
alert('Arg '+arg);
}]);
Instead of accessing the attr from your controller, you could access it from your directive using the link function.
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl",
link: function(scope, element, attr) {
alert(attr.arg1);
}
};
}]);

Categories

Resources