export function triMenuItemDirective() {
var directive = {
restrict: 'E',
require: '^triMenu',
scope: {
item: '='
},
// replace: true,
template: require('./menu-item-dropdown.tmpl.html'),
controller: triMenuItemController,
controllerAs: 'triMenuItem',
bindToController: true
};
return directive;
}
I need to load different html depending on item.
With the old way you could do:
template: '<div ng-include="::triMenuItem.item.template"></div>',
And in Controller
triMenuItem.item.template = 'app/components/menu/menu-item-' + triMenuItem.item.type + '.tmpl.html';
How do I achive this with webpack?
Something like
template: require('./menu-item-{{triMenuItem.item.type}}.tmpl.html'),
I think that to do this, you have at least three different approaches:
1- Use $templateCache and then pass a string variable to ng-include
.directive('myDirective', ['$templateCache', function ($templateCache) {
return {
scope: {
item: '='
},
template: '<div ng-include="content"></div>',
link: function (scope) {
$templateCache.put('a.tpl.html', require('./a.html'));
$templateCache.put('b.tpl.html', require('./b.html'));
scope.content = (scope.item === 'a') ? 'a.tpl.html' : 'b.tpl.html';
}
}
}]);
2- Use ng-bind-html.
app.directive('myDirective', ['$sce', function ($sce) {
return {
scope: {
item: '='
},
template: '<div ng-bind-html="content"></div>',
link: function (scope) {
if(scope.item === 'a')
scope.content = $sce.trustAsHtml(require('./a.html'));
}
}
}]);
3- Use ng-if. Maybe the less dynamic solution of the three, but is pretty simple if your requirements let you do it.
app.directive('myDirective', function () {
return {
scope: {
bool: '='
},
template: `
<div>
<div ng-if="item === 'a'">${require('./a.html')}</div>
<div ng-if="item === 'b'">${require('./b.html')}</div>
</div>
`
}
});
Related
I use directive:
return {
restrict: 'E',
scope: {
isread: "=isread"
,....
templateUrl: 'list.html',
Where list.html is:
<span ng-show="isread"></span>
And parent trmplate from where I call directive:
<div class="top-menu" ng-controller="NotificationController">
<notification-list isread="is_read"></notification-list>
</div>
And NotificationController:
scope.makeRead = function() {
$scope.is_read = true;
}
Why when I change $scope.is_read = true; in controller it is not changed in directive in variable isread?
return {
restrict: 'E',
scope: {
isread: "="
,....
templateUrl: 'list.html',
I have an object which consists of multiple arrays:
$scope.myArrays = {
array1: ['Pizza', 'Spaghetti'],
array2: ['Lasagne', 'Schnitzel']
};
Moreover, I have a custom directive to which I want to pass this object myArrays and bind those arrays to scope variables:
<my-directive my-data="myArrays"></my-directive>
myApp.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
arrayOne: '=myData.array1',
arrayTwo: '=myData.array2'
},
link: function(scope, elem) {
// get access to scope.array1 and scope.array2
}
};
});
All together in a fiddle for you to play around!
Is there a way to bind the arrays directly or do I need to bind arrays: '=myArrays' and access them like arrays.array1?
Binding has to be one to one, you cannot do that. Yes, you will have to access the arrays inside your directive.
myApp.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
myData: '='
},
link: function(scope, elem) {
scope.arrayOne = scope.myData.array1;
scope.arrayTwo = scope.myData.array2;
}
};
});
You can directly access scope.myData.array1 and scope.myDate.array2 inside the directive template if you dont have to process these arrays in the link function and get rid of it.
You could do this by just assigning them to scope fields:
myApp.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
myData: '='
},
link: function(scope, elem) {
scope.arrayOne = myData.array1;
scope.arrayTwo = myData.array2;
}
};
});
However, I would recommeend to just bind the object and to get rid of the link-function. That way the directives code is a lot shorter, more readable, less "black magic" happens and the references inside the directive are more expressive:
myApp.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
myData: '='
}
};
});
Then you can just reference it inside the directive with myData.array1. Replace the '=' with '=someName' to reference it with someName.array1
your HTML should be :
<my-directive arrayone="myData.array1" arraytwo="myData.array2"></my-directive>
and your directive :
myApp.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
arrayOne: '=arrayone',
arrayTwo: '=arraytwo'
},
link:function(scope){
console.log('arrayOne :',scope.arrayOne);
console.log('arrayTwo :',scope.arrayTwo);
},
controller: function($scope) {
console.log('arrayOne :',$scope.arrayOne);
console.log('arrayTwo :',$scope.arrayTwo);
}
};
});
Demo
HTML-Partial:
<div ng-controller="MyCtrl">
<my-directive my-data="myArrays" array-one="myArrays.array1" array-two="myArrays.array2">
</my-directive>
</div>
Script:
angular.module('myApp', [])
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
arrayOne: '=',
arrayTwo: '='
},
link: function(scope, elem) {
// get access to scope.array1 and scope.array2
//console.log(scope.array1)
console.log(scope.arrayOne);
console.log(scope.arrayTwo);
}
};
})
.controller('MyCtrl', function($scope) {
$scope.myArrays = {
array1: ['Pizza', 'Spaghetti'],
array2: ['Lasagne', 'Schnitzel']
};
});
For some reason I can't make this work based on the other examples I've seen here on SO.
Here's my directive:
(function () {
angular.module('materialDesign')
.directive('aSwitch', directive);
function directive() {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false, // I've tried true here
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
scope.toggleState = !scope.toggleState;
});
}
};
}
})();
And the controller scope value that I want to change when toggling the switch/checkbox:
$scope.hideInactive = true;
The html:
<a-switch toggle-state="hideInactive"></a-switch>
and further down in my html page, I have this:
<div ng-show="!hideInactive">
<!-- stuff -->
</div>
EDIT:
This version is "working now", but as soon as I click my switch/checkbox a second time, the element.on fires twice, this flipping my scope value back to what it was.....basically, it's not letting me "un-check" my toggle.
angular.module('material')
.directive('aSwitch', [
'$timeout', function($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false,
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function (scope, element) {
element.on('click touchstart', function () {
$timeout(function () {
scope.toggleState.state = !scope.toggleState.state;
scope.$apply();
});
});
}
};
}
]);
EDIT and FINAL SOLUTION:
Here's the updated directive link property that fixed everything. I'd like to add that Oleg Yudovich's answer was also used (passing an object as the property instead of a true/false by itself)
link: function (scope, element) {
element.on('click touchstart', function (event) {
if (event.srcElement && event.srcElement.id && event.srcElement.id === "switch") {
event.stopPropagation();
$timeout(function() {
scope.toggleState.state = !scope.toggleState.state;
});
}
});
}
Try to pass object instead of primitive variable like this:
$scope.hideInactive = {
state: false;
}
html without changes:
<a-switch toggle-state="hideInactive"></a-switch>
in your directive:
scope.toggleState.state = !scope.toggleState.state;
Reed this awesome article: https://github.com/angular/angular.js/wiki/Understanding-Scopes
You need to run digest cycle after changes in scope, because changing scope binding from event will not run angular digest cycle, you need to run it manually by doing scope.$apply()
Directive
(function () {
angular.module('materialDesign')
.directive('aSwitch', directive);
function directive($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false, // I've tried true here
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
$timeout(function(){
scope.toggleState = !scope.toggleState;
});
});
}
};
}
})();
Try below code:
angular.module('material').directive('aSwitch', ['$timeout', function($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false,
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
$timeout(function() {
scope.toggleState.state = !scope.toggleState.state;
scope.$apply();
});
});
}
};
}]);
Dealing with '&' and isolated scope.
Is it possible to pass a value up through a parent directive? I want to pass id from the textdisp directive to the controller.
HTML:
<body ng-controller="MainCtrl">
<builder removequest="deleteQuestion(id)"></builder>
</body>
ANGULAR:
app.controller('MainCtrl', function($scope) {
$scope.deleteQuestion = function(id) {
alert(id);
}
});
app.directive('builder', function() {
return {
restrict: 'E',
scope: {
removequest: '&'
},
template: '<div>Hello how are you? <textdisp removequest=removequest(id)></textdisp></div>'
}
});
app.directive('textdisp', function() {
return {
restrict: 'E',
scope: {
removequest: '&'
},
template: '<div ng-click="remove()">Click here!</div>',
link: function (scope, el) {
scope.remove = function(id) {
console.log('workin')
scope.removequest(1);
}
}
}
});
I believe there are 2 things going on with your code:
When you're placing removequest="removequest(id)" that is calling the function, and not just referring to the function.
I believe that the &attr binding isn't returning the function that you're expecting.
Try this Plunker; it essentially uses { removequest: '=' } for bi-directional binding, and removequest="deleteQuestion" / removequest="removequest" for function references rather than calling the function.
It's a little confusing, but you can use object parameter when you need to pass values into your function invoked via & binding. Take a look at this code it will make everything clear:
app.controller('MainCtrl', function($scope) {
$scope.deleteQuestion = function(id) {
alert(id);
}
});
app.directive('builder', function() {
return {
restrict: 'E',
scope: {
removequest: '&'
},
template: '<div>Hello how are you? <textdisp removequest="removequest({id: id})"></textdisp></div>'
}
});
app.directive('textdisp', function() {
return {
restrict: 'E',
scope: {
removequest: '&'
},
template: '<div ng-click="remove()">Click here!</div>',
link: function(scope, el) {
scope.remove = function(id) {
scope.removequest({id: 34534}); // <-- 1.
}
}
}
});
Demo: http://plnkr.co/edit/3OEy39UQlS4EyOu5cq4y?p=preview
Note how you specify scope.removequest({id: 34534}) parameter to be passed into <textdisp removequest="removequest({id: id})">.
This should not be too hard a thing to do but I cannot figure out how best to do it.
I have a parent directive, like so:
directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
replace: true,
transclude: true,
template: '
<div class="editable-fieldset" ng-click="edit()">
<div ng-transclude></div>
...
</div>',
controller: ['$scope', function ($scope) {
$scope.edit = ->
$scope.editing = true
// ...
]
};
});
And a child directive:
.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
template: function (element, attrs) {
'<div>
<label>' + attrs.label + '</label>
<p>{{ model.' + attrs.field + ' }}</p>
...
</div>'
},
require: '^editableFieldset'
};
});
How can I easily access the model and editing properties of the parent directive from the child directive? In my link function I have access to the parent scope - should I use $watch to watch these properties?
Put together, what I'd like to have is:
<editable-fieldset model="myModel">
<editable-string label="Some Property" field="property"></editable-string>
<editable-string label="Some Property" field="property"></editable-string>
</editable-fieldset>
The idea is to have a set of fields displayed by default. If clicked on, they become inputs and can be edited.
Taking inspiration from this SO post, I've got a working solution here in this plunker.
I had to change quite a bit. I opted to have an isolated scope on the editableString as well because it was easier to bind in the correct values to the template. Otherwise, you are going to have to use compile or another method (like $transclude service).
Here is the result:
JS:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope) {
$scope.myModel = { property1: 'hello1', property2: 'hello2' }
});
myApp.directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
transclude: true,
replace: true,
template: '<div class="editable-fieldset" ng-click="edit()"><div ng-transclude></div></div>',
link: function(scope, element) {
scope.edit = function() {
scope.editing = true;
}
},
controller: ['$scope', function($scope) {
this.getModel = function() {
return $scope.model;
}
}]
};
});
myApp.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
scope: {
label: '#',
field: '#'
},
template: '<div><label>{{ label }}</label><p>{{ model[field] }}</p></div>',
require: '^editableFieldset',
link: function(scope, element, attrs, ctrl) {
scope.model = ctrl.getModel();
}
};
});
HTML:
<body ng-controller="Ctrl">
<h1>Hello Plunker!</h1>
<editable-fieldset model="myModel">
<editable-string label="Some Property1:" field="property1"></editable-string>
<editable-string label="Some Property2:" field="property2"></editable-string>
</editable-fieldset>
</body>
You can get access to parent controller by passing attribute in child directive link function
link: function (scope, element, attrs, parentCtrl) {
parentCtrl.$scope.editing = true;
}