How to access a controller function in directives with isolated scope? - angularjs

I have a function in my controller name as enableEditor it is working if i call the function from direct HTML i.e(add). But, if I call the function for a element which is created through the directives i.e(edit) is not working. Please look at my code and advice me if any ideas.
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Example - example-example53-production</title>
<script src="js/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<div user-name=""></div>
<div>
add
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$scope','$filter', function ($scope,$filter) {
$scope.enableEditor = function() {
alert("123");
};
}]);
myApp.directive("userName", function() {
return {
restrict: "A",
scope: {
value: "=userName"
},
template: '<div class="click-to-edit">' +
'Edit' +
'</div>'
};
});
</script>
</body>
</html>

Since you have an isolated scope the function belongs to the scope of the directive not your controller. Try using & in your directives scope like this:
<body ng-controller="MainCtrl">
<div user-name="" callme="enableEditor()"></div>
<div>
add
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$scope','$filter', function ($scope,$filter) {
$scope.enableEditor = function() {
alert("123");
};
}]);
myApp.directive("userName", function() {
return {
restrict: "A",
scope: {
value: "=userName",
callme:"&"
},
template: '<div class="click-to-edit">' +
'Edit' +
'</div>'
};
});
The attribute callme="enableEditor()" is used to pass the method to the scope directive, the directive scope uses & to indicate it is method callme:"&". Another example:
method2="someMethod()" like
scope: {
value: "=userName",
callme:"&",
method2:"&"
},template: '<div class="click-to-edit">' + 'Edit' + 'Save' + '</div>'

Related

Unable to change the Parent Scope variable from Directive scope using two way binding

I am trying to create a custom directive which takes a username as an input. It then validates and checks if the username is available or not. If the username is not available I want to pass some values back to the parent Controller.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>AngularJS Isolate Scope</title>
</head>
<body>
<div ng-app="mainApp">
<div ng-controller="AppCtrl">
<div> From Controller : <input type="text" ng-model="ctrlRole"></div><br>
{{parentVariable}}
<input type="text" is-unique="{url: 'http://WWW.GOOGLE.COM'}" ng-model="role"/>
</div>
</div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
var app = angular.module('mainApp', []);
app.controller("AppCtrl", function($scope){
$scope.ctrlRole = "Development";
$scope.parentVariable = "a";
});
app.directive("isUnique", function() {
return {
restrict : 'A',
require : 'ngModel',
transclude: 'true',
scope:{
parentVariable:"="
},
link : function(scope, element, attrs, ngModel) {
element.bind('blur', function (e) {
if (!ngModel || !element.val()) return;
var keyProperty = scope.$eval(attrs.isUnique);
console.log('this is the keyProperty we have received from the front end ' + keyProperty.url);
var currentValue = element.val();
console.log('this is the data we are going to validate ' + currentValue);
if(currentValue == 'AE'){
console.log('Changing the value ');
scope.parentVariable = 'b';
}
});
}
}
});
</script>
</body>
In summary I am trying to change the value of parentVariable from the directive scope depending on some conditions but its not happening, please let me know what I am doing wrong.
You specify the isolate scope parameter parentVariable, but in the template there is no value passed to it:
<input type="text" is-unique="{url: 'http://WWW.GOOGLE.COM'}" ng-model="role"/>
You would need:
<input type="text" is-unique="{url: 'http://WWW.GOOGLE.COM'}" parent-variable="parentVariable" ng-model="role"/>
Here, is an isolated example.
var app = angular.module('test', []);
app.controller('MainCtrl', function($scope) {
$scope.foo = 'foo';
});
app.directive('testDir', function($timeout) {
return {
restrict: 'A',
scope: {
val: '='
},
link: function(scope) {
console.log(scope.val);
$timeout(function() {
scope.val = 'bar';
}, 1000);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="MainCtrl">
{{foo}}
<div test-dir val="foo"></div>
</div>
</div>

ng-hide breaking my directive in angular 1.3.16

I'm trying to migrate from angular 1.2.28 to angular 1.3.16, however my code broke.
Angular 1.2.28 working: http://plnkr.co/edit/XfVakwA3Upm7Z2wosHCQ?p=preview
Angular 1.3.16 not working: http://plnkr.co/edit/4VxcHL0MHddobkmu9DMG?p=preview
JS
var app = angular.module('app', []);
app.run(function($rootScope, $timeout){
$rootScope.loading = true;
$timeout(function(){
$rootScope.items = ['Angular', '1.3.16', 'doesnt work'];
$rootScope.loading = false;
}, 3000);
});
app.directive('refresh', function(){
return {
restrict: 'A',
require: '^myDirective',
link: function(scope, element, attrs, ctrl){
if(scope.$last)
ctrl.init();
}
};
});
app.directive('myDirective', function(){
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p>Height: {{myHeight}}</p> <div ng-transclude></div></div>',
controller: function($scope, $element){
this.init = init;
function init(){
$scope.myHeight = $('.my-directive').height();
}
}
};
});
HTML
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.3.16" data-semver="1.3.16" src="https://code.angularjs.org/1.3.16/angular.js"></script>
<script data-require="jquery#1.11.0" data-semver="1.11.0" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Angular 1.3.16</h1>
<div ng-show="loading">Loading...</div>
<my-directive ng-hide="loading">
<div ng-repeat="item in items" refresh>
<p>{{item}}</p>
</div>
</my-directive>
</body>
</html>
The idea is to only run certain code when the inner html is outputted.
Height is 0 in angular 1.3.16.
However, if I remove ng-hide="loading" from <my-directive ng-hide="loading"> in angular 1.3.16, height gets the appropriated value.
Any ideas how can I solve this?
Inject $timeout into your directive and put the init code block in $timeout(function(){ ... }) like this:
app.directive('myDirective', function($timeout){
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p><b>Height: {{myHeight}}</b></p> <div ng-transclude></div></div>',
controller: function($scope, $element){
this.init = init;
function init(){
$timeout(function(){
$scope.myHeight = $('.my-directive').height();
});
}
}
};
});
var app = angular.module('app', []);
app.run(function($rootScope, $timeout) {
$rootScope.loading = true;
$timeout(function() {
$rootScope.items = ['Angular', '1.3.16', ' work'];
$rootScope.loading = false;
}, 1000);
});
app.directive('myDirective', function($timeout) {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p>Height: {{myHeight}}</p> <div ng-transclude></div></div>',
link: function($scope, $element) {
$element.on('DOMAttrModified DOMSubtreeModified', init);
function init() {
$scope.$apply(function() {
$scope.myHeight = $element.height();
});
}
}
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="jquery#1.11.0" data-semver="1.11.0" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script data-require="angular.js#1.3.16" data-semver="1.3.16" src="https://code.angularjs.org/1.3.16/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Angular 1.3.16</h1>
<div ng-show="loading">Loading...</div>
<my-directive ng-hide="loading">
<div ng-repeat="item in items" refresh>
<p>{{item}}</p>
</div>
</my-directive>
</body>
</html>
You have to set the height in the correct angular directive phase/lifecycle. You should set the hight in the link or even postlink phase. Usually the two phases are the same if you don't use prelink This is when all the content has already been rendered. See angular $compile or google for angular post link
The controller is for the logic and the link is for html/dom manipulations.
EDIT:
You can bind 'DOMAttrModified DOMSubtreeModified` events to trigger changes.

AngularJS - Transcluding multiple directives

I have a situation where I need to transclude multiple child directives inside of my outer directive. I've got it working, but I'm wondering if there is a better, more "angular" way to do it?
The working example is here: http://plnkr.co/edit/pmukXJPxUGd2gnMvEN1a?p=preview
Plunker code:
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="myApp">
<outer-directive>
<inner-directive1>Some Html I want placed elsewhere</inner-directive1>
<inner-directive2>Yet more Html that should be somewhere else</inner-directive2>
</outer-directive>
<script>
var app = angular.module("myApp", []);
app.directive("outerDirective", function($compile) {
return {
restrict: 'E',
transclude: true,
scope: {},
template: '<div>' +
' <h1>This is a title</h1>' +
' <div class="inner1"></div><br />' +
' Some other code that is in the directive<br /><br />' +
' <div class="inner2"></div><br />' +
'</div>',
link: function(scope, element, attrs, emptyCtrl, transclude) {
transclude(scope, function(clone) {
clone.each(function (i, ele) {
if (ele.nodeName === "INNER-DIRECTIVE1") {
$(element).find(".inner1").replaceWith($compile(ele)(scope));
}
if (ele.nodeName === "INNER-DIRECTIVE2") {
$(element).find(".inner2").replaceWith($compile(ele)(scope));
}
});
});
}
}
});
</script>
</body>
</html>

initializing scope using ng-init is not binding in directive

Hi I am trying to bind a value to a directive using '#' which is defined in the ng-init event. but this doesn't comes into my directive and it returns undefined. When does this property actually resolves is it in Compile, link? Also, why is it returning undefined always? What am I doing wrong here?
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="demoController" ng-init="init()">
<h1>Confused directives!!!</h1>
<my-user userName="{{user.name}}"></my-user>
<br>
{{user.name}}
</body>
</html>
(function(){
'use strict';
var app = angular.module('app', []);
app.controller('demoController', ['$scope', demoController])
function demoController($scope){
console.log('Im in the controller');
$scope.init = function (){
$scope.user = {
name: 'Argo'
}
console.log('Im in init method of controller')
}
}
app.directive('myUser', myUser);
function myUser(){
return {
restrict: 'AE',
replace: true,
scope: {
userName: '#'
},
template: '<div><b>{{userName}} is defined from init method </b></div>',
link: function(scope, ele, attr){
console.log('Im in link method of directive');
console.log('this is loaded' + scope.userName);
attr.$observe('userName', function(newValue){
console.log(newValue);
})
}
}
}
})();
http://plnkr.co/edit/vAKMNub8HRcphiaMMauR?p=preview

Angularjs: modal window to be with the size of the window - the angular way

I am learning angular. I have done this before with jquery. I wonder what is the angular way to to this. Also is it possible this functionality to be wrapped in service or directive so it can be reused with other widgets.
html:
<!doctype html>
<html ng-app="plunker">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.js"></script>
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.10.0.js"></script>
<script src="example.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div my-modal="{ data: 'test2'}">test2</div>
</body>
</html>
javascript
angular.module('plunker', ['ui.bootstrap', 'myModal']);
angular.module("myModal", []).directive("myModal", function ($modal) {
"use strict";
return {
template: '<div ng-click="clickMe(rowData)" ng-transclude></div>',
replace: true,
transclude: true,
scope: {
rowData: '&myModal'
},
link: function (scope, element, attrs) {
scope.clickMe = function () {
$modal.open({
template: "<div style=\"overflow:auto\">Created By:" + scope.rowData().data + "</div>"
+ "<div class=\"modal-footer\">"
+ "<button class=\"btn btn-primary\" ng-click=\"ok()\">OK</button>"
+ "<button class=\"btn btn-warning\" ng-click=\"cancel()\">Cancel</button>"
+ "</div>",
controller: function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close({ test: "test"});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
});
}
}
};
});
Working plunker:
http://plnkr.co/edit/yzxtWwZQdq94Tagdiswa?p=preview
I am very new to angular. Any working example will be greatly appreciated.
$modal uses Bootstrap modals and those have #media-queries set on .modal-dialog CSS class - you have to override that to make modal stretch to window width

Resources