Passing Function Arguments with Isolated and Share scopes with Angular Directives - javascript

I have 3 directive with isolate scope and share scope and I want pass a function beteween outermost a innermost directive. The outer and middle has isolate scopes and the middle with inner share the scope. Any suggest ?
Pass the functions of my controller as shown below .
<outer on-edit="helloWorld" ng-model="model" ng-repeat="items in items.objects" ></outer>
In my controller:
$scope.helloWorld = function(){
alert('Hello world');
}
My directive:
angular.module('myApp')
.directive('outer', function () {
return {
restrict: 'E',
replace: true,
scope: {
item: "=ngModel",
onEdit: '&'
},
template: '<div><middle on-edit='onEdit'></middle></div>',
controller : function($scope){
$scope.edit = function(){
$scope.onEdit()();
}
}
};
})
.directive('middle', function () {
return {
restrict: 'E',
replace: true,
scope: {
item : '=ngModel',
onEdit : '&'
},
templateUrl: '<div><inner on-edit='onEdit'></inner></div>'
};
})
.directive('inner', function () {
return {
restrict: 'E',
template: '<div><a ng-click='edit()'>Edit</a></div>'
};
})
And this not work, any ideas?
Thanks

This looks a bad design though, but in the middle directive's template you are using inner directive as follows:
<div><inner on-edit='onEdit'></inner></div>
If you look at it, inner directive has no scope, so the attribute on-edit doesn't make sense there.
If you want to use any method that is present in middle directive can be directly used in inner directive because of shared scope. Think of inner directive as a part of html written in some other html file which will be replaced at run time.
So anything you pass to middle directive is implicitly passed to inner.

Related

Multiple directives with individual scopes for one element

Can the one element have multiple directives with individual scopes?
Let's say, we have custom directive's child with the controller's scope and any directive (here is "ng-class"):
<custom-directive data-model="model">
<input class="child" data-ng-class="controllerScopeValue">
</custom-directive>
Now we want to add extra directive with isolated scope to the child. Something like this:
angular.module('core').directive('customDirective', [function($compile) {
return {
scope: {
'model': '='
},
compile: function(templateElement, templateAttrs) {
templateElement.children().attr('data-ng-model', 'directiveScopeValue');
return function($scope) {
$scope.directiveScopeValue = 'directive\'s scope value';
}
}
};
}]);
So, how to keep individual scopes for each directive?
no that is not possible, if you try to do it, you will get error similar to
Multiple directives [myDirective1, myDirective2] asking for new/isolated scope
plunker
ignore below dummy code
app.directive('myDirective1', ['$document',
function ($document) {
return {
restrict: 'A',
replace: false,
scope : {},
and
app.directive('myDirective2', ['$document',
function ($document) {
return {
restrict: 'A',
replace: false,
scope : {},

Creating a directive which inherit parent scope as well as have some additional properties

i read Angularjs documentation .In directives i can use parent scope for current directive by not speciying scope attribute like this
.directive('myCustomer', function() {
return {
restrict: 'E',
templateUrl: 'my-customer.html'
};
});
and i can create a directive with isolated scope in this way
.directive('myCustomer', function() {
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
templateUrl: 'my-customer-iso.html'
};
});
what if i want to inherit all properties of parent scope and want to add some properties related to this directive .i want to create a new scope when this directive is used but not an isolated scope.How to acheive that ??

How to Create nested objects in isolated scope

So I want to create a nested struture on my nested scope inside a directive like this:
angular.module('myAddress').directive('myAddress', [function () {
return {
restrict: 'AE',
controller: 'myAddressController',
templateUrl: 'my-address.html',
scope: {
address: {
'form': '=addressForm',
'model': '=addressModel'
}
}
};
}]);
But I get an exception that undefined is not a function that I don't get if I remove the address nesting.
How do I put attribute arguments inside a named key on my scope?
Also, If I define $scope.address via the controller it doesn't work as well. But what will execute first? The scope: { 'form' = 'addressForm'} part in my directive or the controller's $scope.form?
With the scope property you define which $scope variables should pass to the directive scope and the type of data-binding.
If you want to create an nested structure within the directive $scope, you could create it in the directive controller function.
For example:
angular.module('myAddress').directive('myAddress', [function () {
return {
restrict: 'AE',
controller: 'myAddressController',
templateUrl: 'my-address.html',
scope: {
addressForm: '=', // Two-way databinding
addressModel: '='
},
controller: function($scope){
$scope.address = {
form: $scope.addressForm,
model: $scope.addressModel
}
},
link: function($scope,$element,$attributes){
//Your code here
}
};
}]);
You can also, define $scope.address in the module controller scope. Then your scope property in the directive should be look like this
scope: {
address: '='
}
UPDATE:
Another question is: Does your directive need an dedicated scope? If not you could set the scope property false. Then your directive can access the $scope variables in your module controller.

How can I pass a binding expression as text into a directive isolation scope?

I'm creating a custom directive similar to a list-box. This is my directive definition:
angular.module('Utilities')
.directive('searchList', [
function () {
return {
restrict: 'E',
templateUrl: '/app/scripts/Utilities/search/search.html',
controller: 'SearchCtrl',
scope: {
itemsSource: '=',
itemTemplate: '#',
filterText: '=?',
selectedItem: '=?',
}
};
}
]);
Here's how I want to use it in my view:
<search-list items-source="productsSource"
item-template="{{item.Name}} Selling for: {{item.Price}}"
selected-item="selectedProduct" />
Both productsSource and selectedProduct come from the view's scope (they work fine). I want item-template to be taken straight up as text (there is no item object in the scope used by my view).
Inside SearchCtrl I obtain the items to show in my search-list, and then I want to apply that item-template to each item (through the use of the $compile service).
The problem is that inside SearchCtrl $scope.itemTemplate is equal to Selling for: (the {{}} syntax was resolved, not passed as text)
tl;dr
My search.html template looks like this:
<li ng-repeat="item in itemsDataSource" ng-class-odd="'oddRow'" ng-class-even="'evenRow'">
<div class="searchResultsItem" ng-click="onItemSelected(item)">
<span compile="internalItemTemplate"></span>
</div>
</li>
Since itemTemplate is one-way binding, the SearchCtrl will reassign it to internalItemTemplate.
if (typeof $scope.itemTemplate === 'undefined') {
$scope.internalItemTemplate = '{{item}}';
} else {
$scope.internalItemTemplate = $scope.itemTemplate;
}
The compile directive on the span tag was borrowed from: Angular Docs for $compile
It looks like this:
angular.module('Utilities')
.directive('compile', ['$compile',
function ($compile) {
// directive factory creates a link function
return function (scope, element, attrs) {
scope.$watch(
function (scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function (value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}
]);
If I hard-code $scope.internalItemTemplate in SearchCtrl to be '{{item.Name}} Selling for: {{item.Price}}', than it works.
How do I get my directive to allow the passing of {{}} without trying to resolve it?
Changing the scope type from # to = and then surrounding it in quotes worked.
The directive would look like this:
angular.module('Utilities')
.directive('searchList', [
function () {
return {
restrict: 'E',
templateUrl: '/app/scripts/Utilities/search/search.html',
controller: 'SearchCtrl',
scope: {
itemsSource: '=',
itemTemplate: '=',
filterText: '=?',
selectedItem: '=?',
}
};
}
]);
and the view would look like this:
<search-list items-source="productsSource"
item-template="'{{item.Name}} Selling for: {{item.Price}}'"
selected-item="selectedProduct" />

Call directive method from transcluded content

I'm trying to access a method in a directive from translcuded content. My HTML looks like:
<typeahead class="typeahead" model="customers" filtered-model="customersfiltered" ng-model="selectedcustomer">
<ul>
<li ng-click="select(customer)" ng-repeat="customer in customersfiltered | filter:filter | limitTo:10">
{{customer.firstname}} {{customer.lastname}}
</li>
</ul>
</typeahead>
And my AngularJS directive:
directive('typeahead', function ($filter) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
model: '=',
filteredModel: '='
},
template: '<div class="typeahead"><form><input type="text" autocomplete="off" class="col-lg-12" ng-model="filter"></form><div ng-transclude></div></div>', //
controller: function($scope){
$scope.filterArray = function(filterString){
$scope.filteredModel= $filter('filter')($scope.model, filterString);
}
$scope.clear = function(){
$scope.filteredModel = [];
}
$scope.$watch('filter', function(){
if($scope.filter) {
$scope.filterArray($scope.filter);
} else {
$scope.clear();
}
});
},
link: function ($scope, $element, $attributes) {
$scope.select = function(customer){
console.log("dwadad");
}
}
}
})
The problem here is that I cannot access the select() function inside the link function() from the ng-click event of the transcluded content (list-element).
Have somebody an idea how to solve this?
Here is a Plunker of the current code: Plunker
I think you can't do that. From the Angular docs:
(...) The advantage of transclusion is that the linking function receives a
transclusion function which is pre-bound to the correct scope. In a
typical setup the widget creates an isolate scope, but the
transclusion is not a child, but a sibling of the isolate scope. This
makes it possible for the widget to have private state, and the
transclusion to be bound to the parent (pre-isolate) scope.
In other words, the scope of the transcluded DOM is a sibling, not a child, of the directive's scope. So you can't access it from there, and I think that's correct. Otherwise you wouldn't be able to bind the transcluded content to the right scope.
You can use $$nextSibling:
link: function($scope) {
$scope.$$nextSibling.select = function(customer) {
alert('used isolated scope using $$nextSibling');
}
},

Categories