Directive isn't properly updating controller variable - angularjs

I'm fairly new to Angular, and I'm trying to update one of my controller's variables from a directive, but it won't seem to work. The variable is populated in the directive, and when I log it there it's fine. Then when I try to log the variable from the controller; it logs fine at first, then it's "undefined".
I have no idea why this is happening. Help :(
Directive
app.directive('reportFilter', function() {
return {
restrict: 'E',
scope: {},
controller: 'MainController',
templateUrl: 'js/directives/reportFilter.html',
link: function(scope, element, attrs) {
scope.updateTable = function(selectedOffer,startDate,endDate) {
var offer='offer:'+selectedOffer.offer_id;
$.ajax({
type: 'POST',
url: 'daily_summary.php',
data: {
'offer': selectedOffer.offer_id
},
dataType: 'json',
cache: false,
success: function(response) {
scope.myvariable=response;
scope.getData();
console.log(scope.myvariable);
scope.$apply();
}
});
}
}
}
});
Controller
app.controller('MainController', ['$scope', 'service', 'Offerservice','$attrs', function($scope, service,Offerservice) {
$scope.getData= function () {
console.log($scope.myvariable);
return $scope.myvariable;
};
}]);

Related

AngularJs custom directive with $http

I'm sorry but I got some troubles with AngularJs.
I have to get an image from S3 bucket and I want to inject it inside directive but I'm lost to how make it.
Here is what I made but, obviously it does not work and I'm pretty sure I'm in the wrong way.
No java code here because I know I properly get the image from S3 bucket.
.directive('logoApp', function(){
return {
restrict: 'A',
template: "<div class='logo' style='background-image:
url(data:image/png;base64,<image I want to inject>);'></div>",
scope: {
logoApp: "="
}
}
})
.service('StartService', function($scope, $http) {
$scope.logo = {
getFileFromS3 : function () {
$http({
method: 'GET',
url: '/**/{[path:[^\\.]*}'
}).then(function(data) {
console.log(data);
$rootScope.err({
title: 'Internal erro',
code: '500',
message: 'dialog_confirm_download_error'
});
});
}
}
})
Thank you
EDITED
I tried something like this and replace .service by .factory
.factory('StartService', function($http, $rootScope) {
return {
getFileFromS3: function () {
return $http({
method: 'GET',
url: '/<something>'
}).then(function (data) {
console.log("data" + JSON.stringify(data.data));
$rootScope.err({
title: 'Erreur Interne',
code: '500',
message: 'dialog_confirm_download_error'
});
});
}
}
})
And I can see I get my image in "data.data" but unfortunately, this part doesn't display my logo and let '{}' empty
.directive('logoApp', ['StartService', function(startService){
return {
restrict: 'E',
replace: true,
template: "<p>{{ logo }}</p>",
controller: function($scope) {
},
link: function($scope) {
console.log('start service ' + JSON.stringify(startService.getFileFromS3()));
$scope.logo = startService.getFileFromS3();
}
}
I believe this may be what you're looking for. I have changed the restrict to 'E' so that it is an element level directive and also added replace=true which will replace your directive element with the template from the directive.
Using the directive in your html would be like so:
<logo-app></logo-app>
Hope this helps.
.directive('logoApp', function(){
return {
restrict: 'E',
replace: true,
template: "<div class='logo' style='background-image:
url(data:image/png;base64,{{vm.imageData}});'></div>",
controller: ['StartService', function(StartService) {
var vm = this;
vm.imageData;
StartService.getFileFromS3()
.then(result => vm.imageData = result)
.catch(error => console.error(error));
}],
controllerAs: 'vm'
}
})
Please check the plunkr example for a live example using the code above.

AngularJS show result of HTTP request inside directive in template

I'm trying to make GET request the first time I load an AngularJS (1.6) directive and show its result in the directive's template.
What I am trying so far is the following:
JS:
app.directive("myDirective", ['$http', function($http) {
return {
restrict: 'AE',
attrs: {
foo : String,
onResponse : function(response){
this.foo = response.data;
}
},
templateUrl: 'templates/my-directive.html',
compile: function(scope, element, attrs) {
$http({
method: 'GET',
url: 'my/url'
}).then(
attrs.onResponse
);
}
}
}]);
the HTML template:
<div id="my-directive">
foo: <span>attrs.foo</span>
</div>
Is there a way to do this properly?
Thanks in advance!
Well, I managed to do what I wanted by adding a controller:
app.directive("myDirective", ['$http', function($http) {
return {
restrict: 'AE',
controller: "myDirectiveController",
templateUrl: 'templates/my-directive.html'
}
}]);
where
app.controller('myDirectiveController', function ($scope, $http) {
$scope.foo;
$scope.onResponse = function(response){
$scope.foo= response.data;
}
$http({
method: 'GET',
url: 'my/url'
}).then(
$scope.onResponse
);
});
and the template looks like this:
<div id="my-directive">
foo: <span>{{foo}}</span>
</div>
Probably this is not the proper way to do it but it works.. Any suggestions are welcome!

Injecting resolve into directive with dynamic controller

I have a directive with dynamic controller which is passed via controller-name property.
Directive:
angular
.module('directives.panel', [])
.directive('panel', panel);
panel.$inject = ['$timeout', '$parse'];
function panel($timeout, $parse) {
var directive = {
restrict: 'EA',
transclude: true,
replace: true,
template: '<div class="panel panel-solid panel-table" ng-transclude></div>',
controller: '#',
name: 'controllerName',
controllerAs: 'panel',
link: linkFunc
};
return directive;
}
}
Is it possible to inject resolve 'taskbook' object into dynamic controller?
When I try to do that I get unknown provider. However injecting Resource service (GroupResource) works fine.
Is it possible to inject resolve in directive controller?
State
angular
.module('taskbooks.taskbook', [
'deployment.group',
'resource.deployment',
'resource.taskbook'
])
.config(TaskbookConfig)
.controller('TaskbookController', TaskbookController);
TaskbookConfig.$inject = ['$stateProvider', '$provide'];
function TaskbookConfig($stateProvider) {
$stateProvider
.state('taskbooks.taskbook', {
url: 'taskbooks/:taskbookId',
parent: 'taskbooks',
views: {
"mainContent#taskbooks": {
controller: 'TaskbookController as taskbook',
templateUrl: 'taskbook/taskbook.tpl.html'
}
},
resolve: {
taskbook: TaskbookPrepare
}
});
}
TaskbookPrepare.$inject = ['$stateParams', 'TaskbookResource'];
function TaskbookPrepare($stateParams, TaskbookResource) {
return TaskbookResource.get({
taskbookId: $stateParams.taskbookId
}).$promise;
}
Directive Controller
angular
.module('deployment.groups', ['resource.group'])
.controller('DeploymentGroupController',DeploymentGroupController);
DeploymentGroupController.$inject = ['$scope', '$element', '$attrs', 'GroupResource', 'taskbook'];
function DeploymentGroupController($scope, $element, $attrs, GroupResource, taskbook) {
}
Sort of... If this directive requires the resolve that is associated with this state, then it's safe to assume it will only ever be used in this state, correct?
Going on that idea, the directive can reference the controller set in the state and you can add the resolve to the controller scope.
Here's a very simplified version of what I'm saying...
angular
.module('taskbooks.taskbook')
.config([ $stateProvider, function ($stateProvider) {
$stateProvider
.state('taskbook', {
url: '/:id',
parent: 'taskbooks',
resolve: {
taskbook: [ '$stateParams', 'TaskbookResource', function ($stateParams, TaskbookResource) {
return TaskbookResource.get({
taskbookId: $stateParams.taskbookId
}).$promise;
}]
},
controller: ['taskbook', function (taskbook) {
this.taskbook = taskbook;
}],
controllerAs: 'taskController'
});
}])
.directive('someDirective', function() {
return {
restrict: 'EAC',
controller: 'taskController',
link: function (scope, el, attr, ctrl) {
var taskbook = ctrl.taskbook;
}
}
});
Do note, I removed a lot of the non-relevant code from yours just to get the point across quicker. Obviously this can be reworked into the structure you've written.

How can I call link directive function after controller code has been run

I'm trying to render code in the link function after the controller resolves the http call but the link function is called before that. How can I call link after $scope.menuHtml has been set?
HTML:
<div id="testD" nav-menu-output="parseMenuJsonAndOutPutMenu()"></div>
DIRECTIVE:
return {
restrict: 'A',
controller: ['$scope', '$q','$http', function ($scope, $q,$http) {
$http.get('ajax/menu' ).then(function (data) {
$scope.menuHtml = generateHtmlMenu(data);
});
}],
link: function(scope, element, attr) {
var templateString = scope.menuHtml;
var compiledTemplate = $compile(templateString)(scope);
compiledTemplate.appendTo("#testD");
}
}
I would suggest using scope.$watch() and rerunning your compiled template code on that. This way you can make as many requests to the menu endpoint as you want and your template will be recompiled.
Here's more information about watch:
http://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch
Here's an updated version which should work properly:
return {
restrict: 'A',
controller: ['$scope', '$q','$http', function ($scope, $q,$http) {
$http.get('ajax/menu' ).then(function (data) {
$scope.menuHtml = generateHtmlMenu(data);
});
}],
link: function(scope, element, attr) {
scope.$watch('menuHtml', function() {
var templateString = scope.menuHtml;
var compiledTemplate = $compile(templateString)(scope);
compiledTemplate.appendTo("#testD");
});
}
}
You can do it by using async: false
Please Try this code instead of your code,
return {
restrict: 'A',
controller: ['$scope', '$q','$http', function ($scope, $q,$http) {
$http({
method: 'GET',
url: 'ajax/menu',
async: false
}).success(function (data) {
$scope.menuHtml = generateHtmlMenu(data);
})}],
link: function(scope, element, attr) {
var templateString = scope.menuHtml;
var compiledTemplate = $compile(templateString)(scope);
compiledTemplate.appendTo("#testD");
}
}

How to reference the $scope in which a directive is applied?

I have a custom directive that uploads a file to amazon and contains a callback(onComplete).
When the callback is complete, I would like to attach a value to the $scope of the controller in which the directive is created. In this case, the scope of Invite.
Both Invite and fineUploader extend the same angular module.
HTML(simplified):
<div ng-controller="Invite" class="apply">
<div fine-uploader ng-switch-when="file" upload-extensions="jpg,jpeg,png,gif"></div>
</div>
Directive:
directive('fineUploader', function() {
return {
restrict: 'A',
require: '?ngModel',
link: function($scope, element, attributes, ngModel) {
$scope.uploader = new qq.s3.FineUploader({
debug: true,
element: element[0],
request: {
endpoint: 'ballentines-bar-project.s3.amazonaws.com',
accessKey: 'AKIAIPT6J4T6XZXV3VWA'
},callbacks: {
onComplete: function(id, fileName, responseJSON) {
if (responseJSON.success === true) {
console.log(this.getKey(id));
console.log($scope);
$scope.test = this.getKey(id);
}
}
},
signature: {
endpoint: '/s3/'
},
iframeSupport: {
localBlankPagePath: '/success.html'
},
retry: {
enableAuto: true // defaults to false
},
deleteFile: {
enabled: false
},
text: {
uploadButton: '<p>Upload File</p>'
},
template:
'<div class="qq-uploader">' +
'<div class="qq-upload-button btn btn-info">{uploadButtonText}</div>' +
'<ul class="qq-upload-list" ><h2>Your files</h2></ul>' +
'</div>',
});
}
};
}).
Controller
controller('Invite', function(
$scope,
$localStorage,
$http
){
$scope.$storage = $localStorage.$default({
"formkey": "1MRSAWTRl5-PnVEoy3tD63BL3q_v2mnAhtqa9bdZk-zg",
"draftResponse": "[]",
"pageHistory": "0",
});
$scope.liking = liking;
$scope.post = function(){
$http.post('/signup.php', $scope.$storage).
success(function(data, status, headers, config){
console.log(data);
});
};
FB.Event.subscribe('edge.create',
function(href, widget) {
liking = true;
}
);
})
You either need to pass the items from the parent scope to the directive (through isolated scope). Or, do as #MaximShoustin says and remove the isolated scope from your directive.
So, option 1:
scope: { directiveProperty: '=nameOfAttributeThatContainsParentProperty' },
Or, option 2:
Remove the isolated scope declaration scope: {}, from the directive. This will allow the directive to extend the scope of it's containing scope.
I would try at least two options:
[1]
change scope: {}, in directive to:
`scope: { test: '#'},`
This makes the test method visible in the private scope.
[2]
The second option try removing the isolate scope a.e: scope: {},

Resources