angular material modal and ng-file-upload - javascript

I am working on image upload using angular material, ng-file-upload, and ng-imgcrop-extended. I was previously using all of this on a normal page, and everything was working fine, but requirements have changed and I had to move this logic to a modal.
The way it works is I am using ng-imgcrop to crop the photos, and ng-file-upload does the uploading. So right now, I have an element listening to the file select, and that handles the cropping. Right now however, it is not listening to the file select, and I can only reason that is from the modal.
Here is my code
modal render
$scope.headshotModal = function(ev) {
var useFullScreen;
useFullScreen = ($mdMedia('sm') || $mdMedia('xs')) && $scope.customFullscreen;
$mdDialog.show({
locals: {
p: $scope.persona
},
controller: 'ImagesController',
templateUrl: 'application/views/images/image_modal.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
fullscreen: useFullScreen
}).then((function(answer) {
$scope.status = 'You said the information was "' + answer + '".';
}), function() {
$scope.status = 'You cancelled the dialog.';
});
$scope.$watch((function() {
return $mdMedia('xs') || $mdMedia('sm');
}), function(wantsFullScreen) {
$scope.customFullscreen = wantsFullScreen === true;
});
};
images_controller
angular.module('App').controller('ImagesController', [
'$scope', 'p', '$mdDialog', 'ImageService', '$routeParams', function($scope, p, $mdDialog, ImageService, $routeParams) {
var handleFileSelect;
$scope.persona = p;
$scope.uploadedImg = false;
$scope.myCroppedImage = '';
$scope.myImage = '';
$scope.blockingObject = {
block: true
};
$scope.callTestFuntion = function() {
$scope.blockingObject.render(function(dataURL) {
$scope.showRender = true;
console.log('via render');
console.log(dataURL.length);
});
};
$scope.blockingObject.callback = function(dataURL) {
console.log('via function');
console.log(dataURL.length);
};
handleFileSelect = function(evt) {
var file, reader;
file = evt.currentTarget.files[0];
console.log(file);
$scope.uploadedImg = true;
reader = new FileReader;
reader.onload = function(evt) {
$scope.$apply(function($scope) {
$scope.myImage = evt.target.result;
});
};
reader.readAsDataURL(file);
};
angular.element(document.querySelector('#imgInput')).on('change', function() {
console.log('handlefileselect');
// this function runs the code needed. it is not being triggered
handleFileSelect;
});
$scope.thenRedirect = function() {
return window.location.href = "personas/" + $scope.persona.slug;
};
$scope.testCrop = function(file) {
ImageService.uploadCroppedImg(file, 'headshot', $routeParams, $scope.cropAttributes);
return $scope.thenRedirect();
};
$scope.cancel = function() {
$scope.uploadedImg = false;
return $scope.showRender = false;
};
$scope.hide = function() {
$mdDialog.hide();
};
return $scope.cancelOut = function() {
$mdDialog.cancel();
};
}
]);
modal.slim
md-dialog.fs [style="width: 100%; margin-left:25%; margin-right: 25%;" aria-label=("Image Edit") ng-cloak=""]
/form
md-toolbar.text-center
.md-toolbar-tools
h2 Image Edit
span flex=""
md-button.md-icon-button [ng-click="cancelOut()" aria-label="Cancel"]
i.fa.fa-times
md-dialog-content
.md-dialog-content
h2.text-center Edit Your Headshot
div.input-div
| Select an image file:
input#imgInput [type="file" ngf-select accept="image/*" ng-model="headshotFile"] /
/ [ng-show='uploadedImg']
div
md-button.render-btn[ng-click="callTestFuntion()"] Render
.crop-area
img-crop cropject='cropAttributes' area-type="rectangle" image="myImage" live-view="blockingObject" result-image="myCroppedImage"
a.img-upload [href="#" ngf-select="uploadBanner($file)" ngf-dimensions="$width > 149 && $height > 149"]
i.fa.fa-camera
span Banner
a.img-upload[style='cursor: pointer;'ng-click="testCrop(headshotFile)"]
i.fa.fa-upload
span Upload
a.cancel-img.img-upload [href="#" ng-click="cancel()"]
i.fa.fa-ban
span Cancel
this code works on a normal html page. But the problem seems to be it cannot listen to the angular.element(document.querySelector('#imgInput')).on('change') part of the ImagesController. does anyone know how using a modal, I can handle these types of events? I have seen that I might have to wrap some logic in the $mdDialog.show().resolve() function, but i'm not sure what it's expecting.
Any help would be appreciated :)

Based on your results, I would approach this problem by wiring up the event in the onShowing or onComplete event for the dialog. I would create a callback function here:
$mdDialog.show({
locals: {
p: $scope.persona
},
controller: 'ImagesController',
templateUrl: 'application/views/images/image_modal.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
fullscreen: useFullScreen,
onComplete: wireUpChangeEvent,
onRemoving: removeChangeEvent // so it doesn't get wired up multiple times
})
I'm not 100%, but I think the dialog stays on the DOM after you hide(close) it. If that's the case, you either need the onRemoving event function or a check to see if it's already been wired up to prevent multiple firings. The function would be called from the controller that launches the dialog. You may need to make the two of them share the same scope by using the scope option and telling it to preserveScope. I'm also not sure if the template will be loaded and on the DOM the first time onShowing is called, so it's probably safer to use onComplete.

Related

Focus on an input after creating it with AngularJS

I've been doing a todo list with AngularJS and want to know if there's a way to focus on a input box after creating it by clicking on a button.
Actually, my save function inside the controller is defined like this:
$scope.save = function() {
$scope.objetivo.$save()
.then(function() {
$scope.message = {
text : 'Saved'
};
$scope.objective = new Objective();
})
.catch(function(err) {
console.log(err.data);
if(err.data.code === 11000) {
text = 'This objective is already registered'
}
$scope.message = {
text : text || "Error when saving"
};
});
};
I think there's might be a way by adding the input box and then focusing on it, but I don't know how to do it.
This fiddle shows you how to implement focus elements even on async logics.
View
<div ng-controller="MyCtrl">
<button ng-click="someAsyncFunction()">
Set focus on async functions
</button>
<input type="text"
set-focus="{{setFocus}}">
</div>
AngularJS Application
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope, $timeout) {
$scope.someAsyncFunction = function ()
//the following timeout indicates ansyc functions.
$timeout(function () {
$scope.setFocus = true;
}, 250);
}
});
myApp.directive('setFocus', function($timeout) {
return {
scope: { trigger: '#setFocus' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === "true") {
$timeout(function() {
element[0].focus();
},250);
}
});
}
};
});

Unit test an AngularJS document ready function

I'm writing an AngularJS application and I'm searching for a way to unit test every single aspect.
In this particular case, I need to unit test a custom directive which I've written that represents a control.
The directive can be found here:
var officeButton = angular.module('OfficeButton', []);
officeButton.directive('officeButton', function() {
return {
restrict: 'E',
replace: false,
scope: {
isDefault: '#',
isDisabled: '#',
control: '=',
label: '#'
},
template: '<div class="button-wrapper" data-ng-click="onClick()">' +
'<a href="#" class="button normal-button">' +
'<span>{{label}}</span>' +
'</a>' +
'</div>',
controller: ['$scope', function($scope) {
var event = this;
var api = {
changeLabel: function(label) {
$scope.label = label;
},
enable: function() {
$scope.isDisabled = false;
},
disable: function() {
$scope.isDisabled = true;
},
setAsDefault: function() {
$scope.isDefault = true;
},
removeDefault: function() {
$scope.isDefault = false;
}
};
event.onClick = function() {
if (typeof $scope.control.onClick === 'function') { $scope.control.onClick(); }
};
$.extend($scope.control, api);
function Init() {
if ($scope.isDefault === 'true') { $scope.isDefault = true; }
else { $scope.isDefault = false; }
}
Init();
}],
link: function(scope, element, attributes, controller) {
scope.$watch('isDefault', function(value) {
if (value === 'true' || value) { $('a', element).addClass('button-default'); }
else { $('a', element).removeClass('button-default'); }
});
scope.onClick = function() { controller.onClick(); }
}
}
});
This directive can be called by using the following HTML snippet:
<office-button label="Office Web Controls" control="buttonController"></office-button>
Now, this directive exposes an API which functions such as changeLabel, enable, disable, ....
Now, those functions are not defined on the load of the application, meaning if at the bottom of my HTML I call the following code:
$scope.buttonController.changeLabel('Office Web Controls for Web Applications Directive Demo');
It will throw an error because the changeLabel() method is not defined.
In order to make it function, I need to wrap those calls in an angular.ready function, such as:
angular.element(document).ready(function () {
$scope.buttonController.changeLabel('Office Web Controls for Web Applications Directive Demo');
});
Here's a plunker for your information.
Now, I'm writing unit tests using Jasmine, and here's what I have for the moment:
describe('Office Web Controls for Web Applications - Button Testing.', function() {
// Provides all the required variables to perform Unit Testing against the 'button' directive.
var $scope, element;
var buttonController = {};
// Loads the directive 'OfficeButton' before every test is being executed.
beforeEach(module('OfficeButton'));
// Build the element so that it can be called.
beforeEach(inject(function($rootScope, $compile) {
// Sets the $scope variable so that it can be used in the future.
$scope = $rootScope;
$scope.control = buttonController;
element = angular.element('<office-button control="buttonController"></office-button>');
$compile(element)($scope);
$scope.$digest();
}));
it('Should expose an API with certain functions.', function() {
});
});
Now, in the it function, I would like to test if the $scope.control does expose the API as defined in the directive.
The problem is that the page needs to be ready before the API is available.
Any tought on how to change the code or how to unit test this correctly?
I've found the issue, it was just a wrong configuration on the unit test.
When using this code:
$scope.control = buttonController;
element = angular.element('<office-button control="buttonController"></office-button>');
I must change the element to:
$scope.control = buttonController;
element = angular.element('<office-button control="control"></office-button>');

Having issue related to $scope variable in AngularJS. Value is not changed in UI

The value of image isn't updated in the UI, even though the scope variable is changed.
Main Controller :
$scope.TVuploadModal = function($files) {
$scope.TvTemp.file = $files[0];
$scope.TvTemp.fileName = $files[0].name
var fileReader = new FileReader();
fileReader.readAsDataURL($scope.TvTemp.file);
fileReader.onload = function(e) {
$timeout(function() { $scope.TvTemp.file.dataUrl = e.target.result; });
};
$scope.ResourceOpenBannerImageCrop('lg');
}
$scope.ResourceOpenBannerImageCrop = function(size) {
$modal.open({ templateUrl: 'BannerImageCrop.html', controller: 'resourceBannerImageCropCtrl', backdrop: 'static', size: size, scope: $scope,
resolve: {
TVImages: function () {
return $scope.TVImages;
}
}
}).result.then(function(images) {
console.log("Hello.....");
console.log($scope.TVImages);
/*$scope.TVImages = images;*/ });
};
resourceBannerImageCropCtrl :
app.controller('resourceBannerImageCropCtrl', function($scope,$rootScope,TVImages,$routeParams, $modalInstance, $location, $upload,ResourceAdditionStepsUpdateService,mediaUploadService) {
$scope.myCroppedImage = '';
$scope.TVImages1 = TVImages;
$scope.showSpinner = false;
$scope.imageCrop = function() {
$scope.showSpinner = true;
var requestMap = { imagedata:$scope.myCroppedImage, fileName:$scope.TvTemp.fileName, fileType:"Resource Images", userId: $rootScope.currentLoggedInUserId }
var saveResourceImages = mediaUploadService.saveImagesToUserBucket(requestMap, function(response) {
$scope.showSpinner = false;
if (response.isSuccess) {
var image = {}
image.uniqueKey = response.data.uniqueKey
image.fileUrl = response.data.url
image.fileType = "Resource Images"
image.description = $scope.description
image.type = "IMAGE"
image.s3UploadStatus = response.data.s3UploadStatus
ResourceAdditionStepsUpdateService.saveResourceImages({resourceId : $scope.resourceMetadata.resourceData.id , image : image},function(response){
console.log(response.data.mediaList);
$scope.showSpinner = false;
if(response.isSuccess){
$scope.TVImages1 = [];
$scope.TVImages1 = response.data.mediaList;
if (!$scope.$$phase) {
$scope.$apply();
}
}
});
$modalInstance.close(/*$scope.volcareTVImages*/);
}
})
};
$scope.cancel = function() { $modalInstance.dismiss('cancel'); };
});
HTML:
<div class="col-sm-4 mb20" ng-repeat="image in TVImages">
//content
</div>
I am passing the scope variable (i.e $scope.TVImages) in resolve as you can see in the code I am updating the $scope.TVImages1.
As per my understanding of two way data binding, if $scope.TVImages1 is updated, then $scope.TVImages should also update via the resolve.
I also tried using $scope.$apply, but that didn't work.
What am I doing wrong?
Qasim,
Some days ago, I was also facing this kind of issue while updating the value of $scope variable which is assigned as ng-repeat variable in the the view.
Could you please try to use following line instead of $scope.TVImages
$rootScope.TVImages that will update the view variable.
Please let me know if you still facing some problem.

Why is Custom Directive conflicting with ui-sref?

Here is the directive:
directive('cgHasPermissions', ['$animate', '$rootScope', 'PermissionService', 'PERMISSION', '$compile', function ($animate, $rootScope, PermissionService, PERMISSION, $compile) {
return {
multiElement: true,
transclude: 'element',
restrict: 'A',
$$tlb: true,
link: function ($scope, $element, $attr, ctrl, $transclude) {
var block, childScope, previousElements;
var unregister = $scope.$watch('user', function (newValue, oldValue) {
var value = $attr.cgHasPermissions;
var needsPermissions = value || ""
if($rootScope.user && $rootScope.permissions) {
unregister()
}
needsPermissions = value.replace(/\s+/g,"").split(",");
needsPermissions = _.map(needsPermissions, function(perm){
return PERMISSION[perm];
})
var user = $rootScope.user;
if(!needsPermissions || PermissionService.hasPermissions(needsPermissions)){
if (!childScope) {
$transclude (function (clone, newScope) {
childScope = newScope;
clone [clone.length++] = document.createComment (' end cgHasPermissions: ' + $attr.cgHasPermissions + ' ');
block = {
clone: clone
}
$animate.enter(clone, $element.parent (), $element);
});
}
} else {
if (previousElements) {
previousElements.remove();
previousElements = null;
}
if (childScope) {
childScope.$destroy();
childScope = null;
}
if (block) {
previousElements = getBlockNodes (block.clone);
$animate.leave (previousElements).then(function () {
previousElements = null;
});
block = null;
}
}
})
}
};
}])
Here is the HTML
<a ui-sref="addStudentForm" cg-has-permissions="canAddNewStudent,canViewStudent">+</a>
There is no problem in the logic of how the permissions work. I have verified that. whenever I remove the directive form HTML, ui-sref works just fine but when I add my directive, the ui-sref probably doesn't get executed and href attributed is not added at all.
I tried an ng-click, which also doesn't work.
What is in this directive that is not letting other directives get executed ?
What I have done is, take the source code of ng-if and create my own directive with an extra condition of permission check but ng-if does work with other elements what's wrong here ?

Broadcasting functions through a service trigger the function multiple times instead of one

I created a service that shares a confirm modal method, and allow to broadcast methods between controillers.
services.SharedService = function($rootScope, $modal) {
var sharedService = {
controller : '',
method : '',
args : []
};
sharedService.prepare = function(controller, method){
this.controller = controller;
this.method = method;
this.broadCast();
};
sharedService.broadCast = function(){
$rootScope.$broadcast('menuBroadcast');
};
return sharedService;
});
Then I have three controllers :
controllers.ctrl1 = function($scope, $rootScope, SharedService) {
$rootScope.$on('menuBroadcast', function() {
if (SharedService.controller == 'ctrl1') {
$scope[SharedService.method]();
}
});
$scope.delete = function(){
var c = window.confirm("Are you sure you want to delete it?");
if(c === true){
//delete
}
};
};
and
controllers.ctrl2 = function($scope, $rootScope, SharedService) {
$rootScope.$on('menuBroadcast', function() {
if (SharedService.controller == 'ctrl1') {
$scope[SharedService.method]();
}
});
$scope.delete = function(){
var c = window.confirm("Are you sure you want to delete it?");
if(c === true){
//delete
}
};
};
};
controllers.menu = function($scope, SharedService) {
$scope.delete1 = function() {
console.debug("Calling delete 1");
SharedService.prepare('ctrl1', 'delete');
};
$scope.delete2 = function() {
console.debug("Calling delete 2");
SharedService.prepare('ctrl2', 'delete');
};
}
The first time I open the confirm from ctrl1, clicking on the ok button works as expected. I can open this modal as many times, it will work.
Then, switching to ctrl2, I open the confirm , I have to click two times on the ok button to be able to close the confirm box.
The console debugs shows that the "calling delete1" and "calling delete2" are triggered only once. But the console.debug from on("menuBroadcast") is triggered sometimes up to 3 times. Any idea why the service triggers the event more than one time? When injected, is it instantiated more than once?
Duplicate of
How can I unregister a broadcast event to rootscope in AngularJS?
The controller is instantiated more than once . I have to un-register the $rootScope.$on listener on when the $destroy is invoked.
var cleanUpFunc = $rootScope.$on('menuBroadcast', function {
if (SharedService.controller == 'ctrl1') {
$scope[SharedService.method]();
}
});
$scope.$on('$destroy', function() {
cleanUpFunc();
});

Categories