Two way binding with controller in directive with isolated scope - angularjs

Im developing a directive, where when the user will click UPDATE, a field for EmployeeId will show up, and that employeeId field on the directive should be bound directly to the controller when changes. If i isolate the scope it doesn't change the controller employeeId, if i dont, than all directives gets toggled.
Plunker: https://plnkr.co/edit/aq06WnfniN51iR7ZtFLc?p=preview
code:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.employeeId = 'Josh'
$scope.employeePin = '1234'
$scope.updateFields = function() {
alert(1);
}
});
app.directive('crudEmp', function($parse) {
var html = '<div class="form-inline">\
<input type="text" class="form-control"\
placeholder="Employe ID"\
ng-model="employeeId"\
required\
ng-show="isEditing"/>\
<input type="password" class="form-control"\
placeholder="Employe PIN"\
ng-model="employeePin"\
ng-show="user.AdminSecurity && isEditing"\
ng-required="user.AdminSecurity"\
disabled/>\
<div class="col-md-12 text-right noPadding pt10 operationButtons" id="opBtn">\
<div class="col-md-12 noPadding" ng-hide="isEditing">\
<button class="btn btn-info" ng-click="toggleEditing()">Edit</button>\
</div>\
<div class="col-md-12 text-right noPadding" ng-hide="!isEditing">\
<button class="btn btn-success mr80 updateBtn" ng-click="ctrl.onClick(); toggleEditing()" ng-disabled="ctrl.valid">Update</button>\
<button class="btn btn-warning" ng-click="toggleEditing()">Cancel</button>\
</div>\
</div>';
return {
restrict: 'E',
template: html,
replace: true,
bindToController: {
employeeId: '=',
employeePin: '=',
valid: '=',
onClick: '&',
},
// scope: true, // When i make the scope true, it doesnt change the EmployeeId defined in the controller
link: function(scope, element, attr){
scope.onClick = attr.onClick;
scope.toggleEditing = function() {
scope.isEditing = !scope.isEditing;
if(scope.isEditing){
element.closest('form').find(':input:not(.operationButtons button)').attr('disabled', false);
var getAllInputs = element.closest('form').find(':input').filter(':visible:enabled');
if(getAllInputs[0]){
getAllInputs[0].focus();
}
}else{
element.closest('form').find(':input:not(.operationButtons button)').attr('disabled', true);
}
};
},
controllerAs: 'ctrl',
controller: function($scope) {
var ctrl = this;
ctrl.onClick = function() {
$scope.$eval($scope.onClick);
};
},
};
});

If I understand you currently it would be solved by changing:
...
restrict: 'E',
template: html,
replace: true,
bindToController: {
employeeId: '=',
employeePin: '=',
valid: '=',
onClick: '&',
},
// scope: true, // When i make the scope true, it doesnt change
To:
...
restrict: 'E',
template: html,
replace: true,
scope: {
employeeId: '=',
employeePin: '=',
valid: '=',
onClick: '&',
},
...
You can verify the behaviour in my plunker: https://plnkr.co/edit/Xia8BhlfFJ6mqgLjaoOp?p=preview

Related

How to get access for function in controller from directive template

Example:
HTML:
<div ng-repeat="notification in vm.notifications" class="notifications">
<notification notification="notification"></notification>
</div>
Directive notification is replaced other directives:
app.directive('notification', function($compile){
return {
restrict: 'E',
transclude: true,
scope: {
notification: "=notification"
},
link: function(scope, element) {
var temp = "<notifications" + scope.notification.type.normalCase() + ">";
element.replaceWith($compile(temp)(scope));
}
}
});
For example a few directives:
app.directive('notificationsStudentRequest', function(){
return {
template: '<div class="alert alert-info alert-dismissible"><button type="button" class="close" ng-click="vm.deleteNotification(notification)"><span aria-hidden="true">×</span></button>Запрос на участие в курсе: "{{notification.payload.course.name}}"</div>',
restrict: 'E',
replace: true,
transclude: true
}
});
app.directive('notificationsStudentRequestAccepted', function(){
return {
template: '<div class="alert alert-success alert-dismissible"><button type="button" class="close" ng-click="vm.deleteNotification(notification)"><span aria-hidden="true">×</span></button>[{{ notification.createdAt | date:"medium"}}] Запрос на участие в курсе: "{{notification.payload.course.name}}" был принят</div>',
restrict: 'E',
replace: true,
transclude: true
}
});
And in controller I have a fuction vm.deleteNotification(value)
app.controller('NotificationsController', function(){
var vm = this;
vm.notifications = {
//some object with notifications
}
vm.deleteNotification = function(notification){
vm.notifications.splice(vm.notifications.indexOf(notification), 1);
}
return vm;
});
ng-click in template don't see this fuction. This problem can fix $parent.vm.del......., but I need other solutions.
Move your notification list and management functions into a service. Inject that service into your directives and controller.
app.service('NotificationManager', function(){
var self = this;
angular.extend(self, {
notifications: {},
deleteNotification: function(notification){
self.notifications.splice(self.notifications.indexOf(notification), 1);
};
});
});
app.directive('notificationsStudentRequest', function(){
return {
template: '<div class="alert alert-info alert-dismissible"><button type="button" class="close" ng-click="dvm.deleteNotification(notification)"><span aria-hidden="true">×</span></button>Запрос на участие в курсе: "{{notification.payload.course.name}}"</div>',
restrict: 'E',
replace: true,
transclude: true,
bindToController: true,
controllerAs: 'dvm'
controller: function(NotificationManager){
this.deleteNotification = function(n){ NotificationManager.deleteNotification(n); }
}
}
});
app.directive('notificationsStudentRequestAccepted', function(NotificationManager){
return {
template: '<div class="alert alert-success alert-dismissible"><button type="button" class="close" ng-click="dvm.deleteNotification(notification)"><span aria-hidden="true">×</span></button>[{{ notification.createdAt | date:"medium"}}] Запрос на участие в курсе: "{{notification.payload.course.name}}" был принят</div>',
restrict: 'E',
replace: true,
transclude: true,
bindToController: true,
controllerAs: 'dvm'
controller: function(NotificationManager){
this.deleteNotification = function(n){ NotificationManager.deleteNotification(n); }
}
}
});
app.controller('NotificationsController', function(NotificationManager){
var vm = this;
vm.notifications = NotificationManager.notifications;
return vm;
});
Try nesting the deleteNotification function within an object -- I've explained a bit more here.
Perhaps something like this, should give you access to that function.
app.controller('NotificationsController', function(){
var vm = this;
vm.someObject = {};
//some code
vm.someObject.deleteNotification = function(notification){
vm.notifications.splice(vm.notifications.indexOf(notification), 1);
}
return vm;
});

Dynamically generate angular directive within a loop

I'm trying to dynamically compile and append a directive with a dynamic attribute in the directive; however, I keep getting an error.
Anyone know why?
http://plnkr.co/edit/wOb9UuFwXR0dCZx2e2xg?p=preview
angular.module('plunker', [])
.controller('MainCtrl', function($scope) {
var vm = this;
vm.foo = {
bar: 'world'
};
}).directive('addEditConfig', AddEditConfig)
.directive('inputElement', InputElement);
function AddEditConfig($compile) {
var directive = {
restrict: 'E',
scope: {
edit: '#',
config: '=',
display: '=',
parentController: '='
},
template: '<div id="dynamicForm"></div>',
controllerAs: 'vm',
bindToController: true,
controller: AddEditConfigController,
link: linkFunction
}
return directive;
function linkFunction(scope, element, attr, ctrl) {
var params = [{description: 'foo', value:'bar'}, {description: 'foo', value:'bar'}, {description: 'foo', value:'bar'}, {description: 'foo', value:'bar'}];
for (var i = 0; i < params.length; i++) {
angular.element(document.getElementById('dynamicForm')).append($compile('<input-element param="' + params[i] + '"></input-element>')(scope));
}
}
};
function AddEditConfigController() {
};
function InputElement() {
var directive = {
restrict: 'E',
scope: {
param: '='
},
controllerAs: 'vm',
bindToController: true,
template: '<div class="form-group"> <div class="col-md-8"> <input id="port" name="textinput" type="text" placeholder="{{vm.param.description}}" class="form-control input-md" ng-model="vm.param.value"/> </div></div>',
controller: InputElementController,
link: linkFunction
}
return directive;
function linkFunction(scope, el, attr, ctrl) {
}
};
function InputElementController() {
var vm = this;
}
The problem (at least one of them) is in this statement :
angular.element(document.getElementById('dynamicForm')).append($compile('<input-element param="' + params[i] + '"></input-element>')(scope));
Writing this param="' + params[i] + '" generates the following DOM param="[Object Object]".
So It will never work as expected.
One way to achieve this :
angular.module('plunker', [])
.controller('MainCtrl', function($scope) {
var vm = this;
vm.foo = {
bar: 'world'
};
}).directive('addEditConfig', AddEditConfig)
.directive('inputElement', InputElement);
function AddEditConfig($compile) {
var directive = {
restrict: 'E',
scope: {
edit: '#',
config: '=',
display: '=',
parentController: '='
},
template: '<div id="dynamicForm"><input-element param="param" ng-repeat="param in vm.params"></input-element></div>',
controllerAs: 'vm',
controller: AddEditConfigController
}
return directive;
};
function AddEditConfigController() {
this.params = [{description: 'foo', value:'bar'}, {description: 'foo1', value:'bar1'}, {description: 'foo2', value:'bar2'}, {description: 'foo3', value:''}];
};
function InputElement() {
var directive = {
restrict: 'E',
scope: {
param: '='
},
template: '<div class="form-group"> <div class="col-md-8"> <input id="port" name="textinput" type="text" placeholder="{{param.description}}" class="form-control input-md" ng-model="param.value"/> </div></div>'
}
return directive;
};
http://plnkr.co/edit/KdPvJdBsRwbjNsBCIvVh?p=preview

AngularJS transcluded nested directive

i'm trying to create an angular directive like this :
<radio-button-group group="myGroup" label="My Group" data-model="myModel.attribute">
<radio-button id="value_1" value="1">Value 1</radio-button>
<radio-button id="value_2" value="2">Value 2</radio-button>
</radio-button-group>
The main goals are reuse and avoid repeating ng-model on every <input type="radio" ng-model="myModel.attribute"> and code duplication in general.
I've written the followings:
formjs.js
var formjsModule = angular.module('hp.formjs', []);
// Directive
formjsModule.directive('radioButtonGroup', function () {
return {
restrict: 'E',
transclude: true,
scope: {
dataModel: '#',
group: '#',
label: '#?'
},
templateUrl: function (elem, attr) {
return 'formjs/radio/radio-group-tpl.html';
}
}
}).directive('radioButton', function () {
return {
restrict: 'E',
require: ['^radioButtonGroup'],
transclude: true,
scope: {
value: '#',
dataModel:'#',
group: '#'
},
templateUrl: function (elem, attr) {
return 'formjs/radio/radio-item-tpl.html';
}
}
});
formjs/radio/radio-item-tpl.html
<input type="radio" name="{{group}}" id="{{group+'_'+id}}" ng-value="value" ng-model="dataModel"/>
formjs/radio/radio-group-tpl.html
{{label}} m: {{dataModel}}
<div class="col-sm-10" id="g_{{group}}" ng-transclude>
</div>
</div>
I'm aware of transcluded scope, but i don't see the elegant way to do this binding.
Help, please!
It has to be directive? Wouldn't this be sufficient?
// HTML template
<div ng-repeat="option in options">
<input type="radio" ng-model="$parent.selected" ng-value="option"> {{ option }}
</div>
...
// Controller
$scope.selected = null;
$scope.options = ['Radio one', 'Radio two', 'Radio three'];
Of course, if you insist, you CAN make it a directive.
Usage in HTML, where options is array like above.
<radio-group group-name="My group"
ng-model="selected"
options="options"></radio-group>
Actual directive
app.directive('radioGroup', function () {
return {
restrict: 'E',
scope: {
groupName: '#',
options: '=',
ngModel: '='
},
template: '<legend>{{ groupName }}</legend>' +
'<div ng-repeat="option in options">' +
' <input type="radio" ' +
' ng-model="$parent.ngModel" ng-value="option"> {{ option }}' +
'</div>'
};
});

Call controller function from directive template in AngularJS

I've found plenty examples of calling controller function from directive however couldn't find an example of calling it from directive template.
Let's say I've got this HTML code to open modal directive
<button ng-click='toggleModal()'>Open</button>
<modal-dialog show='modalShown' confirm="confirmCtrl()">
<p>Modal Content Goes here<p>
</modal-dialog>
Here's my controller with a function confirmCtrl() I want to call:
myApp.controller('myController', function($scope){
$scope.modalShown = false;
$scope.toggleModal = function() {
$scope.modalShown = !$scope.modalShown;
};
$scope.confirmCtrl = function () {
alert('confirmed');
}
})
And here's my directive. I
.directive('modalDialog', function(){
return {
restrict: 'E',
scope: {
show: '=',
corfirm: '&'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div><button ng-click=""> Confirm </button></div></div>"
};
In my template, I've got a button and I want to call confirmCtrl() function on click, however, couldn't grasp how to do it
Here's a working fiddle http://jsfiddle.net/anao4nsw/
You can define your controller in your directive like so, and add an ng-click directive to the button element 'Confirm' in your template.
.directive('modalDialog', function(){
return {
controller: 'myController' //This line.
restrict: 'E',
scope: {
show: '=',
corfirm: '&'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'>
<div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div>
<button ng-click='confirmCtrl()'> Confirm </button></div></div>"
Note the addition of the ng-click='confirmCtrl()' in the last line of your template.
You've almost done what you need. The &-binding does the trick: it assigns a function to the property of the isolate scope and this function when called executes the expression specified in the attribute. So in your template, you just have to call this function of the isolate scope in ng-click: <button ng-click="confirm()"> Confirm </button>. It might not work for you because of a typo: you have coRfirm: '&' instead of coNfirm: '&'.
Your directive it would look so, this work very good. Test it, only copy and replace the current directive
app.directive('modalDialog', function(){
return {
restrict: 'E',
scope: {
show: '=',
confirm: '&'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div><button ng-click='confirm()'> OK </button></div></div>"
};
});

Directive and scope in AngularJS

Here is the code, ideally i should see
parent: true
when I click the toggle
However, it doesn't work
Here is the plunker
<body ng-controller="MainCtrl">
<button type="button" ng-click='isParentShown= !isParentShown' >Toggle</button>
<div><span>Controller-isParentShown: {{isParentShown}}</span></div>
<parent isShown='isParentShown'></parent>
</body>
var app = angular.module('plunker', []).directive('parent',function(){
return {
restrict: 'E',
replace: true,
scope: {
isShown: '='
},
template: '<span>Parent: {{isShown}}</span>'
};
}).directive('child',function(){
return {
restrict: 'E',
replace: true,
template:'<span>Child: {{isChildShown}}</span>',
scope: {
isChildShown: '#'
}
};
});
app.controller('MainCtrl', function($scope) {
$scope.isParentShown = false;
});
The problem is in the casing of the attributes, if you define a isShown binding, it's expecting a is-shown or is:shown attribute. Here's the fixed plunker: http://plnkr.co/edit/UOigth

Resources