I'm trying to pass data from a controller to a directive using $broadcast and $on.
Data only appears on the directive's HTML template when I refresh the page. It does not appear when I route to the controller template with the embedded directive.
The weird thing is, the data appears to have been received when I console log. I have tried using $timeout and angular.element(document).ready
Controller:
$http.getDashboardData(...).success(function(res) {
populateResults(res);
...
}
function populateResults (data) {
$rootScope.safeApply(function () {
$rootScope.$broadcast('show-results', data);
});
}
Directive:
.directive('results',['$rootScope', function ($rootScope) {
return {
restrict: 'AE',
scope: {},
transclude: true,
templateUrl: '/html/directives/results.html',
link: function(scope, elem, attr){
...
$rootScope.$on('show-results', function(event, args) {
angular.element(document).ready(function () {
scope.init(args);
});
});
scope.init = function (args) {
console.log('ARGS', args); //Has data
scope.questions = args;
};
Controller Page with embedded results directive:
<div class="myPage">
<results></results>
</div>
Directive HTML:
<div>
QUESTIONS: {{questions}} : //Empty array
</div>
Console Log: You can see it has data:
Routing sequence:
.config:
...
state('dashboard', {
url : '/dashboard',
templateUrl: '/html/pages/dashboard.html',
controller: 'dashboardCtrl',
resolve : {
ProfileLoaded : function ($rootScope) {
return $rootScope.loadProfile();
}
}
});
.run: This is to load profile if user refreshes the page:
$rootScope.loadProfile = function () {
return Me.getProfile({user_id : Cookies.get('user_id')}).success(function (res) {
$rootScope.me = res;
}).error(function (err) {
console.log('Error: ', err);
});
};
In your directive link function, try to use scope.$on instead of $rootScope.$on, and use scope.init directly without document.ready
Related
I am using UI bootstrap modal dialog box with angular js. Modal is successfully loaded. But when I click YES/NO Button, issued occurred & modal did not close.
Error said, ' $uibModal.close is not a function'.
.directive('confirm', function(ConfirmService) {
return {
restrict: 'A',
scope: {
eventHandler: '&ngClick'
},
link: function(scope, element, attrs){
element.unbind("click");
element.bind("click", function(e) {
ConfirmService.open(attrs.confirm, scope.eventHandler);
});
}
}
})
This is my service
.service('ConfirmService', function($uibModal) {
var service = {};
service.open = function (text, onOk) {
var modalInstance = $uibModal.open({
templateUrl: 'modules/confirmation-box/confirmation-box.html',
controller: 'userListCtrl',
resolve: {
text: function () {
return text;
}
}
});
modalInstance.result.then(function (selectedItem) {
onOk();
}, function () {
});
};
return service;
})
This is my controller file. I am trying to yes/no button inside the controller
.controller('userListCtrl',
['$scope','$http','appConfig','$uibModalInstance', '$uibModal','$log','alertService',
function ($scope,$http, appConfig,$uibModalInstance, $uibModal,$log,alertService) {
$scope.ok = function () {
$uibModalInstance.close();
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}]);
You're attempting to use two usage methods at once time. There are two (probably more) that you can use the $uibModal, but here are the two that I believe you're intermixing:
1) Service controls the modal and returns a promise, I believe this is what I think you're doing. You do not need to call close/dismiss manually in this instance. You can make the following changes:
service.open = function(text, onOK) {
var modalInstance = $uibModal.open({
templateUrl: 'modules/confirmation-box/confirmation-box.html',
controller: 'userListCtrl',
resolve: {
text: function () {
return text;
}
}
});
// Return so you can chain .then just in case. Generally we don't even
// do this, we just return the instance itself and allow the controller to
// decide how to handle results/rejections
return modalInstance.result;
}
In your template file you'd have something like:
<button type="button" ng-click="$close(selectedItem)"></button>
<button type="button" ng-click="$dismiss(readon)"></button>
2) If you want to use the close method directly, then you only need to change the service to:
...
return $uibModal.open({});
then in your controller:
var modal = service.open('confirm');
modal.result.then(...)
modal.close()
Edit - updated with change to op to remove the antipattern as per georgeawg suggestion.
I want to create a table directive, I wanted to know how to pass a callback to for example: "Calling a delete function that is not in the Controller", I was wanting to pass as a callback using parameters, but for some time I am hitting my head with it. When I can return the callback through the "link" I can not return the values, and the controller can not.
Imagem when I use link , but don't back values of callback
Image when I use Controller
View DashBoard
<div crud-table options='vm.options' callback='vm.myCallBack(response)'></div>
Controller DashBoard
(function() {
'use strict';
angular
.module('senhalivre')
.controller('DashboardMikrotikCtrl', DashboardMikrotikCtrl);
DashboardMikrotikCtrl.$inject = [];
function DashboardMikrotikCtrl() {
var vm = this;
vm.myCallBack = myCallBack;
vm.options = {
enabled : true,
msg : 'DashBoard',
// Configs etc.....
}
//////////////////
function myCallBack(response){
console.log('Callback !!! ');
console.log(response);
}
}
})();
Directive
(function() {
'use strict';
angular
.module('senhalivre')
.directive('crudTable', crudTable);
crudTable.$inject = [];
function crudTable() {
var directive = {
bindToController: true,
controller: crudTableCtrl,
templateUrl : './views/crud/crudTable.html',
controllerAs: 'vm',
link: link,
restrict: 'EA',
scope: {
options : '=',
callback : '&callback'
}
};
return directive;
////////////////
function link(scope, element, attrs, ctrl) {
scope.vm.callback({test : 'something'});
// Work
}
}
crudTableCtrl.$inject = [];
function crudTableCtrl() {
var vm = this;
vm.callback({test : 'something'});
// :(
}
})();
You need to provide the property 'response' in the parameter-object when calling the callback:
function link(scope, element, attrs, ctrl) {
scope.vm.callback({response: 'some response text'});
}
or
function crudTableCtrl() {
this.callback({response: 'something'});
}
I created directives for form controls.
There are some controls those will have options from ajax[API] call.
I am stuck here how to make ajax call over directives.
function selectControlDir()
{
return {
transclude: true,
restrict: 'E',
scope: {
data: '=data'
},
template: "<div ng-transclude></div><label>{{data._text}} </label><select type='text' name='{{data._attributeName}}' id='{{data._attributeName}}' >\n\
<option ng-repeat='ans in data._answerOptions'>{{ans._promptText}}</option></select>"
,
link: function (scope, element, attrs)
{
//console.log("scope.data.QuestionData", scope.data.QuestionData);
}
};
}
Plunker for complete code
http://plnkr.co/edit/Op1QDwUBECAosPUC7r3N?p=preview
Here I want to make api call for Year (on page load).
on year change update options of Make.
What you can do for this is better to have a ng-change event directive on the year element and in the controller you can use to have an array which holds all the make happened in that year:
var app = angular.module('yourApp', []);
app.controller('yourController', ['$scope', '$http',
function($scope, $http) {
$scope.o = {
makes: [{make:'Make1'}, {make:'Make2'}], // <---just for demo
getMake: function(year) {
$http.post(url, {
year: year // <----pass the year to backend as year like : { year:1990 }
})
.then(function success(response) {
$scope.o.makes = response.data; // <---put actual data here
},
function error(e) {
console.error(e);
});
}
};
}
]);
<div ng-app='yourApp' ng-controller='yourController'>
<select ng-model='year' ng-change='o.getMake(year)'>
<option>1990</option>
<option>1991</option>
</select>
<select ng-model='make' ng-options='make as make.make for make in o.makes track by make.make'>
<option>Make...</option>
</select>
</div>
You should get the options by angular service. The angular service will send the AJAX call and return the result (and even some default data in case of failure).
How to do it? Here is an example:
var app = angular.module('app',[]);
//Service
app.factory('myService', function(){
function getOption() {
return 'getOption';
}
return {
getOption: getOption
}
});
app.directive('myDirective', ['myService',function(myService){
return {
template: '<h1>{{name}}</h1>'
link: function (scope, element, attrs) {
scope.name = myService.getOption();
}
}
}]);
Use can use $http to make ajax call. You need to inject it to use it. Better solution is to move the service calls to factory/service.
function selectControlDir($http)
{
return {
transclude: true,
restrict: 'E',
scope: {
data: '=data'
},
template: "<div ng-transclude></div><label>{{data._text}} </label
> ><select type='text' name='{{data._attributeName}}' id='{{data._attributeName}}' >\n\ <option ng-repeat='ans in
> data._answerOptions'>{{ans._promptText}}</option></select>"
,
link: function (scope, element, attrs)
{
// $http.post(url, options); => make your call here
//console.log("scope.data.QuestionData", scope.data.QuestionData);
}
};
}
The best way is to create a service or a factory and inject it into your directive.
app.service("serviceName", ["$http", function($http) {
return {
getData: function(configObject) {
// $http.get(..., post... etc
}
}
}]);
You can use it in your link or in controller statement.
selectControlDir(serviceName) {
return{
...
link: function() {
selectControlDir.getData({ url: ..., getParam1: 123})
}
... or ...
controller: function() {
selectControlDir.getData({ url: ..., getParam1: 123})
}
}
}
I am trying to inject services to the below directive which is used link function instead of controller.
(function() {
angular
.module("myApp")
.directive('myDirective',['$scope','myService','myData',function($scope,myService,myData) {
return {
restrict'AE',
replace:'true',
templateUrl :'/myApp/directive/my-directive',
scope: {
userId: "#"
}
},
link:function(scope,elem,attr)
{
//Code for link function goes here ...
scope.myUsersArray [];
scope.getUserDetails = function()
{
//code for getUserdetails goes here...
}
}
}]);
})();
When i run this code, i am getting exception like [$injector:unpr] Unkown Provider <_ $scope error. If i remove the injected services, i am not getting any error. Error throws at angular.js file so i don't have any clue to fix it :(
You have several syntax issues. Your code should look like this:
.directive('myDirective', ['$scope', 'myService', 'myData', function($scope, myService, myData) {
return {
restrict: 'AE',
replace: 'true',
templateUrl: '/myApp/directive/my-directive',
scope: {
userId: "#"
},
link: function(scope, elem, attr) {
//Code for link function goes here ...
scope.myUsersArray = [];
scope.getUserDetails = function() {
//code for getUserdetails goes here...
};
}
};
}]);
You may have more problems as its not evident as to why you are injecting $scope, myService, and myData
I'm building an Angular pop-up system for multiple purposes. The way it works is that I have a directive called bitPopup which three variables get passed on to (type, action and data) as shown below:
index.html
<bit-popup type="popup.type" action="popup.action" data="popup.data"></bit-popup>
popup.js
app.directive('bitPopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
[***]
}
}
The popup controller then loads a different directive based on the type:
popup.html (The HTML template shown above)
<div class="pop-up" ng-class="{visible: visible}" ng-switch="type">
<bit-false-positive-popup ng-switch-when="falsePositive" type="type" action="action" data="data"></bit-false-positive-popup>
</div>
false_positives.js (Containing the bitFalsePositivePopup directive)
[...]
scope: {
type: '=',
action: '=',
data: '='
}
[...]
And then the html template for the bitFalsePositivePopup directive displays some properties from data.
Now the way I'm triggering a pop-up works like this:
From a template inside a directive containing the bitPopup directive i'll change $scope.popup's type, action and data.
I'll do $scope.$broadcast('showPopup');
The bitPopup directive will react because of $scope.$on('showPopup', [...]}); and makes the pop-up visible.
Now this really weird thing occurs where it works on the first try (the pop-up opens with the correct data information), but after the first try it will display the data from the previous try.
Now what's even weirder is that I tried logging the information on the first try and what I found out is that:
$scope.popup at index.html just before calling $scope.$broadcast('showPopup'); displays the right information.
$scope.data at the bitPopup directive displays null
$scope.data at the bitFalsePositivePopup directive displays the right information.
On the second try:
$scope.popup at index.html is correct again.
$scope.data at the bitPopup directive displays the information from the previous attempt
The same holds for the bitFalsePositivePopup directive.
Another weird thing is that when I use $scope.$apply() it does work correctly, only it displays the $apply already in progress error. I know I shouldn't use $scope.$apply() in this case, because it's all Angular events. But how is it possible that the passed scope is always a step behind?
Am I doing something wrong to begin with?
EDIT:
Because of amahfouz's answer I decided to post some more code for clarification. I left out some unimportant details for more clear reading.
index.html
<div class="falsePositives" ng-controller="falsePositives">
<i class="fa fa-minus color-red" ng-click="triggerPopup('falsePositive', 'delete', {detection: getDetection(row.detection, row.source), source: row.source, triggers: row.triggers, hash: row.hash, date: row.date})"></i>
<i class="fa fa-pencil" ng-click="triggerPopup('falsePositive', 'edit', {detection: getDetection(row.detection, row.source), source: row.source, triggers: row.triggers, hash: row.hash, date: row.date})"></i>
<bit-popup type="popup.type" action="popup.action" data="popup.data"></bit-popup>
</div>
index.js
var app = require('ui/modules').get('apps/falsePositives');
app.controller('falsePositives', function ($scope, $http, keyTools, bitbrainTools, stringTools) {
function init() {
$scope.getDetection = getDetection;
$scope.popup = {
type: null,
action: null,
data: null
};
}
function getDetection(hash, source) {
return {
'ids': 'BitSensor/HTTP/CSRF',
'name': 'CSRF Detection',
'description': 'Cross domain POST, usually CSRF attack',
'type': [
'csrf'
],
'severity': 1,
'certainty': 1,
'successful': false,
'input': ['s'],
'errors': []
};
}
$scope.triggerPopup = function (type, action, data) {
$scope.popup = {
type: angular.copy(type),
action: angular.copy(action),
data: angular.copy(data)
};
test();
$scope.$broadcast('showPopup');
};
function test() {
console.log('$scope.popup: ', $scope.popup);
}
}
popup.html
<div class="pop-up-back" ng-click="hidePopup()" ng-class="{visible: visible}"></div>
<div class="pop-up" ng-class="{visible: visible}" ng-switch="type">
<bit-false-positive-popup ng-switch-when="falsePositive" type="type" action="action" data="data"></bit-false-positive-popup>
</div>
popup.js
var app = require('ui/modules').get('apps/bitsensor/popup');
app.directive('bitPopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
controller: function ($scope) {
$scope.visible = false;
$scope.$on('showPopup', function () {
console.log('$scope.data: ', $scope.data);
$scope.visible = true;
});
$scope.$on('hidePopup', function () {
hidePopup();
});
function hidePopup() {
$scope.visible = false;
}
$scope.hidePopup = hidePopup;
}
};
});
false_positives.js
var app = require('ui/modules').get('apps/bitsensor/falsePositives');
app.directive('bitFalsePositivePopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
controller: function ($scope, objectTools, bitbrainTools, keyTools) {
function init() {
console.log('$scope.data # fp: ', $scope.data);
}
function hidePopup() {
$scope.data = null;
$scope.$emit('hidePopup');
}
$scope.$on('showPopup', function () {
init();
});
init();
$scope.hidePopup = hidePopup;
}
}
}
Without the rest of the code I can only guess: You either need to use a promise when displaying the popup or use the $apply service to make the change to the popup visibility.
surround your $broadcast event in $timeout like follow:
$timeout(function() {
$broadcast('eventName');
});
It will wait for $scope update and then will trigger the event.