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
Related
I have a directive with an isolated-scope and one-way binding variable.
yet when i change that variable in the directive controller it updates the parent scope as well.
Example code:
function someDirective() {
return {
restrict: 'E',
replace: true,
scope: {},
bindToController: {
parentVar: '<'
},
templateUrl: templateUrl,
controller: directiveController,
controllerAs: 'vm'
}
}
function directiveController($scope) {
var vm = this;
$scope.$watchCollection('vm.parentVar', doSomething);
function doSomething(newCollection) {
var some_object = {
property1: 1,
property2: 2
};
newCollection.unshift(some_object);
}
}
After I update the passed variable in the directive, I see some_object in other parts of my app.
Thank you.
parentVar is an array reference, so the items that to add to it can be accessed from both parent controller.
If you do not want the changes from directive controller to be reflected, you will have to clone the array before you operate on it.
function directiveController($scope) {
var vm = this;
$scope.$watchCollection('vm.parentVar', doSomething);
function doSomething(newCollection) {
var clonedCollection = newCollection.slice();
var some_object = {
property1: 1,
property2: 2
};
clonedCollection.unshift(some_object);
}
}
i'm trying to build a custom directive in angular; it needs to rielaborate data passed to it before rendering the page, so in the i need to get data passed to my directive through attributes and do some stuff, and finally render the page.
.directive('lpcEdiTable', function($interpolate) {
return {
restrict: "E",
templateUrl: "...",
replace: false,
scope: {
collection: "="
},
link: function(scope, elem, attr) {
//here i need to retrieve data
var myColl = scope.collection; //it's not working
//do some stuff here on myColl
scope.collection = myColl;
}
};
});
so here's how i use the directive:
<lpc-edi-table collection="products"></lpc-edi-table>
where products is a complex object.
in directive template i use the post elaboration data into ng-repeat and other stuff
i tried to follow this but i could not retrieve data into link function
Here is an example of passing an object to a directive
angular.module("myModule", [])
.controller("baseController", ['$scope', function($scope) {
$scope.products = [
"asd",
"asdasd"
];
}])
.directive('myDirective', function() {
return {
restrict: "E",
template: "<p ng-repeat='item in collection'>{{item.attr}}</p>",
scope: {
collection: "="
},
link: function(scope, elem, attr) {
if (!attr.collection) throw new Error("lpc-edi-table directive: 'collection' attribute not found!");
scope.collection = scope.collection.map(function(a) { return {attr: a} });
console.log(scope.collection);
}
};
});
You can call your directive like
<my-directive collection="products"></my-directive>
DEMO https://plnkr.co/edit/cj4oSPRiNo8iYfinIztT?p=preview
For Angular 1.6, I recommend using components. Especially if you don't need to do advanced DOM manipulation.
app.component('lpcEdiTable', {
// $ctrl is controller instance
template: '<div ng-repeat="object in collection">{{object | json}}</div>',
bindings: { //custom attributes
collection: '<' //one way binding. Can also be two way =
},
controller: function(){
//$onInit gets called when the bindings are ready
this.$onInit = function(){
// this.collection is now ready
// safe to manipulate
};
}
});
This can be used like:
<lpc-edi-table collection="products"></lpc-edi-table>
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
};
});
It is possible to define in which scope's property must assign the attributes defined in a directive?
e.g:
angular.module("myMod",[]).
directive("myDir", {
restrict: "E",
scope: {
prop1: "#myProp1",
prop2: "#myProp2",
},
controller: function($scope){
//Here I have prop1 and prop2 assigned to $scope
$scope.prop1;
$scope.prop2;
},
});
But what I want is something like:
angular.module("myMod",[]).
directive("myDir", {
restrict: "E",
scope: {
config: { prop1: "#myProp1" }, //This is invalid
prop2: "#myProp2",
},
controller: function($scope){
//And here, $scope.config.prop1 refers to myProp1
$scope.config.prop1;
$scope.prop2;
},
});
I found a partial solution: using controllerAs and bindToController, controllerAs defines an alias to refer the controller and bindToController binds the isolated scope properties to the controller, then I have:
$scope.alias.prop1
$scope.alias.prop2
But I don't want to bind all properties to controller, because I don't need that. I want to bind some of them to a scope's property, and the rest to another property or directly to the scope.
Why I'm trying to do this?
Because I want to assign:
$scope.config = newConfig;
That is easier than:
$scope.prop1 = newProp1;
$scope.prop2 = newProp2;
I don't have strong idea but just an idea. What about to use something like this?
var cfg = {prop1: '#myProp1'};
angular.module("myMod",[]).
directive("myDir", {
restrict: "E",
scope: {
config: cfg.prop1,
prop2: "#myProp2",
},
controller: function($scope){
$scope.config;
$scope.prop2;
},
});
I don't sure if it is possible to do exactly as you wish (github angular), but may be it would fit something like this:
angular.module("myMod", []).directive("myDir", function () {
return {
restrict: "E",
scope: {
config: "&myConfig",
prop2: "#myProp2"
},
link: function($scope){
console.log($scope.config(), $scope.prop2);
}
}
});
So in HTML you will be able to define config:
<div ng-app="myMod">
<my-dir my-config='{config1: "conf1Val", config2: "conf2Val"}' my-prop2="prop2Val">wver</my-dir>
</div>
http://jsfiddle.net/xrvpoe4r/2/
Another way is to define convention, for example each config should start with "config-" , and make directive, that will create a new field (object) in scope based on such attributes
For example:
http://jsfiddle.net/xrvpoe4r/4/
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>