KendoWindow AngularJS: transcluded elements are not shown - angularjs

The code below wraps a KendoWindow in a directive. The objective is twofold: (1) to open/close KendoWindow via $broadcast and avoid having window open/close methods in the controller (as these methods modify the DOM and not the model), and (2) use transclusion to define in the component child elements that will be shown in the KendoWindow and handled in the controller.
The problem is that the elements don't show up in the KendoWindow (see plunk) and the drow down list doesn't work. What's wrong with this code?
HTML:
<button ng-click="open()">Open window</button>
<button ng-click="close()">Close window</button>
<div my-window title="'This is the title'">
<p>Some HTML content</p>
This is a link
<select kendo-drop-down-list="ddl" style="width: 200px" k-options="ddlOptions">
<option>xxx</option>
<option>yyy</option>
<option>zzz</option>
</select>
</div>
Javascript:
var app = angular.module("app", [ "kendo.directives" ]);
function MyCtrl($scope) {
$scope.open = function () {
$scope.$broadcast('open');
};
$scope.close = function () {
$scope.$broadcast('close');
};
$scope.ddlOptions = {
select: function() {
alert('selected');
}
};
}
app.directive('myWindow', function() {
var directive = {};
directive.restrict = 'AE';
directive.transclude = true;
directive.scope = { title: '=' };
directive.template = '<div kendo-window="win" k-width="500" k-visible="false" ng-transclude></div>';
directive.link = function(scope, element, attrs) {
var init = function() {
scope.win.title(scope.title);
};
scope.$on("open", function(){
scope.win.center();
scope.win.open();
});
scope.$on("close", function(){
scope.win.close();
});
scope.$on("kendoWidgetCreated", function(event, widget){
if (widget === scope.win ) {
init();
}
});
};
return directive;
});

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>

Make directive function accessible in parent scope without events

I have a directive with an isolated scope and want to call its function to update data from the parent controller without using events.
var myApp = angular.module('MyApp',[]);
myApp.directive('myDirective', function() {
return {
scope: {},
link: function(scope) {
scope.update = function() {
alert('Directive updated!');
}
}
}
});
function MyCtrl($scope) {
$scope.updateDirective = function() {
// make me call update() function in directive
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<button ng-click="updateDirective()">Update!</button>
<span my-directive></span>
</div>
You could apply this solution.
In this way you are passing a variable in two way binding:
my-directive="myFunction" in the html
and myFunction: '=myDirective' in the directive)
Then assign the function in the directive:
scope.myFunction = function () {
alert('Directive updated!');
}
In this way you can use a function defined in a directive.
var myApp = angular.module('MyApp', []);
myApp.directive('myDirective', function () {
return {
scope: {
myFunction: '=myDirective'
},
link: function (scope) {
scope.myFunction = function () {
alert('Directive updated!');
}
}
}
});
function MyCtrl($scope) {
$scope.myFunction = {};
$scope.updateDirective = function () {
console.log( $scope.myFunction );
$scope.myFunction();
// make me call update() function in directive
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<button ng-click="updateDirective()">Update!</button> <span my-directive="myFunction"></span>
</div>
You could tackle this issue by introducing a new directive that is required by your isolated directive. Conveniently, you can assign the controller to this new directive.
Once required you then 'register' your isolated directive to the 'parent' directive as the target for your function. In the code snippet below I only provided a way to add 1 directive, but you could easily extend this to be an array of child directives. A good of example of such a setup are tabs, where each tab is a child directive of a common tabs directive.
angular.module("MyApp", []);
angular.module('MyApp').directive("myParentDirective", function(){
return {
controller: function ($scope) {
var childUpdate;
this.registerChild = function(_childUpdate_){
childUpdate = _childUpdate_;
};
$scope.updateDirective = function() {
childUpdate();
};
}
};
});
angular.module('MyApp').directive('myDirective', function() {
return {
require: '^myParentDirective',
scope: {},
link: function(scope, element, attrs, myParentController) {
myParentController.registerChild(update);
function update() {
alert('Directive updated!');
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div my-parent-directive>
<button ng-click="updateDirective()">Update!</button>
<span my-directive></span>
</div>
</div>

AngularJS: How do you use the currency filter inside a directive?

In this plunker, I am trying to apply the currency filter to the display that is not being edited. It is an edit in place directive that displays an input when it is active but I want the filter applied when it is not active.
script:
var app = angular.module('plunker', []);
app.directive('editInPlace', function () {
return {
restrict: 'E',
scope: {
value: '='
},
template: '<span ng-click="edit()" ng-bind="value" ng-show="!editing"></span><input ng-model="value" ng-blur="onBlur()" ng-show="editing"></input>',
link: function ($scope, element, attrs) {
var inputElement = element.find('input');
// reference the input element
element.addClass('edit-in-place');
// Initially, we're not editing.
$scope.editing = false;
// ng-click handler to activate edit-in-place
$scope.edit = function () {
$scope.editing = true;
// element not visible until digest complete
// timeout causes this to run after digest
setTimeout(function() {
inputElement[0].focus();
});
};
$scope.onBlur = function() {
$scope.editing = false;
};
}
};
});
app.controller('MainCtrl', function($scope) {
$scope.contacts = [
{name: 'Katniss', total: 35645.58},
{name: 'Peeta', total: 25178.21}
];
});
view:
<body ng-controller="MainCtrl">
<ul style="margin-top:20px;">
<li ng-repeat="contact in contacts">
{{contact.name}} --
<edit-in-place value="contact.total"></edit-in-place>
</li>
</ul>
<hr/>
<pre>{{contacts}}</pre>
<hr/>
</body>
You use the filter the same way you would use it in any other template:
template: '<span ng-click="edit()" ng-show="!editing">{{ value | currency}}</span><input ng-model="value" ng-blur="onBlur()" ng-show="editing"></input>'
Here's your modified plunkr.

AngularJS bootstrap modal scope value not displayed

I'm using angular bootstrap modal.
termsText is populated and the scope.productTerms does contain a value. But for some reason when I output {{ productTerms }} inside the modal the value is not being displayed. Why?
js
$scope.openProductTerms = function (termsText) {
$scope.productTerms = termsText <-- has a value in console.log()
var modalInstance = $modal.open({
templateUrl: 'myModalTerms.html',
controller: ModalInstanceCtrl
});
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.dismiss('OK');
};
};
html
{{ productTerms }} < ==== value shows outside modal
<script type="text/ng-template" id="myModalTerms.html">
<div class="modal-body">
{{ productTerms }} <==== same value does not show insdie modal?
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
</div>
</script>
The modal has a new controller and therefore a new scope.
services.js
angular.module('services',[]).
factory('sharedService', function($rootScope) {
var sharedService = {};
sharedService.value = null;
sharedService.prepForBroadcast = function(value) {
this.value = value;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('shared');
};
return sharedService;
});
controller.js
$scope.openProductTerms = function (termsText) {
sharedService.prepForBroadcast(termsText);
var modalInstance = $modal.open({
templateUrl: 'myModalTerms.html',
controller: ModalInstanceCtrl
});
lastController.js
var ModalInstanceCtrl = function ($scope, $modalInstance,sharedService) {
$scope.$on('shared', function() {
$scope.productTerms = sharedService.value;
});
$scope.ok = function () {
$modalInstance.dismiss('OK');
};
};
Try using {{$parent.productTerms}} within your HTML
From my experience, it seems that the angular bootstrap modal creates a new scope. So by referencing $parent first, you'll be able to get your model's value.

AngularJS - set a model defined in a directives template

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

Resources