Angularjs passing variable to a directive's controller - angularjs

I am creating Angularjs Directive. However i need to pass a paramter to the directive and use it in its controller to populate its items using $http service.
i am passing a "listId" parameter to the directive, the controller inside the directive expects this parameter to retrieve items of that list from the server.
The code in the controller embedded in the directive is commented.
<script type="text/javascript">
var app = angular.module('app', []);
app.controller('metadataCtrl', function ($scope, $http) {
});
app.directive('mydirective', function ($http) {
return {
restrict: 'AE',
template: '<div ng-repeat="model in items">{{ model.name}} </div>',
replace: true,
scope: {
listId: '='
},
controller: function ($scope) {
//console.log(scope.listId);
// console.log(listId);
//$http({ method: 'GET', url: 'http://localhost:62624/home/listvalues?listid=' }).then(function (response) {
// $scope.items = response.data;
//}, function (result) { alert("Error: No data returned"); });
},
link: function (scope, element, attrs) {
console.log(scope.listId);
}
};
});
</script>
The HTML code
<body ng-app="app">
<form name="myForm" ng-controller="metadataCtrl" class="my-form">
<mydirective list-id="99"></mydirective>
</form>
</body>
The listId can be accessed in the link() function in the directive (i am using console.log() to test that). However, this doesn't work in the controller function.

In the controller, use injected $scope.
controller: function ($scope) {
//USE $scope
console.log($scope.listId);
//
//console.log(scope.listId);

Related

Angular - pass object from directive to parent scope on click

I'm using an angular directive to generate a reusable template and show some data in it. The directive also has an ng-click that should take an object and pass it to the parent controller. I'm kind of stuck, not really sure how to pass that data from the directive controller to the scope of the parent controller. I read here but the circumstances are a bit different.
The js code of the directive:
angular.module("app")
.directive('userData', function() {
return {
restrict: "E",
templateUrl: "directives/userData/userData.html",
scope: {
userObj: "="
},
controller: function($scope){
},
link: function(scope, elements, attrs, controller){
}
}
});
And this is the directive html:
<div class="style" ng-click="displayFullDetails(userObj)">{{userObj.first_name}}</div>
Parent controller:
angular.module("app").controller("parentCtrl", ['$scope', function ($scope) {
angular.element(document).ready(function () {
getDataService.getJsonData().then(function (data) {
$scope.users = data.data;
})
});
}]);

Angular 1.X - Accessing controller variable through directive using $watch

Within a controller I have an ajax call that populates $scope.main_data. I want to within a directive get that data when it populates. However the two issues I'm having is:
I cannot seem to access $scope.main_data from the directive.
$scope.watch doesn't seem to work because of this.
How can I print the data from the directive once the data arrives?
CONTROLLER:
app.controller('myCtrl', ['$scope','$http', function($scope, $http) {
$scope.main_data = [];
$http.get("some_url").success( function(data) {
$scope.main_data = data;
});
}]);
DIRECTIVE:
app.directive('myDir', function($compile, $rootScope) {
return {
restrict: 'A',
scope: {
items: '=items'
},
link: function($scope, element, attrs, ctrl) {
$scope.$watch($scope.main_data ,function(newValue,oldValue){
if (newValue){
console.log($scope.main_data);
}
});
}
};
});
HTML:
<div ng-controller="myCtrl">
<div my-dir>
<div>
</div>
The directive in the html exists within the controller but for some reason I can't access the controller scope through $scope but $scope.$parent.
You've almost got it.
app.directive('myDir', function($compile, $rootScope) {
return {
restrict: 'A',
scope: {
items: '=items'
},
link: function($scope, element, attrs, ctrl) {
$scope.$watch("items",function(newValue,oldValue){
if (newValue){
console.log($scope.main_data);
}
});
}
};
});
And html:
<div ng-controller="myCtrl">
<div my-dir items="main_data">
</div>
</div>
Even though you can "hack" around and access main_data using series of$parent calls on your scope and using some other methods, just pass it in to your directive with = binding, so it will be updated when controller scope is updated. In fact you don't even need $watch in this case, you will always have actual data.

How do I access ng-controller from directive?

I've written my own directive from which I want to call a method on the parent ng-controller. I've tried doing that by adding require:'ngController' in my directive. However the returned controller is just an empty object. I'm running 1.4.8.
<div ng-controller="myController">
<div my-directive></div>
</div>
app.directive('myDirective', function() {
return {
restrict: 'A',
scope: false,
require: '^ngController',
link: function (scope, $element, attrs, controller) {
//controller is empty object..
};
}
});
Update: My misstake was that I added a method to the controllers scope when I should add it directly to the controller instead.
app.controller('myController', function($scope) {
$scope.myMethod = function() {}; //Not callable from controller passed to directive
this.myMethod = function() {}; //IS callable from controller passed to directive
});
You can call the parent controller via scope if your directive doesn't have an isolated scope, and you don't need to require ngController.
angular.module('app', [])
.controller('controller', function($scope) {
$scope.greet = function() {
return 'hi'
};
})
.directive('testDirective', function() {
return {
restrict: 'E',
template: '<h1>{{ greet() }}</h1>'
};
});
Output:
hi
Plnkr: http://plnkr.co/edit/uGXC5i1GjphcZCPDytDG?p=preview
The function needs to be available in the directive's scope. So if your directive is in the scope of the controller it is:
scope.fn();

Directive scope got undefined

I get an issue with passing data to angular directives inside ng-repeat, it always got undefined. Here are my code
The Controller:
angular.module('module').controller('ModuleController', ['$scope', 'MyService', function($scope, MyService) {
$scope.getData = function() {
$scope.data = MyService.myGetRequest(); // returning array of objects
};
});
View:
<div ng-controller="ModuleController" ng-init="getData()" ng-switch="data.length > 0">
<div ng-repeat="d in data" ng-switch-when="true">
<my-directive data="d.object"></my-directive>
</div>
</div>
Directive:
angular.module('module').directive('myDirective', [function() {
return {
restrict: 'E',
template: '<div></div>' // let's ignore the template for now,
scope: { data: '=' },
link: function(scope, el, attrs) {
console.log(scope.data); // always undefined
}
};
}]);
Service:
angular.module('module').factory('MyService', ['$resource', function($resource) {
return $resource('/data/:id',
{ id: '#_id' },
{
myGetRequest: { method: 'GET', isArray: true }
});
}]);
I thought it was because the $scope.data still empty when the template loaded. If yes, anyone know what is the solution? Thanks in advance. :)
EDIT: btw, if I put <my-directive data="data"></my-directive> instead of <my-directive data="d.object"></my-directive> the scope.data is not undefined anymore, it will show my array of object from resource.
EDIT2: this <my-directive data="d"></my-directive> will also resulting scope.data in my directive got undefined.
EDIT3: Add service code snippet
I think Shomz found the problem. You should use a promise with async call. Like this:
angular.module('module').controller('ModuleController', ['$scope', 'MyService',
function($scope, MyService) {
$scope.data = [];
MyService.myGetRequest().$promise.then(function(data) {
$scope.data = data;
});
}
});

How to invoke ng-click from a directive partial?

I have a directive that has a local scope where a partial contains ng-click.
The Fiddle is there: http://jsfiddle.net/stephanedeluca/QRZFs/13/
Unfortunatelly, since I moved my code to the directive, ng-click does not fire anymore.
The controller and the directive is as follows:
var app = angular.module('myApp', ['ngSanitize']);
app.directive('plantStages', function ($compile) {
return {
restrict: 'E',
transclude: true,
template: '<figure class="cornStages">\
<p ng-transclude style="color: skyblue"></p>\
<hr/>\
<p ng-bind-html="title"></p>\
<p ng-bind-html="subtitle">{{subtitle}}</p>\
<ul>\
<li ng-repeat="stage in stages" ng-click="changePage(stage)">{{stage}}</li>\
</ul>\
</figure>',
scope: {
stages:"=",
title:'#'
},
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
}
};
});
app.controller('myCtrl', function ($scope, $location, $http) {
$scope.stages = ['floraison', 'montaison'];
$scope.changePage = function (page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
};
});
The html that invokes it is as follows:
<div ng-controller="myCtrl">
Stages,
<p ng-repeat="stage in stages">{{stage}}</p>
<hr/>
Plant stages
<plant-stages
title="<b>Exploration<br/>du cycle</b>"
subtitle="<em>This is a<br/>sub title</em>"
stages="stages"
>
Inner<br/>directive
</plant-stages>
</div>
Any idea?
You can't access changePage() defined in controller's scope from directive directly, since your directive has isolated scope. However, there are still several ways to do it:
Option 1:
Option 1 is the most simple option. However it is much like a workaround and I don't recommend to use it widely. You can get your controller's scope from element passed to link function and invoke changePage there:
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
scope.changePage = element.scope().changePage; // <= Get parent scope from element, it will have changePage()
}
Option 2:
If you don't have any logic that involves scope defined in the outer controller (as in your example), you can define inner controller for your directive and perform it there:
app.directive('plantStages', function ($compile) {
return {
...
controller: ['$scope', function($scope) {
$scope.changePage = function(page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
}
}]
};
});
Option 3:
If you want do reuse logic defined in changePage() in different directives and controllers, the best way to do it is to move the logic to some service that may be injected to both controller and directive:
app.service('changePageService', function() {
this.changePage = function(page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
}
});
app.controller('myCtrl', function ($scope, $location, $http, changePageService) {
...
changePageService.changePage('page');
...
});
app.directive('plantStages', function ($compile) {
...
controller: ['$scope', 'changePageService', function($scope, changePageService) {
$scope.changePage = changePageService.changePage;
}]
...
});
Option 4:
You can pass piece of code like changePage(page) as value of some attribute of the directive and inside directive define scope property with '&' that will create a function that will be executed in the outer controller's scope with arguments passed to that function. Example:
JavaScript
app.directive('plantStages', function ($compile) {
return {
restrict: 'E',
transclude: true,
template: '<figure class="cornStages">\
<p ng-transclude style="color: skyblue"></p>\
<hr/>\
<p ng-bind-html="title"></p>\
<p ng-bind-html="subtitle"></p>\
<ul>\
<li ng-repeat="stage in stages" ng-click="changePage({page: stage})">{{stage}}</li>\
</ul>\
</figure>',
scope: {
stages:"=",
title:'#',
changePage:'&'
},
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
}
};
});
HTML
<div ng-controller="myCtrl">
Stages,
<p ng-repeat="stage in stages">{{stage}}</p>
<hr/>
Plant stages
<plant-stages
title="<b>Exploration<br/>du cycle</b>"
subtitle="<em>This is a<br/>sub title</em>"
stages="stages"
change-page="changePage(page)"
>
Inner<br/>directive
</plant-stages>
Plunker: http://plnkr.co/edit/s4CFI3wxs0SOmZVhUkC4?p=preview
The idea of directives is to treat them as reusable components and avoid external dependencies wherever possible. If you have the possibility to define the behavior of your directive in its own controller then do it.
module.directive('myDirective', function () {
return {
restrict: 'E',
controller: function() { /* behaviour here */ },
template: '<div>Directive Template</div>',
scope: {
/* directive scope */
}
};
});
If this is not possible you can pass the function as explained in the linked question (see comment above). Check the updated fiddle.

Resources