Function passed to directive doesn't update variables from calling controller - javascript

I have a authentication directive that looks like this:
function loginModal(jwtHelper, responseStringProvider, loadingScreen, $timeout, $http, $route) {
return {
restrict: 'E',
scope: {
onLogin: '=method',
control: '='
},
link: linkFunc,
templateUrl: 'partials/loginModal.html',
controller: loginCtrl,
controllerAs: 'loginVm',
bindToController: true
};
function linkFunc(scope, elm, attrs) {
if (scope.loginVm.onLogin) {
scope.loginVm.onLogin();
}
scope.loginVm.externalControl = scope.loginVm.control || {};
scope.loginVm.externalControl.authenticate = scope.loginVm.authenticate;
}
function loginCtrl($scope) {
//viewmodel
var vm = this;
//bound functions
vm.authenticate = authenticate;
function authenticate(functionToRunOnLogin) {
functionToRunOnLogin(sessionStorage.token, sessionStorage.username).then(function (functionResponse) {
loadingScreen.hide();
$scope.$apply();
}, function (functionResponse) {
if (functionResponse.status === 401) {
loadingScreen.hide();
$('#invalidOperationDialog').modal({ backdrop: 'static' });
}
});
}
There is a single instance of this directive in the index page. The controller for this directive is stored in a $scope variable
index.html:
<login-modal control="vm.authenticationControl"></login-modal>
indexController.js:
vm.authenticationControl = {};
$scope.loginDirective = vm.authenticationControl;
To run a function that needs authentication another controller accesses the directive's authenticate function from the $scope variable and passes the function that needs the authentication
settingsController.js
function openAuthDialog(value) {
settingsVm.currentAction = value;
$scope.loginDirective.authenticate(authorize);
};
function authorize(token, username) {
switch (settingsVm.currentAction) {
case settingsVm.globalSettingAction:
if (_isUpdated) {
return settingsVm.saveGlobalSettings(token, username);
}
break;
case settingsVm.edsAddEditAction:
return settingsVm.edsAddEditConfirm(token, username);
break;
case settingsVm.edsDeleteAction:
return settingsVm.edsDeleteConfirm(token, username);
break;
case settingsVm.updateYellowPod:
case settingsVm.updateBluePod:
case settingsVm.updateDrillThrough:
return updateImagePath(token, username);
break;
}
}
function saveGlobalSettings(token, username) {
settingsVm.isSaving = true;
var globalSettings = getCurrentGlobalSettings();
return GlobalSettings.save(globalSettings, username, token)
.then(function () {
//success
GlobalSettings.setGlobalSettingsChanges(globalSettings);
_savedGlobalSettings = globalSettings;
_isUpdated = false;
openDialogModal('Global Settings have been updated successfully.');
},
function () {
//error
openDialogModal( 'There was an error updating Global Settings at this time. Please try again later.');
});
}
function openDialogModal( message) {
settingsVm.modalMessage = message;
$('#dialogModal').modal({ backdrop: 'static' });
}
settings.html:
<div id="dialogModal" class="modal fade" data-keyboard="false" style="z-index: 30000">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
Update Result
</div>
<div class="modal-body">
<h4>
<span ng-bind-html="settingsVm.modalMessage"></span>
</h4>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="globalSettingsVm.closeDialogModal()">OK</button>
</div>
</div>
</div>
</div>
Most of the functions that are being passed to the directive simply need a token to make a call to the back end. The directive provides that token and runs the function and the back end updates the database.
But there are a couple that need to make front end changes, which is where the problems arises.
After successfully saving the settings, the settingsVm.saveSettings function tries to set settingsVm.modalMessage, but there is no change to the page.
I have stepped through and can see the settingsVm.modalMessage get updated, so I know the function is running properly but cannot figure out why it's not reflected on the page. My suspicion is that the scope of the directive is not set up properly so when settingsVm.modalMessage gets updated it is not in the parent scope which means that instance of settingsVm.modalMessage is not bound to the html. My other thought is that I am not using $scope.$apply properly
I have tried many different scope setups for the directive but nothing works. Any advice would be appreciated

Related

Access to method in the same .factory in Angular.js

I run the factory, and excecute the function "fn_generarPopupConfirm()" in any controller.
in this method, I have now created a template. This template has a buton that has an ng-click, which calls an existing function inside the same factory. In my example I have this:
<button type="submit" class="btn btn-primary" ng-click="fn_confirmar()">
How can I do it to call it ("oElim.fn_confirmar()")?, without needing to define a function in which I define a $scope object, to call the function needed. this function is present in the same factory.
controller: function($scope){
$scope.fn_confirmar=function(){
oElim.fn_confirmar();
}
},
I need the function to be called directly "oElim.fn_confirmar()" with the ng-click event. it's possible?
this is my factory.
.factory('eliminar', function($state,$rootScope,$uibModal,popup_generico) {
var oElim= {};
oElim.fn_generarPopupConfirm = function(objeto,array,titulo,contenido) {
$rootScope.modalInstances.push($uibModal.open({
template: '<form id="form_popup" class="form-horizontal"><div class="modal-header">
<h3 class="modal-title"><button type="submit" class="btn btn-primary"
ng-click="fn_confirmar()">
OK</button></div></form>',
controller: function($scope){
$scope.fn_confirmar=function(){
oElim.fn_confirmar();
}
},
backdrop: 'static'
}));
}
oElim.fn_confirmar = function(){
var index = oElim.array.indexOf(oElim.objeto);
oElim.array.splice(index, 1);
popup_generico.fn_CerrarModal();
}
return oElim;
})
I do not believe that this is possible within angular, as the template is a string that will be interpolated within the context of the controller, not the factory.
If you really want to have scope access to the oElim factory without injecting it into a controller, you could bind the oElim object directly to the $rootScope, giving you prototypal access to its methods within the template "$rootScope.oElim.fn_confirmar()" or equivalently just "oElim.fn_confirmar()" from any template you define in your angular app.
As I know it's not possible, but you can do the following:
controller: function($scope){
$scope.fn_confirmar = oElim.fn_confirmar;
},
or add the object to $rootScope
.factory('eliminar', function($state,$rootScope,$uibModal,popup_generico) {
var oElim= {};
$rootScope.oElim = oElim;
oElim.fn_generarPopupConfirm = function(objeto,array,titulo,contenido) {
$rootScope.modalInstances.push($uibModal.open({
template: '<form id="form_popup" class="form-horizontal"><div class="modal-header">
<h3 class="modal-title"><button type="submit" class="btn btn-primary"
ng-click="oElim.fn_confirmar()">
OK</button></div></form>',
controller: Function.prototype, //Just dummy function
backdrop: 'static'
}));
}
oElim.fn_confirmar = function(){
var index = oElim.array.indexOf(oElim.objeto);
oElim.array.splice(index, 1);
popup_generico.fn_CerrarModal();
}
return oElim;
})
or better, just used built-in angular event service
.factory('eliminar', function($state,$rootScope,$uibModal,popup_generico) {
var oElim= {};
oElim.fn_generarPopupConfirm = function(objeto,array,titulo,contenido) {
$rootScope.modalInstances.push($uibModal.open({
template: '<form id="form_popup" class="form-horizontal"><div class="modal-header">
<h3 class="modal-title"><button type="submit" class="btn btn-primary"
ng-click="$emit('fn_confirmar')">
OK</button></div></form>',
controller: Function.prototype, //Just dummy function
backdrop: 'static'
}));
}
oElim.fn_confirmar = function(){
var index = oElim.array.indexOf(oElim.objeto);
oElim.array.splice(index, 1);
popup_generico.fn_CerrarModal();
}
$rootScope.$on('fn_confirmar', oElim.fn_confirmar);
return oElim;
})

Angular-ui-bootstrap modal not showing data passed to it

I am trying to pass some model data into a modal window when it opens. Its in a $http.post success and also in failure then() with some different titles and button texts. I pass multiple data to it like:
//.then(){...//same as below }
,function (response){
$scope.PostRespData = response.data.message || 'Rq Failed';
$scope.pn = $scope.PostRespData.substring(53,63);
//(status=400) response modal string
$scope.name = 'Hey there!';
//modal options
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl : 'alreadyexists.html',
controller : ModalInstanceCtrl,
resolve: {}
};
$scope.opts.resolve.item = function() {
return angular.copy({name:$scope.name, errmsg:$scope.PostRespData, num:$scope.pn}); // pass name to modal dialo
}
//open the modal and create a modal Isntance
var modalInstance = $uibModal.open($scope.opts);
var ModalInstanceCtrl = function($scope, $uibModalInstance, $uibModal, item) {
$scope.item = item;
$scope.ok = function () {
$uibModalInstance.close();
};
$scope.cancel = function () {
$uibModalInstance.close();
}
My HTML template looks like
<div class="modal-header">
<h1>{{item.name}}</h1>
</div>
<div ng-controller="Nav" class="modal-body">
<p>{{item.errmsg}}
View {{item.num}}
</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">Edit Current Details</button>
<button class="btn btn-warning" ng-click="cancel()">Edit {{item.num}}</button>
</div>
I get my modal and I also get the static data but data being passed to it does not reflect. I've spent the whole day trying to make this work.
Edit: Adding current output pic.
Please help me!
As pointed out by #charlietfl, it was a hoisting issue. To use the above code, move var ModalInstanceCtrl = func.. outside the button's ng-click call of $http.

How to pass data from controller to directive from ajax call AngularJS

I need to send data to directive when call is successful... Here is my ajax call from my controller:
$scope.items ={
avatar: ""
};
$scope.addComment = function(segment) {
commentFactory.saveComment($scope.form.comment,segment,0,0)
.success(function(data){
$scope.items.avatar = data.avatar;
})
.error(function(data){
console.log(data);
});
// Reset the form once values have been consumed.
$scope.form.comment = "";
};
And here is 2 directive first use to submit form and ajax req, second use to update content on client side. I need in second directive to load content form ajax... Problem now is directive not wait for ajax to finish call...
.directive("addcomment", function(){
return {
restrict: "E",
template: '<input type="submit" addcomments class="btn btn-default pull-right" value="Send" />'
};
})
.directive("addcomments", function($compile){
return {
link: function (scope, element, attrs) {
var html = '<div>'+scope.items.avatar+'</div>';
element.bind("click", function(){
angular.element(document.getElementById('space-for-new-comment'))
.append($compile(html)(scope));
})
}
};
});
Any solution for this?
I just want to show you another way of writing this:
You want to put some comments, ok in html:
<div class="smartdivforcomments">
<div ng-repeat="comment in newComments">
{{comment.avatar}}
</div>
</div>
In controller: $scope.newComments = [];
Function for adding comments:
commentFactory.saveComment($scope.form.comment,segment,0,0)
.success(function(data){
$scope.newComments.push({avatar : data.avatar});
})
.error(function(data){
console.log(data);
});
Answer to your comment to previous question: You bind to click event that is not angular, so you need to use scope.apply to correctly update your view.
Use a watch in the addcomments directive and wait for the controller scope variable items.avatar to be defined.
.directive("addcomments", function($compile){
return {
link: function (scope, element, attrs) {
scope.$watch('items.avatar', function(newVal, oldVal) {
// wait for async to finish
if(scope.items.avatar === undefined) return;
// loaded, do work now
var html = '<div>'+scope.items.avatar+'</div>';
element.bind("click", function() {
angular.element(document.getElementById('space-for-new-comment'))
.append($compile(html)(scope));
});
});
}
};
});

Why is my angular binding read only in a bootstrap UI modal in an MVC5 project?

I am trying to pop up a modal to get some input, but the angular binding via ng-model seems to be read only. This is my modal markup:
<script type="text/ng-template" id="signatureWindow.html">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Signature Capture</h4>
</div>
<input type="text" width="100px" ng-model="theName" />
<div class="modal-footer">
<button ng-click="accept()" class="btn btn-primary">Accept</button>
<button ng-click="cancel()" class="btn btn-default">Cancel</button>
</div>
</script>
Then, I invoke this modal as follows:
$scope.getSignatureModal = function(signatureBase64) {
var modalInstance = $modal.open({
templateUrl: 'signatureWindow.html',
controller: 'SignatureModalController',
size: 'lg',
resolve: {
signatureData: function() {
return signatureBase64;
}
}
});
modalInstance.result.then(function(signatureData) {
alert('Signed');
signatureBase64 = signatureData;
}, function() {
alert('Canceled');
});
};
And the following controller code for the modal:
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, signatureData) {
$scope.base64 = signatureData;
$scope.thename = "NamesRus";
$scope.accept = function() {
debugger;
$modalInstance.close($scope.thename);
}
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
}
});
The modal pops up as expected, and the input has the initial value "NamesRus", but when I close the modal, invoking accept in the modal controller, $scope.thename still has it's initial value, not any new value I type when the modal is active. What could be wrong here?
NOTE: At the debugger breakpoint in accept, no matter what I type in the modal's input, theName still has the initial assigned value.
MORE: My Plunker for this works fine. It's when in-place, in an ASP.NET MVC5 project, that I get the strange behaviour.
I think that you mix up two differents scopes.
If you want several variables to be passed to and retrieved from the modal you have to mention them:
in the resolve attribute
resolve: {modalData: function (){return {signatureData:$scope.base64,name:$scope.theName}}}
pass modalData as dependencie to your controller
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, modalData)
update the modal controller $scope with modalData
$scope.signatureData = modalData.signatureData;
$scope.name=modalData.name;
invoke
$modalInstance.close({signatureData:$scope.base64,name:$scope.theName})
reassign the original $scope with the result of promise
modalInstance.result.then(function (data) {
$scope.base64 = data.signatureData;
$scope.thename=data.name;
}
take a look at this plunker forked from ui-boostrap modal orginal example: http://plnkr.co/edit/whLSYt?p=info

AngularJS : Passing a function to an isolated scope of a directive to be called within its controller?

I'm trying to call a function passed from a controller's scope into a directive via the "&" operation from the directive's controller. That method, however, is claimed by Angular to be undefined. After reading my code over and over, scouring the internet, and then repeating that process, I've decided to turn to help here.
Here's the relevant part of my controller. It contains the method I pass to my directive.
angular.module('myApp.controllers', []).controller('PostCtrl', ['$scope', 'postalService', function($scope, postalService) {
$scope.posts = [];
$scope.getPosts = function() {
postalService.getPosts(function(err, posts) {
if(err);
else $scope.posts = posts;
});
};
}]);
Here's my directive. I am unable to invoke onPost.
angular.module('myApp.directives', []).directive('compose', ['postalService', function(postalService) {
return {
restrict: 'E',
transclude: false,
replace: true,
scope: {
onPost: "&" //why will it not
},
templateUrl: "partials/components/compose-partial.html",
controller: function($scope, postalService) {
$scope.title = "";
$scope.content = "";
$scope.newPost = function() {
postalService.newPost($scope.title, $scope.content, function(err) {
if(err) console.log(err + ":(");
else {
console.log("Success getting posts.");
//why can I not invoke onPost()??
$scope.onPost();
}
});
};
},
};
}]);
And here's the relevant part of my html
<div ng-controller="PostCtrl">
<section class="side-bar panel hide-for-small">
<compose onPost="getPosts()"></compose>
</section>
<!--more, non-relevant html here-->
</div>
I know the problem is not with my postalService Service. Instead, the directive reports that no function is passed to it. Why??
Replace
<compose onPost="getPosts()"></compose>
with
<compose on-post="getPosts()"></compose>
and it'll work.
The Angular docs say why it's so:
Directives have camel cased names such as ngBind. The directive can be
invoked by translating the camel case name into snake case with these
special characters :, -, or _.

Categories

Resources