Directive scope - javascript

I have a directive in angular and a form which is bound to a scope variable. Code:
<my-directive></my-directive>
<form>
<input ng-model="username" required>
</form>
Directive:
.directive('myDirective', function(){
restrict : 'E',
controller: function($scope){
console.log($scope.username); // Displays user name, same scope!!!
},
link: function(scope, element, attrs){
// Other codes
},
templateUrl: "templates/my-template-url.html"
}
})
The username variable can be reached inside the controller in my directive. This is not why I expected since I close the directive and they shouldn't share the same scope?
Why does this work?

Well you are using scope:false . It can access and change it's parent scope.
So your node scope doesn't matter here because username is in it's parent scope.
<my-directive></my-directive>
<form>
<input ng-model="username" required>
</form>
If your don't wanna to access parent scope then create an isolate scope
Like this
.directive('myDirective', function(){
restrict : 'E',
scope : {},
controller: function($scope){
console.log($scope.username); // Displays user name, same scope!!!
},
link: function(scope, element, attrs){
// Other codes
},
templateUrl: "templates/my-template-url.html"
}
})

Related

Angular Material - $mdDialog custom template - ng-click for function in directive?

I've used $mdDialog.show() within a custom directive with a templateUrl property to access a custom template for the the dialog itself, in which I've included two buttons; a 'Cancel' button and a 'Confirm' button.
For each of these I've added an ng-click with calls to 'cancelLeaver()' and 'confirmLeaver()' functions, which I need to be able to write in the directive, but have no idea how I would do.
I've attempted to use the $mdDialog.then() functionality, but this didn't work because of having buttons in a custom view template.
How can I call functions from a view template's ng-click attribute that are written inside a separate directive?
HTML:
<md-dialog ng-controller="mainController" style="min-width: 30vw">
<md-dialog-content>
<h2 class="md-title">Confirm leaver status</h2>
</md-dialog-content>
<md-dialog-content class="minus-padding-top">
<p>Enter employee's leave date below</p>
<br>
<md-input-container class="md-prompt-input-container">
<input id="leaveDate" name="leaveDate" ng-model="employee.leaveDate" ng-init="employee.leaveDate = currentDate" aria-label="Leave Date" required>
</md-input-container>
</md-dialog-content>
<md-dialog-actions>
<md-button ng-click="cancelLeaver()">Cancel</md-button>
<md-button ng-click="confirmLeaver()" ng-disabled="!employee.leaveDate.length">Confirm</md-button>
</md-dialog-actions>
</md-dialog>
Directive:
app.directive('makeLeaver', function($window, $mdDialog, $mdToast, $timeout, $state) {
return {
restrict: 'A',
scope: {
employee: '=',
},
controller: 'employeeDetailsController',
controllerAs: 'employeeDetails',
bindToController: true,
link: function(scope, element, attrs) {
element.bind('click', function() {
console.log('makeLeaver(' + '#' + scope.employee.id + ')');
/* Show confirmation prompt dialog */
$mdDialog.show({
parent: angular.element('body'),
clickOutsideToClose: true,
templateUrl: 'views/employees/employeeDetails/dialogs/makeLeaver.html',
targetEvent: element
})
/* ---------------- TRIED THIS BUT DIDN'T WORK ---------------- */
scope.cancelLeaver = function() {
console.log('cancelLeaver()');
$mdDialog.hide();
}
scope.confirmLeaver = function() {
console.log('confirmlLeaver()');
}
})
}
}
})
You can use $rootScope.$broadcast('foo')
and then receive it elsewher like $rootScope.on('foo').
another way would be to pass the scope through the function.
Check out this tutorial.

How to access isolated scope property from static directive template in Angular?

Controller:
app.controller('MainCtrl', ['$scope', function($scope) {
$scope.temp = {
'name': 'Test'
};
}]);
Template:
<custom-field ng-model="temp.name">
<md-input-container class="addon-menu">
<label>Name</label>
<input ng-model="ngModel" type="text" ng-focus="setLastFocusedElement($event)" />
</md-input-container>
</custom-field>
Directive:
app.directive('customField', function($timeout) {
return {
restrict: 'E',
scope: {
ngModel: '='
},
link: function($scope, $element, $attrs) {
console.log($scope.ngModel); // prints "test"
}
};
});
The problem is that once template is rendered, I can't see the value attached to input - it's empty, but I'm expecting to works, because inside link function it's printed correctly.
You are trying to access the directive scope in your template as the controller's scope. Move the markup inside the directive's template instead.
Directive:
app.directive('customField', function($timeout) {
return {
restrict: 'E',
scope: {
ngModel: '='
},
link: function($scope, $element, $attrs) {
console.log($scope.ngModel); // prints "test"
},
template: '<md-input-container class="addon-menu"><label>Name</label><input ng-model="ngModel" type="text" ng-focus="setLastFocusedElement($event)" /></md-input-container>'
};
Template:
<custom-field ng-model="temp.name"></custom-field>
You can also use separate html files as directive templates, which is good practise.
Are you trying to see the value in controller?
Please try $parent.$scope to see if value exist.

Angular: custom directive not updating controller

I started to learn Angular not so long time ago and I'm trying to understand scope, binding and etc.
I have an order details controller:
orderApp.controller('OrderDetailsController', ['$http','$routeParams','$scope','config', function($http, $routeParams, $scope, config){
var orderCtrl = this;
orderCtrl.orderId = $routeParams.orderId;
orderCtrl.order = {};
orderCtrl.editingView = false;
...
}]);
On order details page I want to output all information about selected order. Also we need to give user ability to edit order. Information about editing mode is stored in orderCtrl.editingView.
I decided to create custom directive. If edit mode is off - display text, otherwise display input.
orderApp.directive('editableText', function(){
return {
restrict: 'E',
scope: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
This is template:
<div class="col-xs-8" ng-if="!editMode">{{property}}</div>
<div class="col-xs-8" ng-if="editMode"><input type="text" class="form-control" ng-model="property"></div>
And this is how I use directive in html files:
<editable-text property="orderCtrl.order.coid" edit-mode="orderCtrl.editingView"></editable-text>
Text and input are switching when edit mode is on/off. Problem is that orderCtrl.order.coid property is not updated when I change it in input.
Before edit property looks like:
Turn on edit mode and change value:
Turn off edit mode and we see old value:
Do I need to synchronise controller values and directive scope? I thought that with 2-ways binding it should happen automatically. Probably there is any other way to write this functionality? Will appreciate any help.
UPD
Directive code:
orderApp.directive('editableText', function(){
return {
restrict: 'E',
bindToController: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
Directive template:
<div class="col-xs-8" ng-if="!orderCtrl.editMode">{{orderCtrl.property}}</div>
<div class="col-xs-8" ng-if="orderCtrl.editMode"><input type="text" class="form-control" ng-model="orderCtrl.property"/></div>
Directive usage:
<editable-text property="orderCtrl.order.coid" edit-mode="orderCtrl.editingView"></editable-text>
I'm not sure that we really need to pass edit-mode attribute.
You should use bindToController: { ..scope properties.. } option here inside your directive to make sure that isolated scope properties should get bounded to controller this context.
Directive
orderApp.directive('editableText', function(){
return {
restrict: 'E',
bindToController: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
Template
<div class="col-xs-8" ng-if="!orderCtrl.editMode">
{{orderCtrl.property}}
</div>
<div class="col-xs-8" ng-if="orderCtrl.editMode">
<input type="text" class="form-control" ng-model="orderCtrl.property"/>
</div>
Note:- this above bindToController: { ..scope properties.. } option available for angular 1.4+ versions.
For Angular 1.3 > version & 1.4 > version you should use former way of doing it by having bindingToController: true to bind scope variable to controller context & do keep the varaibles inside scope: { ...props... }
scope: {
property: '=property',
editMode: '=editMode'
},
bindToController: true

Update $watch using require in angular directive

as an angular newbie this is my problem
If I have two directives in HTML like this
<parent-dir param="par">
<child-dir></child-dir>
</parent-dir>
and in JS like this (in parent)
app.directive('parentDir', function(){
return {
restrict: 'E',
scope: {
param: '='
}
}
})
and in child
app.directive('childDir', function(){
return {
restrict: 'E',
require: '^parentDir',
controller: function($scope, $element){
<-- SHOULD I PUT WATCHER HERE -->
},
link: function(scope, element, attrs, parentdirCtrl){
<-- SHOULD I PUT WATCHER HERE -->
}
}
})
where in the child directive should I make an optional $watch in order to catch all changes to the param model?
Off course if I use $watch in the parent controller, all changes in the param are reflected in the parent directive but I can`t seem to find a way to pass this information to child directive.
You should place it inside the link function which have access of the parent controller using 4th parameter of link function parentdirCtrl. Actually you don't need to worry about the params variable because it uses = two way binding inside directive that does update the value in both parent controller scope & directive scope. Additionally you need define controller in your parentDir directive so that the scope of parentDir directive shared with the childDir.
Code
app.directive('childDir', function() {
return {
restrict: 'E',
require: '^parentDir',
template: '<div class="test">INner {{param}}</div>',
controller: function($scope, $element) {
},
link: function(scope, element, attrs, parentdirCtrl) {
scope.$watch('param', function(newVal, oldVal) {
console.log(newVal);
}) //true only if its object.
}
}
})
Demo Plunkr

How can i change array in directive, and then reflect that change in my controller?

I made directive with isolated scope with "=" method, in that directive i pass empty array, then i push data on that array.... How that change can be reflected on original array in my controller?
Here is the example:
angular.module('myModule').controller('MyController', ['$scope', function($scope) {
$scope.test = [];
}]);
angular.module('myModule').directive('mydirective', function() {
return {
scope: {
test: "=",
bread: "="
},
restrict: 'E',
link: function(scope, element, attribute) {
scope.test.push('one more')
},
replace: true,
templateUrl: 'some template'
};
});
HTML
<div ng-controller='MyController'>
<mydirective test='test'></mydirective>
<div ng-bind='test'> </div>
</div>
When i push something on array i dont have a reflection of that in my controller.
How can i fix that?
Here's how to do what you are trying to achieve.
HTML
<!-- myCtrl contains the original array, which we push here to the scope as 'ctrl' -->
<div ng-controller='myCtrl as ctrl'>
<!-- we pass your directive 'ctrl.test', which tells angular to two-way bind to the
test property on the 'ctrl' object on the current scope -->
<mydirective test='ctrl.test'>
<!-- we're inside the isolate scope: test here refers to mydirective's idea of test,
which is the two-way bound array -->
<div ng-bind='test'></div>
</mydirective>
</div>
JS
angular.module('app', [])
.directive('mydirective', function() {
scope: {
test: '='
},
link: function($scope) {
$scope.test.push('one more');
}
})
.controller('myCtrl', function() {
this.test = [];
});
Any alterations to the array will now be reflected in the ng-bind. Please note that it is bad practice to place primitives on $scope without being part of an object (due to the mechanics of prototypical inheritance) so you'd want to change $scope.test to something else.

Categories