Function inside factory is not accessible from custom directive - angularjs

I have an angular App which has the following code in the script:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope, FileAccessor) {
$scope.countrynames = []
});
myApp.directive('country', function() {
var directiveDefinitionObject = {
restrict: 'E',
templateUrl: 'partials/scape.html',
controllerAs: 'dm',
compile: function(scope, FileAccessor) {
FileAccessor.fetchCountryDetails('https://restcountries.eu/rest/v1/all').success(function(response) { //assigning the fetched countries to the scope object var
scope.countrynames = response;
});
}
}
return directiveDefinitionObject;
})
myApp.factory("FileAccessor", ['$http', function($http) {
return {
fetchCountryDetails: function(url) {
return $http.get(url);
}
}
}]);
Inside scape.html, the following code is placed:
<div ng-controller="Ctrl" class="container" style="height:500px" >
<select ng-model="model" ng-options="obj.name for obj in countrynames | orderBy: '-population'" placeholder="Select" autofocus>
<option value="">- Please Choose -</option>
</select>
{{model.name}}
{{model.currencies[0]}}
</div>
As we can see, I am trying to access the function which is placed inside the factory, FileAccessor from the custom directive, country.
When I run it on browser, there is this error that pops on console:
angular.min.js:86 TypeError: FileAccessor.fetchCountryDetails is not a function
Can somebody please explain what went wrong along with the fixed code?
P.S. I want the HTTP requests to be processed only through the factory(as a requirement).
Edit 1: Including the Plunkr link here: http://plnkr.co/edit/gXQKBd?p=info

Inject the service at your directive declaration, NOT in the compile function:
myApp.directive('country', function(FileAccessor) { //inject FileAccessor here
var directiveDefinitionObject = {
restrict: 'E',
templateUrl: 'partials/scape.html',
controllerAs: 'dm',
compile: function(scope) {
FileAccessor.fetchCountryDetails('https://restcountries.eu/rest/v1/all').success(function(response) { //assigning the fetched countries to the scope object var
scope.countrynames = response;
});
}
}
return directiveDefinitionObject;
})
Edit: There are quite a number of bugs inside your code, maybe you would want to read the docs?
One: Use link instead of compile, as link gives you the scope but compile doesn't:
myApp.directive('country', function(FileAccessor) {
var directiveDefinitionObject = {
restrict: 'E',
templateUrl: 'scape.html',
controllerAs: 'dm',
link: function(scope) {
scope.test="Hello";
FileAccessor.fetchCountryDetails('https://restcountries.eu/rest/v1/all').then(function(response) { //assigning the fetched countries to the scope object var
scope.countrynames = response.data;
});
}
}
return directiveDefinitionObject;
})
Two: Remove the ng-controller directive in your scape.html as this will override the of your directive:
<div class="container" style="height:500px"> //remove the ng-controller='Ctrl'
<select ng-model="model" ng-options="obj.name for obj in countrynames | orderBy: '-population'" placeholder="Select" autofocus>
<option value="">- Please Choose -</option>
</select>
{{model.name}} {{model.currencies[0]}}
</div>
Here's a working plnkr

After analysing your code and agreeing with #CozyAzure, I could see that there is this controller Ctrl which doesn't do anything in the script.
Do you really need this? Eliminate it from your script.js and index.html and do a run. This would surely fetch the data in the desired way.

Related

Angular 1.6+ - How to set ng-options dynamically from the DB using the element inside a forEach loop

I want to take a response from a web service and apply it to the ng-options inside the angular.forEach loop. All I think I have to access it is the element itself. Here is the code. Normally I would just write:
scope.whatIWantTheOptionsToB = DBResponse;
But inside the forEach loop I can't do that. How would I set the options variable inside the for each loop.
Here is my code so far:
HTML:
<div class="form__row">
<label>Injury Type:</label>
<select
class="form__select"
ws="populateDDL"
param="tblInjuryTypes"
ng-model="injuryType_id"
ng-options="injury.id as injury.name for injury in injuryTypes"
ng-change="injuryUpdate()"
name="injuryTypes"
required
></select>
</div>
<div class="form__row">
<label>Work Status:</label>
<select
class="form__select"
ws="populateDDL"
param="tblWorkStatus"
ng-model="workStatus_id"
ng-options="status.id as status.name for status in workStatus"
name="workStatus"
required
></select>
</div>
Directive:
app.directive(
'providerForm',
['$http', 'populateDDL', 'classHandler', function ($http, populateDDL, classHandler){
var directive = {
link: link,
scope: true,
restrict: 'E',
templateUrl: '/modules/providerForm.html',
};
return directive;
function link(scope, element, attrs, ctrl) {
var selects = element.find('select');
angular.forEach(selects, function(e,i) {
if (e.attributes['ws'].value === 'populateDDL') {
scope.data = {
sKey: sessionStorage.getItem('key'),
sTableName: e.attributes['param'].value,
iInjuryTypeId: scope.injuryType_id,
iBodyPartId: scope.bodyPart_id
};
scope.getSelectOptions = new populateDDL(e.attributes['ws'].value, scope.data).
then(function(response) {
console.log(e)
scope.whatIWantTheOptionsToBeEachTimeThru = response;
});
}
});
}
}]
);

How to pass data from controller to custom directive

I am creating a custom directive in AngularJS. This directive should open a popup to display data. The code for the popup is in another html page and the custom directive injects the code into my main page. I am able to open the popup however I cannot display the existing data anywhere in the pop up.
Normally, I am able to display the data in the main page however the data just do not want to go into the html injected by the custom directive.
Like this I do not get any error however it does not pass the data.
Note: I had to trim some of the code here to simplify it.
This is my custom directive:
function updateCandidatePopup() {
var directive = {};
directive.restrict = "E";
directive.scope = {};
directive.templateUrl = "UpdateCandidatePopup.html";
directive.controller = function ($scope) {
$scope.SingleCandidate;
}
return directive;
}
This is where I register it:
myApp.directive("updateCandidatePopup", UpdateCandidatePopup);
This is how I use the directive in the mainpage
<update-candidate-popup value="SingleCandidate" class="modal fade" ng-model="SingleCandidate"
id="myUpdateModal"
role="dialog"
popup-data="SingleCandidate">
zxc</update-candidate-popup>
This is the UpdateCandidatePopup.html:
<div> {{SingleCandidate.FirstName}} </div>
This is the to display the data in the pop up controller: (FYI it is still trimmed)
myApp.controller('CandidatesController', function ($scope, $http, EmployerService, CandidateService) { //we injected localservice
//Select single data for update
$scope.getSingleData = function (C_ID) {
alert(C_ID);
$http.get('http://localhost:49921/api/Candidates/?C_ID=' + C_ID).success(function (data) {
$scope.SingleCandidate = data;
$scope.FName = $scope.SingleCandidate.FirstName;
alert($scope.SingleCandidate.FirstName);
alert($scope.FName);
}).error(function () {
$scope.error = "An Error has occured while loading posts!";
});
};
});
Sorry wrong !, answered your question, here I leave I found a code that will serve for your problem. In the background to the template you want to take, you let a controller and in the statement of your policy, put you going to do with those values, I think in your case is just printing.
myApp.directive('editkeyvalue', function() {
return {
restrict: 'E',
replace: true,
scope: {
key: '=',
value: '=',
accept: "&"
},
template : '<div><label class="control-label">{{key}}</label>' +
'<label class="control-label">{{key}}</label>' +
'<input type="text" ng-model="value" />'+
'<button type="button" x-ng-click="cancel()">CANCEL</button>' +
'<button type="submit" x-ng-click="save()">SAVE</button></div>',
controller: function($scope, $element, $attrs, $location) {
$scope.save= function() {
console.log('from directive', $scope.key, $scope.value);
$scope.accept()
};
}
}
});
jsFiddle
Solved the problem like below. It was only to inject to $scope in the directive controller.
myApp.directive("updateCandidatePopup", function () {
return {
templateUrl : "UpdateCandidatePopup.html",
restrict: 'E',
controller: function ($scope) {
}
}
});

Use Directive scope in Controller

I built a directive that has checkboxes next to labels, inside a jQuery UI Accordion:
<ul class="checkbox-grid employee-info-tabs">
<li ng-repeat="column in columnsData">
<div class="styleAvailableColumns">
<input type="checkbox" ng-model="column.Selected" />
<label class="list-columns">{{ column.ColumnDisplayName }}</label>
</div>
</li>
</ul>
In my Controller, I want to be able to save the selected choices the user makes inside the directive, but I'm not sure how.
Here's my directive:
angular.module('component.column', [])
.directive('uiAccordion', function ($timeout, Columns, $location) {
return {
scope: {
columnsData: '=uiAccordion'
},
templateUrl: '/scripts/app/directives/test.html',
link: function (scope, element) {
var generateAccordion = function () {
$timeout(function () {
$(element).accordion({
header: "> div > h3",
collapsible: true,
active: 'none'
});
});
}
var loc = $location.absUrl();
var reportId = loc.substring(loc.lastIndexOf('/') + 1);
Columns.getAll(reportId).then(function (data) {
scope.columnsData = data;
generateAccordion();
}
Here's how I use the directive in my view <div ui-accordion="accordionData"></div>
I tried using scope: { '=' } but got Expression 'undefined' used with directive 'uiAccordion' is non-assignable!.
I've done some other googling, but I'm not 100% on the 'correct' direction on how to get this accomplished. If I can provide any other information, please let me know.
Set your directive scope to:
scope: {
columnsData: '='
},
Since you want the controller to maintain that data, your controller should have a reference to $scope.columnsData.
Then, on the view which is using the controller, you can feed that into the directive like so:
<div ui-accordion columns-data="columnsData"> </div>
Here's an example of your controller:
angular
.module('...')
.controller('myCtrl', ['$scope', function($scope) {
$scope.columnsData = "abcd123"
}]);
Try using your directive as:
<div ui-accordion="controllersColumnsData"></div>
where controllersColumnsData is a collection you can iterate in your controller whose items will have ColumnDisplayName and Selected properties set from your directive.

Angular ng-click not executing function inside a custom directive

I have created a custom directive and added a controller to it and a function on the state hamburgerClick.Here is my code:
directivesModule.directive('headerDir', [function () {
var headerDir = {
restrict: 'E',
templateUrl: 'App/scripts/main/directives/header/HeaderDir.html',
replace: true
};
headerDir.controller = ['$state', function ($state) {
$state.hamburgerClick = function() {
var app = $('.application-wrap');
if (app.hasClass('menu-opened')) {
app.removeClass('menu-opened');
}
else {
app.addClass('menu-opened');
}
};
}];
return headerDir;
}]);
<div>
<span class="menu-overlay"></span>
<section class="menu-bar">
<article>
<div class="menu-button" ng-click="hamburgerClick()">
<span class="hamburger-icon"></span>
</div>
<h1 class="logo"></h1>
</article>
</section>
My problem is that for some reason the function does not get executed when I am trying to click on it.ANyone know what I am doing wrong?
Try this!
directivesModule.directive('headerDir', [function () {
return{
restrict: 'E',
templateUrl: 'App/scripts/main/directives/header/HeaderDir.html',
replace: true
controller: function($scope){
$scope.hamburgerClick = function() {
var app = $('.application-wrap');
$('.application-wrap').toggleClass('menu-opened');
};
}
}
}]);
There are several things doubtful in your code
1) You should replace $state with $scope
2) You do not use your directive inside your HTML code. Instead, you refer to a directive named 'article'
3) You use replace:true, which replaces the original content of the directive. Unless you planned on defining your $('.menu-button') as header-dir directive, the call to hamburgerClick will be removed.
Furthermore, you could replace
var app = $('.application-wrap');
if (app.hasClass('menu-opened')) {
app.removeClass('menu-opened');
}
else {
app.addClass('menu-opened');
}
with
$('.application-wrap').toggleClass('menu-opened');

Nested directives won't work as desired

I am new to AngularJS, was trying to use nested directives. I have a home layout which has following
<product-list></product-list>
This directive has a template which is present in another file. It has content like that
<div class="products">
<product prod-id="{{product.id}}" ng-repeat="product in products"></product>
</div>
The problem I am facing is when the product-list directive is compiled it isn't able to understand product.id in the expression. It gives me some error like that
Error: Syntax Error: Token 'product.id' is unexpected, expecting [:] at column 3 of the expression [{{product.id}}] starting at [product.id}}].
The directives are defined as
app.directive('productList', function($compile) {
return {
restrict: 'AE',
replace: true,
controller: 'ProductListCtrl',
templateUrl: base_url + 'partials/directives/product-list.html'
};
});
app.directive('product', function() {
return {
restrict: 'AE',
controller: 'ProductCtrl',
templateUrl: base_url + 'partials/directives/product.html',
scope: {
prodId: '=prodId'
},
link: function ($scope, element, attrs) {
var num = $scope.$eval(attrs.prodId);
if(!isNaN(parseInt(num))){
$scope.prodId = num;
}
}
};
});
UPDATE: Added controller for directive
myApp.controller("ProductListCtrl", ['$scope', 'ProductModel', '$stateParams', '$location', function($scope, ProductModel, $stateParams, $location) {
$scope.products = {};
//Fetch the products if we have some category for a given state
if(typeof $stateParams.categoryId != 'undefined' && typeof $stateParams.prodId == 'undefined'){
//Fetch products for selected category and page
ProductModel.getProductsByCategory($stateParams.categoryId, function(products){
$scope.products = products;
});
}
}]);
Please guide what I am doing wrong or missing anything.
Strange thing happened not sure if its the right way, but it worked for me :)
I used
<product prod-id="product.id" ng-repeat="product in products"></product>
instead of
<product prod-id="{{product.id}}" ng-repeat="product in products"></product>
and it worked

Resources