AngularJS pass $resource as a directive parameter - angularjs

I have just came up with a directive that loads a dropdown box according to a list coming from an API call ($resource).
Controller:
App.controller(
'TestCtrl', [
'$scope', 'countriesFactory',
function($scope, countriesFactory){
/* Call API */
countriesFactory().then(function(data){
$scope.countryList = data;
});
}])
The API call returns:
{"country":[{"code":"ABW","label":"Aruba"},{"code":"AFG","label":"Afghanistan"},{"code":"AGO","label":"Angola"}]}
Template:
<input-select model-ref-list="countryList"></input-select>
Directive:
App
.directive("inputSelect"
, function() {
var Template =
'<select ng-options="item.label for item in modelRefList" required></select>';
return {
restrict: 'EA',
template: Template,
scope: {
modelRefList: '='
},
link: function(scope){
console.log(scope.modelRefList);
}
};
}
);
First of all: I simplified a lot the overall issue, so that it looks that the directive is completely overkill in that situation, but in the end, it is not :D.
Problem: My console.log is always undefined.
I made a bit of research and realized that I needed to play with promises to wait for my country list to appear to be actually given to the directive.
So I tried modifying my controller and not use the result of the API call promise, but directly the resource itself:
New Controller:
App.controller(
'TestCtrl', [
'$scope', 'countriesFactory',
function($scope, countriesFactory){
/* Call API */
$scope.countryList = resourceAPICall();
}])
But still undefined :/.
How can I pass direclty the resource (containing the promise I can then use to defer the load of the select) to the directive?
SOLUTION FOR ANGULARJS 1.2:
Directive:
App
.directive("inputSelect"
, function() {
var Template =
'<select ng-options="item.label for item in modelRefList" required></select>';
return {
restrict: 'EA',
template: Template,
scope: {
modelRefList: '='
},
link: function(scope){
scope.modelRefList.$promise.then(function(data){
console.log(data);
}
};
}
);
To pass a API call result to a directive, you need to pass its resource and play with its promise inside the directive itself.
Thanks everybody for the help.

Here we simulated async call factory by using wrapper with $q.
We changed modelReflist to modelRefList
added ng-model="item" to template
HTML
<div ng-controller="TestCtrl">
<input-select model-ref-list="countryList"></input-select>
</div>
JS
var App = angular.module('myModule', ['ngResource']);
App.controller(
'TestCtrl', [
'$scope', 'countriesFactory',
function ($scope, countriesFactory) {
/* Call API */
countriesFactory.resourceAPICall().then(function (data) {
$scope.countryList = data.country;
console.log($scope.countryList);
});
}])
App.$inject = ['$scope', 'countriesFactory'];
App.directive("inputSelect", function () {
var Template = '<select ng-model="item" ng-options="item.label as item.label for item in modelRefList" required></select>';
return {
restrict: 'EA',
template: Template,
scope: {
modelRefList: '='
},
link: function (scope) {
console.log(scope.countryList);
}
};
});
App.factory('countriesFactory', ['$resource', '$q', function ($resource, $q) {
var data = {
"country": [{
"code": "ABW",
"label": "Aruba"
}, {
"code": "AFG",
"label": "Afghanistan"
}, {
"code": "AGO",
"label": "Angola"
}]
};
var factory = {
resourceAPICall: function () {
var deferred = $q.defer();
deferred.resolve(data);
return deferred.promise;
}
}
return factory;
}]);
Demo Fiddle

modelReflist needs to be fully camel-cased in your directive scope. modelRefList.

Related

Angular directive controller scope

I'm trying to develop a directive that has it's own controller so it can collect the data it need's from the API and be injected anywhere.
Here's what I got so far:
(function () {
'use strict';
angular
.module('app.hostelsmg.bookings')
.directive('bookingsChart', bookingsChart);
function bookingsChart() {
return {
restrict: 'E',
scope: true,
controller: [function(){
$scope.test = 'Hi there!';
}],
compile: function(tElem, attrs){
tElem.html('{{test}}');
return function(scope, elems, attrs){
}
}
}
}
})();
So what I'd like, is the directive to get data from it's own controller. So far I couldn't get it to work that way.
Does anyone know how?
You can use something like this
function bookingsChart() {
return {
restrict: 'E',
scope: true,
controller: ['$scope', 'yourservice', function ($scope, yourservice) {
$scope.data = []; //bind this data to your view
$scope.getServiceData = function (count) {
//your service call
yourservice.getServiceData(count).then(function (data) {
$scope.data = data; //sets data in $scope.data variable
});
}
}],
link: function (scope, elements, attributes) {
//if you want to pass any parameters from your UI to your service method
scope.getServiceData(attributes.count); //calls the getServiceData in controller defined above
}
}
}

Angularjs passing variable to a directive's controller

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);

directive unable to retrieve data from a service

I have a little SPA using angular. The concept is simple, after login, $routeProvider redirects to a home page where I have a homeController specified.
this is from my home view that is rendered by ng-view while navigating to "/home" :
<my-directive datas=getData()></my-directive>
<ul>
<li ng-repeat="data in datas"> {{data.title}} {{data.content}} </li>
</ul>
my directive is written as:
angular.module('app').directive('myDirective', ['myService', function (myService) {
return {
restrict: "E",
scope: {
data: '='
},
templateUrl: "partials/my-directive.html",
controller: function ($scope) {
$scope.getDatas = function()
{
myService.retData();
}
}
};
}]);
the home controller is:
angular.module('app').controller('homeController', homeController);
homeController.$inject = ['myService', '$scope'];
function homeController(myService, $scope) {
var vm = this;
vm.data = [];
initController();
function initController() {
vm.data = myService.retData();
}
}
and finally my service is
angular.module('app').service('myService', myService);
function myService () {
var data = [
{ id: 1, title: 'Albert Einstein', content: 'Random Content' }
];
return {
retData: function () {
return data;
},
addData: function (title, content) {
var currentIndex = data.length + 1;
data.push({
id: currentIndex,
title: title,
content: content
});
}
};
}
now that i mentioned everything, here comes the problem. the directive is not able to retrieve data from the service. Actually when i run the project in VS2013, myDirective.js is not even loaded. I included all services, directives, controllers etc in the main HTML page.
What is causing this problem?
Does it have something to do with the scope being isolated in the directive?
What is a better approach to sharing data between a controller, directive and service?
I may have made some silly mistakes while rewriting all the code. Please do point them out, however keep in mind my actual issue and what error may be causing that.
Better to use isolated scope to pass data controller to directive.
Html:
<my-directive datas="getData()" data="data"></my-directive>
Directive:
angular.module('app').directive('myDirective', [function () {
return {
restrict: "E",
scope: {
data: '='
},
templateUrl: "partials/my-directive.html",
link: function (scope) {
//Here you got the isolated scope data
var details = scope.data;
}
};
}]);
OR
app.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: 'partials/my-directive.html',
scope: {
date: '=',
},
controller : ['$scope', 'myService', function($scope, myService) {
myService.retData();
}],
link: function(scope, elem, attr) {
//
}
};
});

How to deal with more than one scope in an Angular directive?

Using directives I ended stuck when I needed to have more than one scope.
I'm building a data visualization app with Mongoose Node, Express and D3JS.
Here's the directive
angular.module('prodsChart', [])
.controller('businessCtrl', ['$scope','$http', 'BusinessSrv', 'Products', function($scope, $http, $location, BusinessSrv, Products) {
Products.get()
.success(function(data) {
$scope.products = data;
});
BusinessSrv.getTotal()
.success(function(data) {
$scope.businessSales = data;
});
}])
.directive( 'saleProd', [
function () {
return {
restrict: 'E',
link: function (scope, element) {
// Building the chart here
}
And the HTML :
<sale-prod></sale-prod>
Is it good to inject that way the Services in the Directive ?
Now I have 2 set of data in two $scope.
How do I use them in the directive ?
You can inject the $rootScope into your directive:
angular.module('prodsChart', [])
.directive( 'saleProd', ['$rootScope', function ($rootScope) {
// your code here
}]);
and then use it everywhere within the directive.
In 1st and 2nd example directive is in controller's scope and controller's datasets transferred to the directive as attributes. In 1st example directive can modify controller's data. In 2nd example directive use controllers data as strings and creates 2 objects 'productsObj' and 'salesObj' and can't modify parent's scope. It depends on how you handle attributes in the directive and how to transfer them into it. Just click on items in 'Product list (directive)' section and you'll see result.
1st: http://plnkr.co/edit/v46oEGHvUnxMNYsKAeaW?p=preview
var directive = function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'directive-template.html',
scope: {
products: '=',
sales: '='
}
};
};
html:
<div ng-controller="BusinessController as BusinessCtrl">
<sale-prod products="BusinessCtrl.products" sales="BusinessCtrl.sales"></sale-prod>
</div>
2nd: http://plnkr.co/edit/7CyIsqBNLbeZjyfbkGo9?p=preview
var directive = function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'directive-template.html',
scope: {
products: '#',
sales: '#'
},
link: function(scope, element, attrs) {
attrs.$observe('products', function(newVal) {
scope.productsObj = angular.fromJson(newVal);
});
attrs.$observe('sales', function(newVal) {
scope.salesObj = angular.fromJson(newVal);
});
}
};
};
html:
<div ng-controller="BusinessController as BusinessCtrl">
<sale-prod products="{{BusinessCtrl.products}}" sales="{{BusinessCtrl.sales}}"></sale-prod>
</div>
3rd example is just a piece of code that show's how to inject service in directive and controller. I add it because in you example i didn't see service injection in directive:
(function(undefined) {
var app = angular.module('prodsChart', []);
var controller = function($scope, Business, Products) {
// controller logic
};
var directive = function(Business) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// here you can use all Business service logic
}
};
};
var serviceBusiness = function() {
// business service logic
};
var serviceProducts = function() {
// products service logic
};
app.controller('BusinessController', ['$scope', 'Business', 'Products', controller])
.directive('saleProd', ['Business', directive])
.service('Business', serviceBusiness)
.service('Products', serviceProducts);
})();
html:
<div ng-controller="BusinessController as BusinessCtrl"></div>
<sale-prod></sale-prod>
You could inject a Service into a directive and use it to bring data across the application or use $emit.
A less elegant solution would be using a .value() and deal with it everywhere in your application.

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;
});
}
});

Resources