AngularJS - set a model defined in a directives template - angularjs

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;
}
}
};
});

Related

How to expose directive methods using a service

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>

Calling a method within a controller that's within a custom directive

Been trying to figure this out for too long now. Maybe someone can shed some light:
Am experimenting with custom directives and as an exercise I'm trying to create a method within the custom directive's controller that can be called from a simple button within the view. But the method isn't being called, even though I can see the method (using console) as a property within isolated scope object. Any ideas please?
HTML:
<my-dir>
<p>My dir content</p>
<p><button ng-click="hideMe()">Hide element with isolated scope</button></p>
</my-dir>
JS:
var app = angular.module('myApp', []);
app.directive('myDir', function() {
return {
restrict: 'EA',
scope: {},
controller: ['$scope', function ($scope) {
$scope.hideMe = function(){
console.log('hideMe called');
};
}]
};
})
You have to declare your template inside the directive using template: property or inside an external .html file using templateUrl:"path/to/template.html"
Example using template :
var app = angular.module('myApp', []);
app.directive('myDir', function() {
return {
restrict: 'EA',
scope: {},
template : '<p>My dir content</p><p><button ng-click="hideMe()">Hide me</button></p>',
controller: ['$scope', function ($scope) {
$scope.hideMe = function(){
console.log('hideMe called');
};
}]
};
})
Example using templateUrl :
var app = angular.module('myApp', []);
app.directive('myDir', function() {
return {
restrict: 'EA',
scope: {},
templateUrl : 'my-dir.tpls.html',
controller: ['$scope', function ($scope) {
$scope.hideMe = function(){
console.log('hideMe called');
};
}]
};
})
Template : my-dir.tpls.html
<p>My dir content</p>
<p><button ng-click="hideMe()">Hide me</button></p>
HTML:
<my-dir></my-dir>
You can try this,
Directive:
app.directive('myDir', function() {
return {
restrict: 'EA',
scope: {},
link: function($scope, element, attrs) {
$scope.hideMe = function() {
alert('hideMe called');
}
}
}
});
HTML:
<div ng-controller="MyCtrl">
<my-dir>
<p>My dir content</p>
<p>
<button ng-click="hideMe()">Hide element with isolated scope</button>
</p>
</my-dir>
</div>
DEMO

Angular directive does not call parent scope function

I am trying to call a parent function from a directive. But my functions are not being called.
Here is the code for your reference.
Controller
'use strict';
angular.module('myApp')
.controller('MyCtrl', function($scope) {
$scope.iconSelected = function() {
console.log('iconSelected');
var icon = angular.element('#icon').prop('files');
if (!icon) {
return;
}
icon = icon[0];
var _URL = window.URL || window.webkitURL;
$scope.utility.icon = _URL.createObjectURL(icon);
}
$scope.sourceSelected = function() {
console.log('sourceSelected');
var source = angular.element('#source');
console.log(source.prop('files'));
};
});
Directive
'use strict';
angular.module('myApp')
.directive('uploadButton', function() {
return {
templateUrl: 'app/directives/upload-button/upload-button.html',
restrict: 'E',
transclude: true,
scope: {
onSelect: '&'
},
link: function(scope, element, attrs) {
scope.name = attrs.name;
scope.id = attrs.id || attrs.name;
scope.label = attrs.label || attrs.name;
scope.accept = attrs.accept;
scope.showDialog = function() {
element.find('#' + scope.id).trigger('click');
};
element.find('input').change(function() {
scope.$apply(attrs.onSelect);
});
}
};
});
Directive Template
<md-input-container class="upload-button">
<md-button class="md-raised" ng-click="showDialog()">
<span ng-transclude></span>
</md-button>
<input type="file" name="{{name}}" id="{{id}}" aria-label="{{label}}" accept="{{accept}}">
</md-input-container>
Directive Usage
<upload-button name="icon" on-select="iconSelected()" accept=".svg">Choose an icon</upload-button>
<upload-button class="source-btn" name="source" on-select="sourceSelected()" accept=".zip">Select source code</upload-button>
Inside your directive code you are calling onSelect using attrs.onSelect change it to scope.onSelect. attrs.onSelect will just give you the string value iconSelected(). You need the function reference which will be available in the isolated scope which is created by the directive.
element.find('input').change(function() {
scope.$apply(scope.onSelect);
});

ng-hide doesn't work within hierarchy directives

I'm a newbie angular and will be happy to have some help here.
I'm struggeling to find out why I cannot setup one directive that is setting up an attribute hide="true" or "false" that will be used within the directive (Rank) as a parameter to change the inner directive (label) ng-hide to hide the label.
I tried everything
The outer directive (Rank) html:
<div>
<img src="/Components/Directives/images/blue_{{RankValue}}.svg" tooltip="{{RankValue}}/4" />
<label-info ng-hide="hide" header="{{header}}"></label-info>
</div>
The outer directive (Rank) directive java script:
angular.module('reusableDirectives')
.directive('Rank', function () {
return {
restrict: 'E',
scope: {
hide: '='
},
link: function (scope, element, attrs) {
scope.safeApply(scope.RankValue = scope.$eval(attrs.value));
scope.safeApply(scope.hidelabel = "true");
if (attrs.hidelabel == "false")
scope.safeApply(scope.hidelabel = "false");
scope.hidelabel = attrs.hide;
},
templateUrl: '/Components/Directives/Rank.html'
};
})
.controller('rankCtrl', ['scope', function ($scope) {
}]);
The inner directive (label) Html:
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3>{{header}}</h3>
</div>
<div class="modal-body">
<div ng-bind-html="items"></div>
</div>
</div>
<div class="modal-footer">
<div style="float:left;">
<button class="btn btn-primary" ng-click="ok()">Close</button>
</div>
</div>
</script>
<div>
<div class="fs-labelInfo-text">
{{header}}
</div>
<img class="fs-labelInfo-img"
ng-click="update(header)"
src="Components/Directives/images/questionMark.png" />
</div>
The inner directive (Label) directive java script:
angular.module('reusableDirectives')
.directive('labelInfo', function () {
return {
restrict: 'E',
scope: {
isolatedLabelHide: '#hidelabel'
},
controller: function ($scope, $element, $modal, $log, $http, $rootScope, myService) {
$scope.header = "header attribute";
$scope.caption = "label caption";
$scope.ok = function (header) {
myService.getLabelInfo(header).then(function (data) {
//this will execute when the AJAX call completes.
$scope.items = data;
console.log(data);
$scope.open();
});
};
$scope.open = function () {
$log.info('open');
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
header: function () {
return $scope.header;
},
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function () {
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
},
link: function (scope, element, attrs) {
scope.header = attrs.header;
},
templateUrl: '/Components/Directives/LabelInfo.html'
};
});
angular.module('reusableDirectives')
.controller('ModalInstanceCtrl', function ($scope, $modalInstance, header, items) {
$scope.items = items;
$scope.header = header;
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
The html that I'm using to test is:
One example to show the label:
<rank hide="false" value="3.5"></rank>
Another example to show the label:
<rank value="3.5"></rank>
example to hide will be:
<rank hide="true" value="3.5"></rank>
Thank you for your effort.
Best regards,
Chen
You set the scope property name as "hideLabel":
scope.hidelabel = attrs.hide;
So, you need to use "hideLabel" for the ng-hide attribute:
<label-info ng-hide="hideLabel" header="{{header}}"></label-info>
And you need to declare the ng-hide in your "labelInfo" directive template:
<div ng-hide="hideLabel">
(This "div" is the one that comes above <div class="fs-labelInfo-text"> at /Components/Directives/LabelInfo.html.)

Angular.js How to update the scope from a directive?

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?

Resources