Here are mine two directives. I basically want to share the scope between them. However with this I get an undefined $http error. I know I need to put $http somewhere, but where?
aresAnalytics.directive('market', function($http) {
return {
restrict: 'E',
controller: function ($scope, Data) {
$scope.data = Data;
},
link: function(scope, element, attrs) {
element.bind('click', function() {
console.log("A1 " + attrs.market);
scope.temp = attrs.market;
$http.get('get_markets').success(function(markets) {
Data.markets = markets;
});
})
}
}
});
aresAnalytics.directive('events', function($http) {
return {
restrict: 'E',
controller: function ($scope) {
scope = $scope;
},
link: function(scope, element) {
element.bind('click', function() {
console.log(scope.temp);
});
}
}
});
HTML:
<market ng-repeat="market in data.markets" market="{{ market }}">
{{ market }}
</market>
Also, I think the way I am doing this
$http.get('get_markets').success(function(markets) {
Data.markets = markets;
});
is not correct, what can I replace with it.
And, should I use Isolate Scope '#' instead? How will that look like?
Thanks for reading this!
You need to inject the $http service like so.
app.directive('something', function( $http ) { ... });
Angular Dependency Injection
To access attributes like that there are many ways, but this is a simple way.
change html to
<market ng-repeat="market in data.markets" market="market">
{{ market }}
</market>
Require parse like this
app.directive('something', function( $http, $parse ) { ... });
And get your attribute like so
scope.temp = $parse(attrs.market)(scope);
This way you're getting it directly from the scope while the other way, angular hasn't rendered the attribute yet.
The problem is with your dependency injection. Try this:
aresAnalytics.directive('market', ['$http', function($http) {
// (you code)
}]);
or if you don't use code minifiers/uglifiers:
aresAnalytics.directive('market', function($http) {
// (you code)
});
I don't know, but I simply had to append a $parent to my scope to always use the parent scope. (like use scope.$parent instead of scope).
REF: https://github.com/angular/angular.js/wiki/Understanding-Scopes
Related
How can I do console.log(childResult) in the code below? In general where/how can I learn how to access ngRepeat scope(s) variables... NOT those in respective controller (it seems easy)... but esp I want to access the LOOP VARIABLES e.g. childResult in my case. I have read all the guides and tutorials incl stackoverflow questions but I just cannot resolve it.
app.directive("appNamesTyped", ['$http', '$timeout', function ($http, $timeout) {
//app-names-typed
return {
restrict: 'A',
template: "<li ng-repeat=\"childResult in childrenLookupResults.d.as_typed | orderBy:'fname' | limitTo: limitLookupResults : beginLookupResults\"> </li>",
link: function (scope, element, attrs) {
try {
console.log(childResult);
} catch(er) {
}
}
};
}]);
The way I would do it is by using the childrenLookupResults object and looping through it. It would print the elements without regard to the template. Something like this
link: function (scope, element, attrs) {
for(var i = 0; i < scope.childrenLookupResults.length; i++){
console.log(scope.childrenLookupResults[i]);
}
}
Here's a related question that might be helpful for you: AngularJS: Setting a variable in a ng-repeat generated scope
Another way:
link: angular.forEach(scope.childrenLookupResults,function(childrenLookup){
console.log(childrenLookup);
});
I am trying to use $emit in my code for a directive.
I have
(function () {
angular
.module('myApp')
.directive('testDirective', testDirective);
function testDirective() {
var directive = {
link: link,
restrict: 'A',
controller: testCtrl,
controllerAs: 'vm'
};
return directive;
function link(scope, element, attrs) {
}
}
function testCtrl() {
var vm = this;
// do something
vm.$emit('someEvent', {'id': '123'})
}
})();
However, I am getting 'TypeError: vm.$emit is not a function'. I am not sure how to fix this. Can anyone help? Thanks a lot!
controllerAs just means that scoped variables are attached directly to the controller -- it doesn't mean that the controller is an instance of an angular scope itself. In this case, you'll need to inject the scope into the controller and then emit the event from on the scope:
function testCtrl($scope) {
// do something
$scope.$emit('someEvent', {'id': '123'})
}
Also beware, the normal injection rules apply -- If you're going to minify this, you'll probably need something like:
testCtrl['$inject'] = ['$scope'];
I'm trying to update some code in a small personal project which uses angular to conform to better practices, and I have heard that the future of Angular can be mimicked in a way by putting a lot of functionality into controllers of directives. I'm not sure how correct my understanding is but it seems like a clean way of organizing code.
Anyways, to get to the point of my issue, I can't seem to get the isolate scope to work when I give my directive a controller. I've been googling my brains out trying to figure out what the issue is, and I saw many topics about it, but none which solved my issue.
Here's a code fragment:
angular.module('myCongresspersonApp')
.directive('congressPersonPane', function () {
var controller = [function() {
}];
return {
templateUrl: 'app/congressPersonPane/congressPersonPane.html',
restrict: 'EA',
scope: {
congressPerson: '=info'
}//,
// controller: controller,
// controllerAs: 'paneCtrl',
// bindToController: true
};
});
This is really just a way to test before I move functionality around, but when I uncomment those lines, I no longer have access to the isolate scope I pass in and all the data accessed through that is gone (it is an array object in a ng-repeat).
I also have a similar problem in a directive which sits inside this directive. That problem makes me even more confused, as I can correctly use a method if I define it under the $scope, but when I use controllerAs, I cannot use that method. So I am pretty stumped as I pulled this implementation (to remove scope) from this website (mentioned by Lauren below)
here's the code for that:
'use strict';
angular.module('myCongresspersonApp')
.directive('voteRecord', function () {
var controller = ['$scope', 'sunlightAPI', function ($scope, sunlightAPI) {
var voteCtrl = this;
voteCtrl.voteInfo = [];
voteCtrl.test = 'Test';
voteCtrl.pageNumber = 1;
voteCtrl.repId = '';
console.log('inside controller definition');
voteCtrl.getVotingRecord = function(repId) {
console.log('inside method');
voteCtrl.repId = repId;
var promiseUpdate = sunlightAPI.getVotes(repId, pageNumber);
promiseUpdate.then(function(votes) {
console.log('fulfilled promise');
voteCtrl.voteInfo = votes;
console.log(voteCtrl.voteInfo);
}, function(reason) {
console.log('Failed: ' + reason);
}, function(update) {
console.log('Update: ' + update);
});
};
voteCtrl.nextPage = function() {
voteCtrl.pageNumber++;
voteCtrl.getVotingRecord(voteCtrl.repId, voteCtrl.pageNumber);
};
voteCtrl.previousPage = function() {
voteCtrl.pageNumber--;
voteCtrl.getVotingRecord(voteCtrl.repId, voteCtrl.pageNumber);
};
}];
return {
restrict: 'EA',
scope: {
repId: '=representative'
},
controller: controller,
contollerAs: 'voteCtrl',
bindToController: true,
templateUrl: 'app/voteRecord/voteRecord.html',
};
});
I'm not sure if that issue is related to this issue or not, but they seem similar. Any help or directions to resources which could help would be really appreciated, as I don't want to be writing code where my conventions are constantly changing because I don't fully understand why one thing works.
Thanks!
I'm not sure if I fully understand what your problem is, but it sounds like you are having problems accessing $scope from the controller. You can actually pass in the scope to the controller, like this:
angular.module('myCongresspersonApp')
.directive('congressPersonPane', function () {
var myController = function($scope) {
// use $scope in here
};
return {
templateUrl: 'app/congressPersonPane/congressPersonPane.html',
restrict: 'EA',
scope: {
congressPerson: '=info'
},
controller: ['$scope', myController]
};
});
This blog post details how to use controllers in your directives. Also the Angular documentation will explain more too. Good luck!
I'm using ng-grid for data display and I want to dynamically adjust height of the grid itself depending on the number of returned results and user's monitor resolution.
Here's the angular code:
angular.module('modulename', [])
.controller('ctrl', function($scope, $http) {
$scope.gridResult = [];
$scope.gridOptions = {
data: 'gridResult'
};
$scope.listData = function() {
//Async call using $http.get which on success callback asigns response.data to $scope.gridResult
}
//Get data when page loads
$scope.listData();
})
.directive('tableheight', function() {
return {
restrict: 'A',
scope: {
},
controller: 'ctrl',
link: function(scope, elem, attrs) {
scope.$watchCollection('gridResult', function(n, o) {
console.log(n); //Shows empty array
if (n.length > 0) {
console.log(n) //Never displayed
//Calculate new size for the grid
...
}
});
}
};
});
HTML portion:
<div data-ng-grid="gridOptions" tableheight></div>
As you can see 'gridResult' is always empty array even after 'listData' success callback. If I move whole $watchCollection method to controller body everything is working as expected. What I'm trying to achieve is somehow run directive's linking function after DOM has been rendered which includes rendering data from async call.
I guess I'm doing something wrong here or my approach is wrong, however I would appreciate if someone could offer a solution to this.
On a subject of calling directive's linking function after DOM is rendered and ready I tried putting the code inside angular's $timeout with 0 delay but that didn't do anything for me. And speaking of this is there any way to call directive's linking function after DOM rendering since I believe some of my problems are coming from this issue?
Just now there is no communication between the controller and directive. Here you find basic scenarios how such communication can be organized.
The most secure and generic option would be to create data bind like that:
angular.module('modulename', [])
.controller('ctrl', function($scope, $http) {
$scope.gridResult = [];
...
})
.directive('tableHeight', function() {
return {
require: 'dataGrid', // it would be better to use this directive only alongside data-grid
restrict: 'A',
scope: {
gridResult: '=tableHeight'
},
// controller: 'ctrl', this is unnecessary -- it points to directive's controller
link: function(scope, elem, attrs) {
scope.$watchCollection('gridResult', function(n, o) {
...
});
}
};
});
<div data-ng-grid="gridOptions" table-height="gridResult"></div>
Problem
Dynamically add the ng-bind attribute through a custom directive to be able to use ng-bind, ng-bind-html or ng-bind-html-unsafe in a custom directive with out manually adding to the template definition everywhere.
Example
http://jsfiddle.net/nstuart/hUxp7/2/
Broken Directive
angular.module('app').directive('bindTest', [
'$compile',
function ($compile) {
return {
restrict: 'A',
scope: true,
compile: function (tElem, tAttrs) {
if (!tElem.attr('ng-bind')) {
tElem.attr('ng-bind', 'content');
$compile(tElem)
}
return function (scope, elem, attrs) {
console.log('Linking...');
scope.content = "Content!";
};
}
};
}]);
Solution
No idea. Really I can not figure out why something like the above fiddle doesn't work. Tried it with and with out the extra $compile in there.
Workaround
I can work around it might adding a template value in the directive, but that wraps the content in an extra div, and I would like to be able to that if possible. (See fiddle)
Second Workaround
See the fiddle here: http://jsfiddle.net/nstuart/hUxp7/4/ (as suggested by Dr. Ikarus below). I'm considering this a workaround for right now, because it still feels like you should be able to modify the template before you get to the linking function and the changes should be found/applied.
You could do the compiling part inside the linking function, like this:
angular.module('app').directive('bindTest', ['$compile', function ($compile) {
return {
restrict: 'A',
scope: true,
link: {
post: function(scope, element, attrs){
if (!element.attr('ng-bind')) {
element.attr('ng-bind', 'content');
var compiledElement = $compile(element)(scope);
}
console.log('Linking...');
scope.content = "Content!";
}
}
};
}]);
Let me know how well this worked for you http://jsfiddle.net/bPCFj/
This way seems more elegant (no dependency with $compile) and appropriate to your case :
angular.module('app').directive('myCustomDirective', function () {
return {
restrict: 'A',
scope: {},
template: function(tElem, tAttrs) {
return tAttrs['ng-bind'];
},
link: function (scope, elem) {
scope.content = "Happy!";
}
};
});
jsFiddle : http://jsfiddle.net/hUxp7/8/
From Angular directive documentation :
You can specify template as a string representing the template or as a function which takes two arguments tElement and tAttrs (described in the compile function api below) and returns a string value representing the template.
The source code tells all! Check out the compileNodes() function and its use of collectDirectives().
First, collectDirectives finds all the directives on a single node. After we've collected all the directives on that node, then the directives are applied to the node.
So when your compile function on the bindTest directive executes, the running $compile() is past the point of collecting the directives to compile.
The extra call to $compile in your bindTest directive won't work because you are not linking the directive to the $scope. You don't have access to the $scope in the compile function, but you can use the same strategy in a link function where you do have access to the $scope
You guys were so close.
function MyDirective($compile) {
function compileMyDirective(tElement) {
tElement.attr('ng-bind', 'someScopeProp');
return postLinkMyDirective;
}
function postLinkMyDirective(iScope, iElement, iAttrs) {
if (!('ngBind' in iAttrs)) {
// Before $compile is run below, `ng-bind` is just a DOM attribute
// and thus is not in iAttrs yet.
$compile(iElement)(iScope);
}
}
var defObj = {
compile: compileMyDirective,
scope: {
someScopeProp: '=myDirective'
}
};
return defObj;
}
The result will be:
<ANY my-directive="'hello'" ng-bind="someScopeProp">hello</ANY>