AngularJS scopes - angularjs

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

Related

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

AngularJS Directive as a comment

I'm just playing with angular directives, but somehow the comment way to add directives is not showing up. Here is the markup.
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="angular.js"></script>
</head>
<body ng-app="directive-app">
<first-directive></first-directive>
<div first-directive></div>
<div class=first-directive></div>
<!-- directive: first-directive -->
<script src="main.js"></script>
</body>
</html>
Directive definition
var app=angular.module('directive-app',[]);
app.directive("firstDirective", function() {
return {
restrict : "EACM",
templateUrl : 'template.html'
};
});
In template.html there is one h1 element. But comment way to add directives is not showing up in UI and in which case comment approach is even required.
Set replace option to true as follows
app.directive("firstDirective", function() {
return {
restrict: "EACM",
replace: true,
templateUrl: 'template.html'
};
});
Here's demo.

Does controllerAs work with Isolated Scope?

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.

AngularJS - how to pass object (created on the fly) with a interpolated data to a custom directive

I would like to create a custom directive to which I can pass an object (created on the fly) with interpolated values.
Example:
Newly created array as a parameter:
{userName: vm.data.name}
vm.data.name should be replaced with controller data.
Here is my code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom directive</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
<script>
(function(angular) {
'use strict';
angular.module('testDirectiveApp', [])
.controller('testController', ['$scope', function($scope) {
var data = {
name: 'ABC'
}
var vm = this;
vm.data = data;
}])
.directive('customDirective', ['$interpolate', function($interpolate) {
return {
scope: {
customParam: '='
},
link: function(scope, element, attributes){
scope.$watch('customParam', function() {
console.log(attributes.customParam, $interpolate(attributes.customParam)(scope), scope.$eval(attributes.customParam));
});
}
};
}]);
})(window.angular);
</script>
</head>
<body ng-app="testDirectiveApp">
<div ng-controller="testController as vm">
Name: <input ng-model="vm.data.name"> <hr/>
<div custom-directive custom-param="{userName: vm.data.name}"></div>
<p>{{vm.data.name}}</p>
</div>
</body>
</html>
Is this doable? What I'm doing wrong?
Best Regards,
The problem is that you are trying to access a scope variable with the attributes object, and not the scope object.
In your directive, change attributes.customParam to scope.customParam
http://plnkr.co/edit/1o4746GamtIcwHekoKyl?p=info

Angular js directive issue

Below is my code
I created a simple page and include a directive in it. And on ng-click in directive i want to call parent scope's method.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Directive Page</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"> </script>
</head>
<body>
<div ng-app="myapp" ng-controller="myController">
<ul-dir on-item-click="itemClick(obj)"></ul-dir>
</div>
</body>
</html>
<script>
var myapp = angular.module("myapp", []);
myapp.directive('ulDir', function() {
return {
restrict: 'E',
replace: 'true',
template: '<div id="container"><ul><li ng-repeat="content in contents">{{content.name}}</li></ul></div>',
controller: function ($scope) {
$scope.contents = [{'name':'Nishu', 'age':'20'},{'name':'Nidhi', 'age':'21'},{'name':'Kirti', 'age':'24'}];
},
scope: {
onItemClick: '&' // Pass a reference to the method
}
}
});
myapp.controller('myController', function($scope) {
$scope.itemClick = function(content){
console.log("Success : "+content);
};
});
</script>
So my console log print as "success : undefined"
So content object not passing from directive scope to parentscope.
Please help me.
I believe your call inside template should either be:
<a href="#" ng-click="onItemClick({'content':content})">
or
<a href="#" ng-click="onItemClick({'obj':content})">
Can you try. For more details see this SO post Can an angular directive pass arguments to functions in expressions specified in the directive's attributes?

Resources