Accessing controller from a directive - angularjs

I have an angular app:
HTML
<body ng-controller="DashboardController as vm">
<div ng-controller="OneController as vm">
Number inside the controler: {{vm.number}}
</div>
<div ng-controller="TwoController as vm">
<me-dir></me-dir>
</div>
</body>
ANGULAR
angular.module('plunker', [])
angular.module('plunker').controller 'DashboardController', ()->
vm = #
angular.module('plunker').controller 'OneController', ()->
vm = #
vm.number = 7
angular.module('plunker').controller 'TwoController', ()->
vm = #
angular.module('plunker').directive 'meDir', ()->
return {
#scope: {} ???
#require ???
#link ???
template: "<strong>Got it!{{number}}</strong>"
}
How can I access the value vm.number from OneController and assign it to the scope inside of the directive?
Is it possible to do it using require and link field from directive?
Can you reference existing controller using require?
At the moment OneController isn't the parent of directive so require: '^ctrlName' doesn't work. I haven't found a lot of documentation about controller/require field. I know how to do it if I would have to pass it in using attributes and stuff. The question is strictly about require link controller directive fields.
Plunker link

Use a service to share data across components
Very simple service upgrade to your code with data shared between controller and directive through the service
angular.module('plunker').service 'SharedService', ()->
vm = #
vm.number=7
angular.module('plunker').controller 'OneController', (SharedService)->
vm = #
vm.number = SharedService.number
angular.module('plunker').directive 'meDir', (SharedService)->
return {
scope:{}
controllerAs:'dir'
controller: ()->
vm = #
vm.number = SharedService.number
template: "<strong>Got it!{{dir.number}}</strong>"
}
DEMO

EDIT 2
var app = angular.module("test", []);
app.controller("Ctrl1", function($scope) {
$scope.name = "Harry Potter";
});
app.controller("Ctrl2", function($scope) {
$scope.any = "Any"
});
app.directive("myDirective", function($compile) {
return {
restrict: "EA",
scope: true,
link : function(scope, element, attr) {
var scopeCtrlOne = angular.element('[ng-controller="Ctrl1"]').scope();
angular.element(element).append($compile(
"<div>Your name is : {{name}}</div>" +
"Change your name : <input type='text' ng-model='name' />")(scopeCtrlOne)
);
}
};
});
h2 {
cursor: pointer;
}
.directive {
border: 5px solid #F5BF6E;
;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="Ctrl1">
<h2 ng-click="reverseName()"> {{name}}, CTRL ONE</h2>
<div ng-controller="Ctrl2">
<h2 ng-click="reverseName()"> {{any}}, CTRL TWO</h2>
<div my-directive class='directive'></div>
</div>
</div>
</div>
EDIT 1
Look this. For details: http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/
var app = angular.module("test",[]);
app.controller("Ctrl1",function($scope){
$scope.name = "Harry";
$scope.reverseName = function(){
$scope.name = $scope.name.split('').reverse().join('');
};
});
app.directive("myDirective", function(){
return {
restrict: "EA",
scope: true,
template: "<div>Your name is : {{name}}</div>"+
"Change your name : <input type='text' ng-model='name' />"
};
});
h2 {
cursor: pointer;
}
.directive {
border: 5px solid #F5BF6E;;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="Ctrl1">
<h2 ng-click="reverseName()">Hey {{name}}, Click me to reverse your name</h2>
<div my-directive class='directive'></div>
</div>
</div>

Related

Adding ng-model directive to dynamically created input tag using AngularJs

I am trying that on a button click, a div and and input tag are created and the input tag contain ng-model and the div has binding with that input.
Kindly suggest some solution.
You can create the div and input beforehand and and do not show it by using ng-if="myVar". On click make the ng-if="true".
<button ng-click="myVar = true">
In controller : $scope.myVar = false;
$scope.addInputBox = function(){
//#myForm id of your form or container boxenter code here
$('#myForm').append('<div><input type="text" name="myfieldname" value="myvalue" ng-model="model-name" /></div>');
}
Here is another solution, in which there's no need to create a div and an input explicitly. Loop through an array of elements with ng-repeat. The advantage is that you will have all the values of the inputs in that array.
angular.module('app', [])
.controller('AppController', AppController);
AppController.$inject = ['$scope'];
function AppController($scope) {
$scope.values = [];
$scope.add = function() {
$scope.values.push('');
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="AppController">
<button ng-click="add()">Click</button>
<div ng-repeat="value in values track by $index">
<input type="text" ng-model="values[$index]"/>
<div>{{values[$index]}}</div>
</div>
<pre>{{values}}</pre>
</div>
UPDATE. And if you want only one input, it's even simpler, using ng-show.
angular.module('app', []);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<button ng-click="show = true">Click</button>
<div ng-show="show">
<input type="text" ng-model="value"/>
<div>{{value}}</div>
</div>
</div>
You should use $compile service to link scope and your template together:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', '$compile', '$document' , function MyCtrl($scope, $compile, $document) {
var ctrl = this;
var inputTemplate = '<div><span ng-bind="$ctrl.testModel"></span>--<span>{{$ctrl.testModel}}</span><input type="text" name="testModel"/></div>';
ctrl.addControllDynamically = addControllDynamically;
var id = 0;
function addControllDynamically() {
var name = "testModel_" + id;
var cloned = angular.element(inputTemplate.replace(/testModel/g, name)).clone();
cloned.find('input').attr("ng-model", "$ctrl." + name); //add ng-model attribute
$document.find('[ng-app]').append($compile(cloned)($scope)); //compile and append
id++;
}
return ctrl;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<input type="button" value="Add control dynamically" ng-click="$ctrl.addControllDynamically()"/>
</div>
</div>
UPDATE: to add a new compiled template each time the button is clicked, we need to make a clone of the element.
UPDATE 2: The example above represents a dirty-way of manipulating the DOM from controller, which should be avoided. A better (angular-)way to solve the problem - is to create a directive with custom template and use it together with ng-repeat like this:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
var ctrl = this;
ctrl.controls = [];
ctrl.addControllDynamically = addControllDynamically;
ctrl.removeControl = removeControl;
function addControllDynamically() {
//adding control to controls array
ctrl.controls.push({ type: 'text' });
}
function removeControl(i) {
//removing controls from array
ctrl.controls.splice(i, 1);
}
return ctrl;
}])
.directive('controlTemplate', [function () {
var controlTemplate = {
restrict: 'E',
scope: {
type: '<',
ngModel: '='
},
template: "<div>" +
"<div><span ng-bind='ngModel'></span><input type='type' ng-model='ngModel'/></div>" +
"</div>"
}
return controlTemplate;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<input type="button" value="Add control dynamically" ng-click="$ctrl.addControllDynamically()"/>
<div ng-repeat="control in $ctrl.controls">
<control-template type="control.type" ng-model="control.value"></control-template>
</div>
</div>
</div>

angular same height directive not working

I have created a directive so that the columns in a bootstrap row are of same height.
Here is the code - http://jsbin.com/waxoboloqo/edit
But it is not working. Can you please fix it ?
Html code:
<h1>Height: {{ hei }}</h1>
<div class="row">
<div class="col-lg-6 col-1">
<h1 ng-repeat="x in y" match-height>{{x}}</h1>
</div>
<div class="col-lg-6 col-2">
</div>
</div>
CSS:
.col-lg-6 {
border: 1px solid;
min-height: 50px;
}
Js:
angular.module('myApp', [])
.controller('myController', function($scope){
$scope.y = [1,2,3];
$scope.hei = "initial"
})
.directive('matchHeight', function(){
return function(scope, element) {
if(scope.$last){
scope.hei = $(element).height();
$(element).closest(".row").find(
".col-2").height(scope.hei);
}
}
});
you can try this.
app.directive('getHeight',function () {
return {
restrict: 'AE',
link: function (scope, element, attrs) {
scope.$watch(function(){
scope.style = {
height:element[0].offsetHeight+'px',
// width:element[0].offsetWidth+'px'
};
});
}
};
});
<div class="col-lg-6 col-1" get-height>
<h1 ng-repeat="x in y">{{x}}</h1>
</div>
<div class="col-lg-6 col-2" ng-style="style">
</div>

Access the main controller's scope from inside a directive

Suppose the following blueprint code:
<div ng-controller="myCtrl">
<div ng-repeat="...">
<div ng-repeat="...">
<div ng-repeat="...">
<div ng=if="..." my-directive>
</div>
</div>
</div>
</div>
</div>
myApp.directive('myDirective', function() {
return {
controller: function($scope){
console.log('controller scope');
console.log($scope);
},
link:function(scope,element){
console.log('link scope');
console.log(scope);
}
}
});
Both outputs in console will point to the scope created by ng-if directive. My question is how may I access myCtrl's scope from inside the directive . Of course not by using $parent.$parent....
The easiest way could be by using require in the directive, like:
<div ng-controller="MyCtrl">
<div my-directive></div>
</div>
var myApp = angular.module("app", []);
myApp.controller("MyCtrl", function($scope) {
this.text = "I am in Controller Scope";
this.getValue = function() { return this.text; };
});
myApp.directive("myDirective", function() {
return {
require: "^ngController",
link: function(scope, elem, attrs, ngCtrl) {
elem.text(ngCtrl.getValue());
}
};
});
EDIT
In your case, I think you could use the controller scope variables and methods in the directive by using scope binding with &; snippet below:
<div ng-controller="MyCtrl as vm">
<my-directive on-get-value="vm.getValue()">
</my-directive>
</div>
angular.module('app', [])
.controller('MyCtrl', function($window) {
var vm = this;
vm.getValue = function() { $window.alert("I am in Controller Scope"); };
})
.directive('myDirective', function() {
return {
scope: {
onGetValue:'&'
},
controllerAs:'vm',
controller: function($scope) {
$scope.onGetValue();
}
};
});
Use services to share data between angular components. This question might be a good start: Share data between AngularJS controllers. This approach will work for sharing data between controller and directive as well
When you are creating your directive, the returning function is called DDO (Directive Defining Object). One of its attributes is 'scope'. if you initialize it with scope : true, the directive will prototypically inherit the parent scope. If you set scope: false, the directive will use the parent scope. And finally, if you set scope : {...}, it will created an isolated scope.
var app = angular.module("test",[]);
app.controller("myCntrl",function($scope){
$scope.text = "Im in controller Scope";
});
app.directive("myDirective", function(){
return {
restrict: "EA",
scope: true,
template: "<div>Where are you, directive ? {{text}}</div>"
};
});
h2 {
cursor: pointer;
}
.directive {
border: 5px solid #F5BF6E;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="myCntrl">
<h2 ng-click="reverseName()">Where are you ? {{text}}</h2>
<div my-directive class='directive'></div>
</div>
</div>
You can check this link for more details : Directive Scopes

Angularjs - Scope value is not applied in template

I have used the directive scope in directive template.
I have tried to get the html from template cache which was stored earlier.
But the current directive scope is not applied to the directive. I don't what will be the reason.
I have tried to compile the template and get the value. But not applied.
contentString = $templateCache.get('template/MyTemplate')
var div = document.createElement("div");
div = angular.element(div).html(contentString);
var s = $compile(div.contents())($scope);
template/MyTemplate would be following
<div>
{{obj.value}}
</div>
Directive scope like following,
link: function ($scope, $element, $attributes) {
$scope.obj.value="This is my test"
}
I got the output like
<div class="ng-scope">
{{obj.value}}
</div>
What will be the issue?
Check this example which is using a custom directive with an isolated scope. I hope the below examples will be of help to you.
angular
.module('demo', [])
.directive('hello', hello);
hello.$inject = ['$templateCache', '$compile'];
function hello($templateCache, $compile) {
var directive = {
scope: {
},
link: linkFunc
};
return directive;
function linkFunc(scope, element, attrs, ngModelCtrl) {
scope.obj = {
value: 'Hello, World!'
};
var template = $templateCache.get('templateId.html');
element.html(template);
$compile(element.contents())(scope);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="demo">
<hello></hello>
<script type="text/ng-template" id="templateId.html">
<div>
{{obj.value}}
</div>
</script>
</div>
Another example using controller aliasing syntax i.e. controller as with a directive to be consistent with using controller as with view and controller pairings
angular
.module('demo', [])
.controller('DefaultController', DefaultController)
.directive('hello', hello);
function DefaultController() {
var vm = this;
vm.message = 'Hello, World!';
}
hello.$inject = ['$templateCache', '$compile'];
function hello($templateCache, $compile) {
var directive = {
link: linkFunc,
scope: {
message: '='
},
controller: HelloController,
controllerAs: 'vm',
bindToController: true
};
return directive;
function linkFunc(scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get('templateId.html');
element.html(template);
$compile(element.contents())(scope);
}
}
function HelloController() {
var vm = this;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="demo">
<div ng-controller="DefaultController as ctrl">
<hello message="ctrl.message"></hello>
<script type="text/ng-template" id="templateId.html">
<p>{{vm.message}}</p>
</script>
</div>
</div>

Angularjs: get parent element width

html"
<div id="parent">
<input type=button ng-click="getWidth()">
</div>
js:
function Ctrl($scope) {
$scope.getWidth = function(){
// How to get div[id=parent] width here?
}
}
So my question is how to get parent div in getWidth method?
like hansmaad's answer, you better use directive for handling DOM stuffs.
app.directive('getWidth', ['$timeout', '$location', function($timeout, $location) {
return {
scope: {
callbackFn: "&"
},
link: function(scope, elem, attrs) {
scope.callbackFn({width: elem[0].clientWidth});
}
}
}]);
in html
<body ng-controller="MainCtrl" >
<div get-width callback-fn="returnWidth(width)" style="height:100%; width: 100%;">
<p >Hello {{name}}!</p>
</div>
</body>
working example here
use $event.target to get the element click and then use jquery to find width
function SimpleController($scope) {
$scope.width = 0;
$scope.getWidth = function($event) {
// How to get div[id=parent] width here?
$scope.width = $($event.target).parent().outerWidth();
}
}
#parent{
background-color : red;
width : 200px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<div class="container" ng-app="" ng-controller="SimpleController">
<div id="parent">
parent width : {{width}}
<button type=button ng-click="getWidth($event)"> find width </button>
</div>
</div>

Resources