Does controllerAs work with Isolated Scope? - angularjs

I'm familiar with using controllerAs, but do not understand why, when using with isolated scope, none of my controller properties are showing up on the scope.
Here's a plunker showing what I'm talking about.
Why is vm.min and vm.max both blank when I specifically set properties from within the controller function?
If I remove the isolate scope, {{vm.min}} works, as expected. But with the isolated scope, it does not.
1) this.min was set within the controller, so {{vm.min}} should have a value, right?
2) the property max was set on the element, so when i use bindToController and the isolated scope, {{max}} should have a value, right?

Since you have taken the vm.min and vm.max inside the my-example directive, it did not work for you.
Check this code once,
var myApp = angular.module('myApp', []);
function myExample() {
var directive = {
restrict: 'EA',
scope: {
max: '='
},
controller: ExampleController,
controllerAs: 'vm',
bindToController: true, // because the scope is isolated,
templateUrl: `tpl.html`
};
return directive;
}
ExampleController.$inject = ['$scope'];
function ExampleController($scope) {
// Injecting $scope just for comparison
var vm = this;
vm.min = 3;
}
angular
.module('myApp')
.directive('myExample', myExample);
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#*" data-semver="1.5.8" src="https://code.angularjs.org/1.5.8/angular.js"></script>
</head>
<body>
<h1>Why are vm.min and vm.max both blank?</h1>
<my-example max="5">
</my-example>
<script src="script.js"></script>
<script type="text/ng-template" id="tpl.html">
<div>
vm.min: {{vm.min}}
<br/>
vm.max: {{vm.max}}
</div>
</script>
</body>
</html>
If you want to use the templates inside the directive, you should use transclude option.

You need a template or templateUrl attribute in which those values need to be rendered.

Related

AngularJS Directive Template is not display

I start learning AngularJS today with directive ... I can't get directive work. Please help
<html ng-app="ngBasic">
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script>
var app = angular.module('ngBasic', []);
app.controller('ngBasicCtrl', function($scope) {
$scope.age = 20;
});
app.directive('myDirective', function($scope) {
return {
template: '<h1>{{age}}</h1>'
}
});
</script>
</head>
<body ng-controller="ngBasicCtrl">
<h1>{{age}}</h1>
<my-directive></my-directive>
<div my-directive></div>
<div class='my-directive'></div>
</body>
</html>
https://jsfiddle.net/bigzidane/t8Lv3mpg/2/
Currently, the page onlys show 20. Expecting tag but not displaying yet.
Use the scope option to create an isolated scope the template can refer to :
app.directive('myDirective', function() {
return {
scope: {
age: '='
},
template: '<h1>{{age}}</h1>'
}
});
Now you can pass the controller $scope's age to the directive scope, for example this way :
<my-directive age="age+1"></my-directive>
Will render out 21. Updated fiddle -> https://jsfiddle.net/t8Lv3mpg/3/
You have to remove the $scope from the function returning your directive as it does not take a $scope parameter.
app.directive('myDirective', function() {
return {
template: '<h1>{{age}}</h1>'
}
});

angularjs - access transclude html scope from hosting directive

I have a simple directive with transcluded html.
I want to be able to inject directive scope params to the transclude.
I wrote a simple example in plunker :
https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
I know in angular 4 it can be done, but I can't find a good way to do it in angularjs.
// Code goes here
var app = angular.module("app", []);
app.controller("mainCtrl", function($scope) {
$scope.users = ["tal", "oren", "orel", "shluki"];
$scope.deleteUser = (user) => {alert("trying to delete", user);}
});
app.directive('myList', function myList() {
return {
restrict: 'E',
transclude: true,
template: "<div><table><tr ng-repeat='item in collection'><td> This is inside myList - user name: {{item}} <ng-transclude></ng-transclude></td></tr></table></div>",
scope: {
collection: "="
},
replace: true
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="mainCtrl">
<h1>Hello Plunker!</h1>
<my-list collection="users">
<h2>This is transclude</h2>
<button ng-click="deleteUser(user)">Delete user: {{user ? user : "User name should be here"}}</button>
</my-list>
</body>
</html>
Will really appreicate some help.
plunker: https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
Here's a working plunker with your example.
http://plnkr.co/edit/BjSowyQdLXd0xoCZFqZ6?p=preview
The idea is to pass it as contents and not html as string. $compile is here because the link is done after ng-repeats already has transcluded its own template.
var template = '<h1>I am foo</h1>\
<div ng-repeat="item in users">\
<placeholder></placeholder>\
<hr>\
</div>';
var templateEl = angular.element(template);
transclude(scope, function(clonedContent) {
templateEl.find("placeholder").replaceWith(clonedContent);
$compile(templateEl)(scope, function(clonedTemplate) {
element.append(clonedTemplate);
});
});
If you want a proper explanation of what the problem was you should check the detailed answer here : Pass data to transcluded element
Hope this helped you out

How to call a controller function, inside a directive link function using '&' with a parameter?

I'm trying to pass a parameter into a function, inside of a directive link function, similar to this question:
Angular: calling controller function inside a directive link function using &
However, while I've seen a few examples, like this one: Passing Parameters from a Directive to a function and I've seen a parameter value get set inside the link inside a directive. Yet, I've not seen any where you are passing a primitive, like a number in as a parameter to the directive which passes that into the controller function.
I've tried multiple things, but haven't figured out the syntax.
HTML CODE:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.2" data-semver="1.4.2" src="https://code.angularjs.org/1.4.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id="app" ng-app="app">
<div ng-controller="mainCtrl">
<my-directive ctrl-fn="ctrlFn(count)"></my-directive>
</div>
</div>
</body>
</html>
SCRIPT.JS
var app = angular.module('app', []);
app.controller("mainCtrl", function($scope) {
$scope.count = 0;
$scope.ctrlFn = function() {
$scope.count = 0;
console.log('In mainCtrl ctrlFn!');
$scope.count += count;
// console.log("count is: " + JSON.stringify($scope.count));
//Call service here
};
})
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
'count' : '&',
'ctrlFn' : '&'
},
template: "<div><button ng-click='ctrlFn({count: 10})'>Click Here</button></div>",
link: function(scope, element, attributes) {
var count = null;
scope.ctrlFn = scope.ctrlFn({count: count});
//scope.text = scope.fn({ count: 0 });
}
};
});
My plunker is here: http://plnkr.co/edit/6uDntNeqe0g343PmeCED?p=preview
Can a primitive get passed in as a parameter in this use case? If so, what am I missing here?
Aftermath:
In case someone is looking for this syntax: ctrlFn({count: 10}) in the angularjs docs, it's mentioned here under custom directives:
Often it's desirable to pass data from the isolate scope via an
expression to the parent scope, this can be done by passing a map of
local variable names and values into the expression wrapper function.
For example, the hideDialog function takes a message to display when
the dialog is hidden. This is specified in the directive by calling
close({message: 'closing for now'}). Then the local variable message
will be available within the on-close expression.
You've made two mistakes:
scope.ctrlFn = scope.ctrlFn({count: count}); -
this line overrides passed reference to a function and sets it to be value returned by this function (undefined in this case)
to pass count value to your direcive you should use = instead of &
Below is code of simplified example.
script.js:
var app = angular.module('app', []);
app.controller("mainCtrl", function($scope) {
$scope.count = 0;
$scope.ctrlFn = function(count) {
console.log(count)
console.log('In mainCtrl ctrlFn!', count);
$scope.count += count;
//Call service here
};
})
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
'count' : '=',
'ctrlFn' : '&'
},
template: "<div><button ng-click='ctrlFn({ count: 100 })'>Click Here</button></div>"
};
})
index.html:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.2" data-semver="1.4.2" src="https://code.angularjs.org/1.4.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id="app" ng-app="app">
<div ng-controller="mainCtrl"> {{count}}
<my-directive ctrl-fn="ctrlFn(count)"></my-directive>
</div>
</div>
</body>
</html>

AngularJS scopes

I have a weird case of angular scope mixup. Here's the plnkr: http://plnkr.co/edit/o8cyZXkHX8Lt9Vkbn0xm
The transcluded scope does not resolve appCtrl.testString, which is strange, because transcluded content should have the outside scope. What's even more strange is that num gets resolved correctly.
Also, data-test-attr gets the correct value on the element that ng-controller is defined.
This is a minimal working example, so solutions with reordering the elements or defining a scope in a different place do not really suit me. I'd rather hack the repeatedDirective somehow, if that's at all possible.
Here's the code:
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.4.3" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<repeated-directive ng-repeat="num in [1, 2, 3, 4, 5]" ng-controller="AppController as appCtrl" data-test-attr="{{appCtrl.testString}}">
<div>{{num}} - {{appCtrl.testString}}</div>
</repeated-directive>
</body>
</html>
script.js
angular.module('app', [])
.controller('AppController', AppController)
.directive('repeatedDirective', repeatedDirective);
function AppController($scope) {
this.testString = 'Controller value';
}
function repeatedDirective() {
return {
transclude: true,
template: '<div ng-transclude></div>'
};
}
Your controller is not in scope of your directive:
this fixes it without changing anything in your html:
angular.module('app', [])
.controller('AppController', AppController)
.directive('repeatedDirective', repeatedDirective);
function AppController($scope) {
this.testString = 'Controller value';
}
function repeatedDirective() {
return {
transclude: true,
template: '<div></div>',
link: function(scope, element, attrs, ctrl, transclude) {
transclude(scope, function(clone, scope) {
element.append(clone);
});
}
};
}
http://plnkr.co/edit/LsRfyw98f4BP4G72Bgd6?p=preview
This way your transclusion does not use a seperate scope but takes the scope of the controller.
e voila
for more information on what exactly is happening I will include a reference blog :
http://angular-tips.com/blog/2014/03/transclusion-and-scopes/
Within your ng-repeat, you have your scope of [1, 2, 3, 4, 5] and not the appCtrl which explains the empty value of testString (actually it is unexisting). You should access the $parent to be able to have your appCtrl scope.
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.4.3" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<repeated-directive ng-repeat="num in [1, 2, 3, 4, 5]" ng-controller="AppController as appCtrl" data-test-attr="{{appCtrl.testString}}">
<div>{{num}} - {{$parent.appCtrl.testString}}</div>
</repeated-directive>
</body>
</html>
http://plnkr.co/edit/qc3j0nsJZKypXSsqMEqx

Talking to Angular Directive from Controller

I have a directive and I want to call a method of the Directive from outside controller . That means when I click a button in the main controller , I want to hide a component in the directive . Is this really possible ,If yes please help me .
Kamal
To call directive methods outside in a controller I would share a directive control object with the controller. Inside the controller you can call methods of this control object and they get executed inside your directive.
create a control object inside your directive
share this control object with the controller using tw data binding
call methods on this control object inside your controller
here is a plunker that demonstrates it: http://plnkr.co/edit/MqN9yS8R5dnqTfjqldwX?p=preview
You can accomplish this by allowing your directive to listen to a scoped property from your controller. Using isolated scope and the =, you can tell your directive what to pay attention to in order to hide its component:
Html:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script data-require="angular.js#*" data-semver="1.2.0-rc3-nonmin" src="http://code.angularjs.org/1.2.0-rc.3/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="Ctrl">
<h1>Hello Plunker!</h1>
<button ng-click="action()">Toggle</button>
<the-directive show-component="showIt"></the-directive>
</body>
</html>
JavaScript:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope) {
$scope.showIt = true;
$scope.action = function() {
$scope.showIt = !$scope.showIt;
}
});
myApp.directive('theDirective', function() {
return {
restrict: 'E',
scope: {
'showComponent': '='
},
template: '<div><div ng-show="showComponent">show Me!</div></div>'
}
})
Here is a plunker demonstrating the technique.

Resources