OK, so I have a directive which takes attributes and reads it (and writes it out).
Here is the plunker: http://embed.plnkr.co/IkKPLahPc9yqeHWEQUG3/
I think it's because of the controller: ctrl inside main-directive.js which has nothing whereas the actual action is happening inside the isolated directive's controller controller.
Here is the main-directive.js:
var app = angular.module('testapp.directive.main', ['main']);
app.directive('myCustomer', function() {
var controller = ['$scope', function($scope) {
$scope.dan = { 'name': 'Dan', 'nationality': 'ESP' };
// scope from here obv...
}];
var template = 'Getting attribute value of =getInfo... {{getInfo.name}} from {{getInfo.nationality}}';
return {
restrict: 'E',
controller: controller,
scope: {
getInfo: "=info"
},
template: template
};
});
app.controller('ctrl', function($scope) {
})
and here's my template:
<div ng-controller="ctrl">
<my-customer info="dan">
</my-customer>
</div>
Why is my directive not reading the attribute of info?
You're right, the $scope.dan object needs to be in the ‘ctrl’ controller scope and pulled out of the isolate directives controller scope.
app.controller('ctrl', function($scope) {
$scope.dan = { 'name': 'Dan', 'nationality': 'ESP' };
})
This is applicable to the method of two-way data binding that you have set up for getInfo used by "=info"
The way that is coded, it is expecting the ctrl controller to have a property called "dan" on its scope. If you are just passing in the string 'dan', you want to change your directive to use # instead of =
app.directive('myCustomer', function () {
var controller = ['$scope', function ($scope) {
$scope.dan = {'name': 'Dan', 'nationality': 'ESP'};
// scope from here obv...
}];
var template = 'Getting attribute value of =getInfo... {{getInfo.name}} from {{getInfo.nationality}}';
return {
restrict: 'E',
controller: controller,
scope: {
getInfo: "#info" //<--NOTE THE CHANGE HERE
},
template: template
};
});
Related
I want to update the scope variable in a a $watch call and then when this is updated have it reflect into ng-repeat so everytime it's updated ng-repeat updates.
Here is a plunker of all the code.
https://plnkr.co/edit/LB8CCvtEZG76D8plMgu6?p=preview
link: function($scope, $el,$attrs) {
$scope.$watch(function(scope){
return PlayerlistS.getList();
},
function (newVal, oldVal) {
console.log("CHANGED");
/*I want this to contain the new array and then the ng-repeat must update with the new values.*/
$scope.players = newVal;
}, true);
}
For this case you don't even need $watch. You've misspelled factory name in directive, by having that array inside service & call it in a controller then it'll automatically updated. so your directives code can be
app.directive("player", ['PlayerListS', function (PlayerListS) {
return {
restrict: 'E',
scope: {
person:'#person',
add:'&add'
},
replace: false,
templateUrl: "player.html",
controller: function($scope, $element, $compile) {
$scope.add = function(name) {
PlayerListS.addToList(name);
var get = PlayerListS.getList();
$scope.newName = "";
console.log(get);
}
}
};
}]);
Then in controller you can have $scope.players = PlayerListS.getList(); and just have that array inside your factory. Here's working plunker version of your requirement: https://plnkr.co/edit/BJofKoeIplaNjoy3k5TW?p=preview
Let us say I have this html:
<div ng-controller="MyCtrl">
<br>
<my-directive my-name="name">Hello, {{name}}!</my-directive>
</div>
with this simple controller:
myApp.controller('MyCtrl', function ($scope) {
$scope.name = 'Superhero';
});
And I have a directive in which I want to change the 'name' using require like this:
myApp.directive('myDirective', function($timeout) {
var controller = ['$scope', function ($scope) {
$scope.name = "Steve";
}];
return {
restrict: 'EA',
require: 'myName',
controller: controller,
link: function(scope, element, attrs, TheCtrl) {
TheCtrl.$render = function() {
$timeout(function() {
TheCtrl.$setViewValue('StackOverflow');
}, 2000);
};
}
};
});
But throws:
Error: No controller: myName
Here is the fiddle
But if I implement it using ng-model, works. Look here in this other fiddle
I have read that if you use 'require' in a directive, you need to have a controller for it.
So:
What I'm doing is wrong? It is not in this way? I need to do any other thing?
Well finally I got it.
Essencially what I'm trying to do is something called: 'Communication between directives using controllers'. I have found an article explaining this, and helped me a lot:
The view:
<div ng-controller="MyCtrl">
<br>
<my-directive my-name>Hello, {{name}}!</my-directive>
</div>
As you see above, there are two directives: my-directive and my-name. I will call inside my-directive a function from the controller of my-name directive using require.
myDirective:
myApp.directive('myDirective', function($timeout) {
return {
require: 'myName',
link: function(scope, element, attrs, myNameCtrl) {
$timeout(function() {
myNameCtrl.setName("Steve");
}, 9000);
} // End of link
}; // return
});
myName:
myApp.directive('myName', function($timeout) {
var controller = ['$scope', function ($scope) {
// As I tried, this function can be only accessed from 'link' inside this directive
$scope.setName = function(name) {
$scope.name = name;
console.log("Inside $scope.setName defined in the directive myName");
};
// As I tried, this function can accessed from inside/outside of this directive
this.setName = function(name) {
$scope.name = name;
console.log("Inside this.setName defined in the directive myName");
};
}];
return {
controller: controller,
link: function(scope, element, attrs, localCtrl) {
$timeout(function() {
localCtrl.setName("Charles");
}, 3000);
$timeout(function() {
scope.setName("David");
}, 6000);
} // End of link function
};
});
Interesting and works like a charm. Here is the fiddle if you want to try it out.
Also, you can get communication between directives using events. Read this answer here on SO.
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) {
}
};
}]);
Is it possible to pass a parameter to a directive and to set that value as the directive scope?
Example:
angular
.module('app', [])
.controller('CTRL', function($scope) {
$scope.some_value = {
instance1: {
key1: 'value11',
key2: 'value12'
},
instance2: {
key1: 'value21',
key2: 'value22'
},
};
})
.directive('uiClock', function() {
return {
restrict: 'E',
scope: {},
template: template,
link: function(scope, element, attr) {
// scope should now contain either (first directive)
// {
// key1: 'value11',
// key2: 'value12'
// }
// or (second directive)
// {
// key1: 'value21',
// key2: 'value22'
// }
console.log(scope);
}
};
});
<div ng-controller="Ctrl">
<ui-clock ng-bind="some_value.instance1"></ui-clock>
<ui-clock ng-bind="some_value.instance2"></ui-clock>
</div>
The reason I want to do this is I have multiple instances of same directive and each should modify the value passed as parameter from the parent scope.
Any thoughts?
You should use the two-way data binding.
In your directive, you can specify an isolate scope, and use the = syntax, which is pretty useful.
Controller
(function(){
function Controller($scope) {
$scope.some_value = {
instance1: {
key1: 'value11',
key2: 'value12'
},
instance2: {
key1: 'value21',
key2: 'value22'
},
};
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Directive
(function(){
function directive($compile) {
return {
restrict: 'E',
scope: {
data: '='
},
templateUrl: 'template.html',
link: function(scope, element, attr) {
var elm = angular.element(element);
//For all key in scope.data
Object.keys(scope.data).forEach(function(key){
//Create a new property for our isolate scope
scope[key] = scope.data[key];
//Add attr to our element
elm.attr(key, scope[key]);
});
//Remove our data attribute
elm.removeAttr('data');
//Then we can access to scope.key1 & scope.key2
console.log(scope.key1);
console.log(scope.key2);
}
};
}
angular
.module('app')
.directive('directive', directive);
})();
Template
<div>Key 1 : {{key1}}</div>
<div>Key 2 : {{key2}}</div>
Then you can call your directive, by passing specific data to our isolate scope. If you want, you can remove data attribute for the parent element and replace it by the value of your object.
HTML
<body ng-app='app' ng-controller="ctrl">
<directive data='some_value.instance1'></directive>
<directive data='some_value.instance2'></directive>
</body>
If you check your directive element, the data attribute will be removed and replace by key1 = value... etc ...
You can see the Working Plunker
In my directive, I'm instantiating an object.
I'd like to pass this object to the scope of the controller I associate with the directive. How do I do that?
Please keep in mind this is an isolated code for you to understand the issue.
In the actual issue it won't help to instantiate that object inside of the controller.
I know that the scope object in the directive is for passing values that are specified in the HTML, I wrote it that way to help you understand what I'm trying to do.
angular.module('test', [])
.controller('test', ['$scope', function($scope) {
alert($scope.obj); //Needs to contain {value: 'bla'}
}])
.directive('pTest', ['$compile', function($compile) {
var object = {value: 'bla'};
return {
scope: {
obj: object //how can I do that?
},
controller: 'test'
};
}]);
You can do this in the link function of the direction. Since you want to set the value on the scope, you can use the scope parameter of the link function. You can also set the object on the controller, since The fourth argument (optional) argument to the link function is the controller for the directive.
.directive('pTest', ['$compile', function($compile) {
var object = {value: 'bla'};
return {
controller: 'test',
link: function(scope, elements, attrs, controller) {
scope.obj = object;
// or
controller.obj = object;
}
};
}]);
Now that assume you don't want to isolate your scope by using a "scope" member in the return of your directive. From your example I don't think you actually want an isolated scope. (Regardless, the link function would work there too.)
You can have two solution
Solution 1: use '=' in isolated scope, it binds a local/directive scope property to a parent scope property.
.directive('ptest', ['$compile', function($compile) {
var object = {value: 'changed value'};
return {
scope: {
iobj:"="
},
template : "<div>{{iobj.value}}<div>",
link: function(scope,elem,attr){
scope.iobj=object ;
}
};
}]);
in html
<div ng-controller="testCtrl">
<div ptest iobj="object"></div>
</div>
Solution 2: use $controller service and make testCtrl as parent and copy its all scope to controllers scope
.directive('ptest', ['$compile', function($compile,$controller) {
var object = {value: 'changed value'};
return {
controller:function($scope,$controller){
$controller('testCtrl', {$scope: $scope});
console.log($scope.object.value);
$scope.object = object;
}
};
}]);
working example for '=' solution 1 :
angular.module('test',[])
.controller('testCtrl',function($scope){
$scope.object = {value:'intial value'};
})
.directive('ptest', ['$compile', function($compile) {
var object = {value: 'changed value'};
return {
//replace:true,
scope: {
iobj:"="
},
template : "<div>{{iobj.value}}<div>",
link: function(scope,elem,attr){
scope.iobj=object ;
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="testCtrl">
{{object.value}}
<div ptest iobj="object"></div>
</div>
Working example for solution 2 with $controller
angular.module('test',[])
.controller('testCtrl',function($scope){
$scope.object = {value:'intial value'};
})
.directive('ptest', ['$compile', function($compile,$controller) {
var object = {value: 'changed value'};
return {
controller:function($scope,$controller){
$controller('testCtrl', {$scope: $scope});
console.log($scope.object.value);
$scope.object = object;
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="testCtrl">
{{object.value}}
<div ptest ></div>
</div>