How can I update scope in directive?
<div ng-controller="MyCtrl">
<p t></p>
</div>
My directive:
var myModule = angular.module('myModule', [])
.directive('t', function () {
return {
template: '{{text}}',
link: function (scope, element, attrs) {
scope.text = '1';
element.click(function() {
scope.text = '2';
});
}
};
})
.controller('MyCtrl', ['$scope', function ($scope) {
}]);
After click directive does not update.
Use $apply method:
element.click(function() {
scope.$apply(function(){
scope.text = '2';
});
});
Explanation: How does data binding work in AngularJS?
Related
How to expose directive methods without using $broadcast or '=' between modules?
Using $broadcast (events) if there are multiple directives all will be notified. It cannot return value too.
Exposing directive's function by html attribute I think it is not that best that Angular has to offer.
Angular Bootstrap UI do it using services (I guess): It have a service named "$uibModal".
You can call a function "$uibModal.open()" of Modal Directive by injecting $uibModal service.
Is that the right way?
An example of a directive that registers its API with a service:
app.service("apiService", function() {
var apiHash = {};
this.addApi = function (name,api) {
apiHash[name] = api;
};
this.removeApi = function (name) {
delete apiHash[name];
};
this.getApi = function (name) {
return apiHash[name];
};
});
app.directive("myDirective", function (apiService) {
return {
restrict: 'E',
scope: {},
template: `<h1>{{title}}</h1>`,
link: postLink
};
function postLink(scope, elem, attrs)
var name = attrs.name || 'myDirective';
var api = {};
api.setTitle = function(value) {
scope.title = value;
};
apiService.addApi(name, api);
scope.$on("$destroy", function() {
apiService.removeApi(name);
});
}
});
Elsewhere in the app, the title of the directive can be set with:
apiService.getApi('myDirective').setTitle("New Title");
Notice that the directive registers the api with a name determined by the name attribute of the directive. To avoid memory leaks, it unregisters itself when the scope is destroyed.
Update
How could I use it from a controller?
app.controller('home', function($scope,apiService) {
$scope.title = "New Title";
$scope.setTitle = function() {
apiService.getApi('mainTitle').setTitle($scope.title);
};
})
<body ng-controller="home">
<my-directive name="mainTitle"></my-directive>
<p>
<input ng-model="title" />
<button ng-click="setTitle()">Set Title
</button>
</p>
</body>
The DEMO
angular.module('myApp', [])
.service("apiService", function() {
var apiHash = {};
this.addApi = function(name, api) {
apiHash[name] = api;
};
this.getApi = function(name) {
return apiHash[name];
};
})
.directive("myDirective", function(apiService) {
return {
restrict: 'E',
scope: {},
template: `<h1>{{title}}</h1>`,
link: postLink
};
function postLink(scope, elem, attrs) {
var name = attrs.name || 'myDirective';
var api = {};
api.setTitle = function(value) {
scope.title = value;
};
apiService.addApi(name, api);
scope.$on("$destroy", function() {
apiService.addApi(name, null);
});
}
})
.controller('home', function($scope,apiService) {
$scope.title = "New Title";
$scope.setTitle = function() {
apiService.getApi('mainTitle').setTitle($scope.title);
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="home">
<my-directive name="mainTitle"></my-directive>
<p>
<input ng-model="title" />
<button ng-click="setTitle()">Set Title
</button>
</p>
</body>
.factory('myService', [function() {
return {
charCount: function(inputString) {
return inputString.length;
}
}
}])
this service exposes function charCount();
in your directive you have to inject it like this
.directive('testDirective', ['myService', function(myService) {
return {
restrict: 'A',
replace: true,
template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
link: function($scope, el, attrs) {
$scope.myTestString = 'string of length 19';
$scope.strLen = myService.charCount( $scope.myTestString );
}
}
}])
and, of course call it
$scope.strLen = myService.charCount( $scope.myTestString );
<html>
<style>
#out {
width:96%;
height:25%;
padding:10px;
border:3px dashed blue;
font-family: monospace;
font-size: 15px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var APP = angular.module('MYAPP', []);
APP.controller('main', ['$scope', '$element', '$compile', 'myService', function($scope, $element, $compile, myService) {
$scope.test = 'my Test Controller';
$scope.directiveTest = "directive test";
var testSvc = myService.charCount($scope.test);
$scope.showTestDir = true;
}])
.directive('testDirective', ['myService', function(myService) {
return {
restrict: 'A',
replace: true,
template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
link: function($scope, el, attrs) {
$scope.myTestString = 'string of length 19';
$scope.strLen = myService.charCount( $scope.myTestString );
}
}
}])
.factory('myService', [function() {
return {
charCount: function(inputString) {
return inputString.length;
}
}
}])
.filter('toUpper', function() {
return function(input) {
return input.toUpperCase();
}
})
.filter('toLower', function() {
return function(input) {
return input.toLowerCase();
}
})
;
</script>
<body ng-app="MYAPP">
<div id="out" ng-controller="main">
{{test}} - not filtered
<br/>
{{test|toUpper}} - filtered toUpper
<br/>
{{test|toLower}} - filtered toLower
<br/>
<br/>
<div test-directive ng-if="showTestDir"></div>
</div>
</body>
</html>
I have the following directive:
.directive("feedList", function() {
return {
restrict: 'E',
scope: {
feeds: '=feedData'
},
templateUrl: 'jstemplates/feed-list.html',
link: function(scope) {
angular.forEach(scope.feeds, function(value, key) {
if(value.who.fullname == " "){
scope.feeds[key].fullname = "email";
}
console.log(value.who.fullname);
});
}
}
})
Inside my template there is an event: ng-click="do()". How to handle this event in directive ot in parent controller?
As it's your isolated scope directive, so pass the callback function and then call that function directly from template or from controller or link function.
Working fiddle
var app = angular.module('myApp', []);
app.controller('AppCtrl', function($scope){
$scope.testFunction = function(){
alert("Called from isolated scope directive");
};
});
app.directive("isolatedScopeDirective", function(){
return{
scope:{
go:"&"
},
template : `<button ng-click='go()'>Test Button</button>`
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="AppCtrl">
<isolated-scope-directive go="testFunction()"></isolated-scope-directive>
</div>
I wrote a plunker to see how to use bindToDirective to isolate scopes and using directive controller to call main controller function, but, I am doing something wrong. Could you suggest?
This is the plunker: http://plnkr.co/edit/UJLjTmIiHydHr8qRzAsX?p=preview
Code sample:
.controller('Ctrl', function() {
var self = this;
self.func = function() {
console.log('In the controller function');
};
})
.directive('myDirective', [ function() {
var self = {};
self.link = function (scope, elem, attrs, ctrl) {
elem.bind('click', function () {
ctrl.ctrlFunc();
});
elem.addClass('fa fa-file-excel-o fa-lg');
};
return {
restrict: 'E',
scope: {},
controller: function () {
},
controllerAs: 'DirCtrl',
bindToController: {
ctrlFunc: '&'
},
link: self.link
};
}])
html sample to associate main controller function to directive:
<div ng-controller="Ctrl">
<my-directive ctrlfunc="Ctrl.func()"></my-directive>
</div>
You have a number of issues:
You need a hyphen in your directive argument name and you should be passing the function reference, not calling the function directly (with params):
<my-directive ctrl-func="ctrl.func"></my-directive>
Second, you are using alias syntax in your controller (var self = this;), but not in your template. You need to update it to the following:
<div ng-controller="Ctrl as ctrl">
<my-directive ctrl-func="ctrl.func"></my-directive>
</div>
Finally, pass down the function reference with two-way binding instead of with & since that passes down values for implicit evaluation.
bindToController: {
ctrlFunc: '='
},
See working plunkr
I'm not sure you need bindToController...
This version calls your Ctrl's function: http://plnkr.co/edit/Rxu5ZmmUAU8p63hR2Qge?p=preview
JS
angular.module('plunker', [])
.controller('Ctrl', function($scope) {
$scope.func = function() {
console.log('In the controller function');
};
}) angular.module('plunker', [])
.controller('Ctrl', function($scope) {
$scope.func = function() {
console.log('In the controller function');
};
})
.directive('myDirective', [ function() {
return {
template: '<pre>[clickme]</pre>',
replace: true,
restrict: 'E',
scope: {
target: '&'
},
link: function (scope, elem, attrs) {
elem.bind('click', function () {
var fn = scope.target && scope.target(scope);
fn && fn();
});
elem.addClass('fa fa-file-excel-o fa-lg');
}
};
}])
HTML
<div ng-controller="Ctrl">
<my-directive target="func"></my-directive>
</div>
I am trying to implement the directive, in the directive, I want to $eval the values which contains the function name and parameter value:
Html page:
<select mydirective="action('pValue')">
AngularJS directive code:
app.directive('mydirective', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, element, attr) {
$timeout(function () {
$scope.$eval(attr.mydirective);
});
}
}
What I am expected is it will invoke the action function define in scope and pass the pValue as function parameter. How can I make it work please?
What you want happen automatically, the function will invoke with the value, this is the purpose of eval:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.action = function(val) {
alert(val);
}
});
app.directive('mydirective', function($timeout) {
return {
restrict: 'A',
link: function($scope, element, attr) {
$timeout(function() {
$scope.$eval(attr.mydirective);
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js"></script>
<div ng-app="plunker" ng-controller="MainCtrl">
<select mydirective="action('pValue')"></select>
</div>
For those whom looking for a way to pass $event info to custom directive method see example below:
TEMPLATE:
<div on-touch-end="onTouchEnd( 'some data' )">
TOUCH ME!
</div>
CONTROLLER:
$scope.onTouchEnd = function( data ) {
console.log("onTouchEnd event with data", data, event );
};
DIRECTIVE:
.directive('onTouchEnd', function() {
return {
restrict : 'A',
link : function( $scope, $element, $attr ) {
$element.on('touchend', function( event ) {
$scope.$apply(function() {
$scope.$eval( $attr.onTouchEnd );
});
});
}
}
})
I have a directive defined like so:
angular.module('directives.myInput', [])
.directive('myInput', function($parse, $http, $sce){
return {
restrict: 'E',
template: '<input type="text" ng-model="searchStr" />',
controller: function($scope){
$scope.keyPressed = function(event){
$scope.showDropdown = true;
.
.
.
}
}
};
});
And then I have a button in html and directive above declared like so:
<div ng-controller="IndexCtrl">
<button ng-click="startNewLog()">Start</button>
<div ng-controller="ItemNewCtrl">
<myInput />
</div>
</div>
I want to change/initialize ng-model="searchStr" model on a button ng-click. How can I do that?
Thanks guys,
Jani
If I understand you right, first of all you need call child controller with $broadcast. Since we don't use isolate scope, we just call directive method from child controller:
[Short answer]
No isolate scope example
Demo 1 Fiddle
For isolate scope, I would map value to directive that listens on value change automatically:
Isolate scope example
Demo 2 Fiddle
[Full answer]
No isolate scope example
HTML
<div ng-controller = "IndexCtrl">
<button ng-click="startNewLog()">Start</button>
<div ng-controller="ItemNewCtrl">
<my-input></my-input>
</div>
</div>
JS
var app = angular.module('myModule', []);
app.controller('IndexCtrl', function ($scope) {
$scope.startNewLog = function(){
$scope.$broadcast('someEvent');
};
});
app.controller('ItemNewCtrl', function ($scope) {
$scope.$on('someEvent', function() {
$scope.callDirective();
});
});
app.$inject = ['$scope'];
app.directive('myInput', function(){
return {
restrict: 'E',
template: '<input type="text" ng-model="searchStr" />',
controller: function($scope){
$scope.searchStr;
$scope.keyPressed = function(event){
$scope.showDropdown = true;
}
},
link: function(scope, elm, attrs) {
scope.callDirective = function() {
scope.searchStr = 'callDirective';
};
}
};
});
Isolate scope example
HTML
<div ng-controller = "IndexCtrl">
<button ng-click="startNewLog()">Start</button>
<div ng-controller="ItemNewCtrl">
<my-input my-model='contInput'></my-input>
</div>
</div>
JS
var app = angular.module('myModule', []);
app.controller('IndexCtrl', function ($scope) {
$scope.startNewLog = function(){
$scope.$broadcast('someEvent');
};
});
app.controller('ItemNewCtrl', function ($scope) {
$scope.contInput = '';
$scope.count = 0;
$scope.$on('someEvent', function() {
$scope.contInput = 'hey mate';
});
});
app.$inject = ['$scope'];
app.directive('myInput', function(){
return {
restrict: 'E',
scope:{searchStr: '=myModel'},
template: '<input type="text" ng-model="searchStr" />',
controller: function($scope){
$scope.searchStr;
$scope.keyPressed = function(event){
$scope.showDropdown = true;
}
}
};
});