get source of selected file in ionicframwork - angularjs

I am building a mobile app using ionic. One of the usecase is to let the user browse a file and upload it to the backend server (which exposes a rest service).
On the UI, I am using the html file tag.
<input type="file" ng-select="uploadFile($files)" multiple>
.controller('UploadDocCtrl', function ($scope, $cordovaFileTransfer) {
$scope.uploadFile = function(files) {
console.log("selected file "+files);
// hard coded file path "/android_asset/www/img/ionic.pdf" to be replaced with the user selected file
$cordovaFileTransfer.upload(restServiceEndpoint, "/android_asset/www/img/ionic.pdf", properties).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
}, function (progress) {
// constant progress updates
});
});
The problem is that I am not able to get a reference to the selected file. Can someone please help with the steps to achieve this. Thanks!

Keep file in www folder which has index.html file.
then just give path like "ionic.pdf"
.controller('UploadDocCtrl', function ($scope, $cordovaFileTransfer) {
$scope.uploadFile = function(files) {
console.log("selected file "+files);
// hard coded file path "/android_asset/www/img/ionic.pdf" to be replaced with the user selected file
$cordovaFileTransfer.upload(restServiceEndpoint, "ionic.pdf", properties).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
}, function (progress) {
// constant progress updates
});
});

Related

ngDialog not programmatically closing on IE but works on Chrome

I have a site that allows a person to import some data. They click a button, and the file select opens and they select a file. When they select a file I open a dialog that they can't close that tells them their data is being imported. Once I get the call back from the api call to import the file, I then close that dialog and open a new one that gives the status of the import.
On Chrome the "please wait while importing" dialog closes as expected. On IE it doesn't. If you use IE 11 it should happen in the following fiddle:
http://jsfiddle.net/og6qsxdw/
You see a ghost like outline of the dialog go up and fade away like its trying to close but the dialog and overlay still remain.
<div ng-controller="MyCtrl">
<input type="file" ng-simple-upload web-api-url="http://www.fakeresponse.com/api/?sleep=5" select-fn="importFileSelected" callback-fn="importDataComplete" />
<script type="text/ng-template" id="templateId">
<div>
Getting Data
</div>
</script>
</div>
JavaScript/AngularJS code:
var myApp = angular.module('myApp', ['ngDialog', 'ngSimpleUpload']);
function MyCtrl($scope, $http, ngDialog) {
$scope.importDataComplete = function() {
$scope.dlg.close();
}
$scope.importFileSelected = function() {
$scope.dlg = ngDialog.open({
template: 'templateId',
className: 'ngdialog-theme-default',
closeByDocument: false,
showClose: false
});
}
}
angular.module('ngSimpleUpload', [])
.directive('ngSimpleUpload', [function() {
return {
scope: {
webApiUrl: '#',
callbackFn: '=',
selectFn: '=',
buttonId: '#'
},
link: function(scope, element, attrs) {
// if button id value exists
if (scope.buttonId) {
$('#' + scope.buttonId).on('click', function() {
// retrieves files from file input
var files = element[0].files;
// will not fire until file(s) are selected
if (files.length == 0) {
console.log('No files detected.');
return false;
}
Upload(files);
});
} else {
// original code, trigger upload on change
element.on('change', function(evt) {
var files = evt.__files_ || (evt.target && evt.target.files);
Upload(files);
// removes file(s) from input
$(this).val('');
});
}
function Upload(files) {
var fd = new FormData();
angular.forEach(files, function(v, k) {
fd.append('file', files[k]);
});
// this tell us the user clicked open instead of cancel so we can start our overlay
scope.selectFn();
return $.ajax({
type: 'GET',
url: scope.webApiUrl,
async: true,
cache: false,
contentType: false,
processData: false
}).done(function(d) {
// callback function in the controller
scope.callbackFn(d);
}).fail(function(x) {
console.log(x);
});
}
}
}
}]);
Alright, so here's the deal. In IE, when you open the dialog, two instances are instantiated. When the upload completes, you have a reference to close the most recent dialog, but one existed milliseconds before as well.
I had originally thought at quick glance that this was just an ugly IE bug, and you had to "keep track" of the instances, however, I failed to take note of jQuery's involvment in your link function. Thusly, my initial solution was a hack/workaround, but better can be done.
It seems that the mixture of the two libraries is the culprit, where Angular and jQuery are not communicating properly. I've inserted a reference below to a ticket that discusses jQuery events with Angular.
jQuery and AngularJS: Bind Events to Changing DOM
Solution
My suggestion, as always in these cases, is not to leverage jQuery on top of Angular. It adds an additional layer of confusion, and requires you to be prudent about maintaining proper coupling between the two (in circumstances such as this).
I have provided a solution, where I clean up your link function. It uses a lot of your existing code, but with the absence of the jQuery bits. It seems to work just fine for me in both Chrome and IE now.
http://plnkr.co/edit/6Z4Rzg1Zm3w5rYyqQqmg?p=preview
link: function(scope, element, attrs) {
console.warn("Element is", element);
// original code, trigger upload on change
element.on('change', function(evt) {
var files = evt.__files_ || (evt.target && evt.target.files);
Upload(files);
});
function Upload(files) {
var fd = new FormData();
angular.forEach(files, function(v, k) {
fd.append('file', files[k]);
console.log("File loaded");
});
// this tell us the user clicked open instead of cancel so we can start our overlay
scope.selectFn();
$http({
url: scope.webApiUrl,
method: "GET",
cache: true
}).success(function(d, status, headers, config) {
scope.callbackFn(d);
}).error(function(data, status, headers, config) {
console.warn("Request failed...");
});
}
}

Upload file to server using ionic

I am building a mobile app using ionic. One of the usecase is to let the user browse a file and upload it to the backend server (which exposes a rest service).
On the UI, I am using the html file tag
<input type="file" ng-select="uploadFile($files)" multiple>
This opens a file browser to select a file. Then in the controller, I am doing the following
.controller('UploadDocCtrl', function ($scope, $cordovaFileTransfer) {
$scope.uploadFile = function(files) {
console.log("selected file "+files);
// hard coded file path "/android_asset/www/img/ionic.pdf" to be replaced with the user selected file
$cordovaFileTransfer.upload(restServiceEndpoint, "/android_asset/www/img/ionic.pdf", properties).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
}, function (progress) {
// constant progress updates
});
});
The problem is that I am not able to get a reference to the selected file. Can someone please help with the steps to achieve this. Thanks!

Reload angular-translate-static-loader-files within app, without refresh in Ionic

I'm working on this Ionic app and I'm using angular-translate-loader-static-files with angular-translate to load in a bunch of language .json files.
Everything is working fine, but I'm trying to figure out how to basically "re-run the $translateProvider" so it can reload all the static files all over again as the .json files will get updated from the server periodically. I have yet to figure this out, and even trying to force a "page reload" doesn't cause the static files to reload.
I should note that I'm currently testing this in iOS and I realize that the directory structure will change, based on OS.
Here is my service that utilizes $cordovaFile to overwrite the file with new text. Right now I'm just using a simple json string to make sure I can solve the problem:
(function() {
'use-strict';
angular.module('coursemill.services')
.service('Translations', Translations);
/**
* Service: Check network connection
*/
function Translations($cordovaFile) {
function updateLanguageFile(lang) {
document.addEventListener("deviceready", function() {
$cordovaFile.checkFile(cordova.file.applicationDirectory + "/www/languages/", lang + ".json")
.then(function (success) {
// Update the language file
$cordovaFile.writeFile(cordova.file.applicationDirectory + "/www/languages/", lang + ".json", '{"COURSES_MENU_REQUIRED": "Required"}', true)
.then(function (success) {
// TO-DO: reload translation files
},
function (error) {});
},
function (error) {});
});
}
return {
updateLanguageFile: updateLanguageFile
}
}
})();
Here is a snippet from my .config:
// Setup the language translations
$translateProvider.useStaticFilesLoader({
prefix: 'languages/',
suffix: '.json'
});
Here is a snippet from my controller:
Translations.updateLanguageFile('en_US');
When I open the file up after this function has been run, the contents of the file are replaced and are doing exactly what I want, but I'd like my language variables inside the app to be updated as well, and they aren't.
Any thoughts on what can be done here?
Doink, I needed to use $translate.refresh() in my service function. So now it looks like this:
(function() {
'use-strict';
angular.module('coursemill.services')
.service('Translations', Translations);
function Translations($cordovaFile, $translate) {
function updateLanguageFile(lang) {
document.addEventListener("deviceready", function() {
$cordovaFile.checkFile(cordova.file.applicationDirectory + "/www/languages/", lang + ".json")
.then(function (success) {
// Update the language file
$cordovaFile.writeFile(cordova.file.applicationDirectory + "/www/languages/", lang + ".json", '{"COURSES_MENU_REQUIRED": "Required"}', true)
.then(function (success) {
// Reload translation files
$translate.refresh();
},
function (error) {});
},
function (error) {});
});
}
return {
updateLanguageFile: updateLanguageFile
}
}
})();

Barcode Scanner using Ionic/ngcordova, not getting data

I've got a controller:
.controller('BarCodeScanCtrl', function($scope, $cordovaBarcodeScanner) {
$scope.scanBarcode = function() {
$cordovaBarcodeScanner.scan().then(function(barcodeData) {
// Success! Barcode data is here
console.log(barcodeData.text);
alert('barcode scanned:' + barcodeData);
}, function(error) {
alert('Error');
console.log(error);
// An error occurred
});
};
});
I'm using the QR code generator: http://www.qr-code-generator.com/ but I can't seem to retrieve any of the data I'm inputting. It's returning the barCode object the values of text & format properties are empty. The cancel property is true.
{
"cancelled": true,
"text":"",
"format": ""
}
Any ideas?
Have uploaded a working sample in github, please have a look.
Steps:
1) Create a project
2) cd into the project 3) Add required platforms
4) Add ngcordova-min.js file to the js folder 5) Add the ngcordova
js file path in index.html just before cordova path 6) Add ngcordova
dependancy to app.js 7) Add plugin 8) Add the function in html page
9) Add the logic in controller 10) Test the app
I supposed you are using Ionic. You should use on-touch directive from ionic not ng-click, because somehow when you are using ng-click it triggered the scan view twice and cause a force quit (CMIIW).
use it like this in your views:
<button on-touch="scanBarcode()">Scan</button>
Hope it works!
If the cancel property is true you stop the scanning function manually.
inject $ionicPlatform in your controller
.controller('BarCodeScanCtrl', function($scope, $cordovaBarcodeScanner, $ionicPlatform) {
$scope.scanQR = function () {
$ionicPlatform.ready(function() {
$cordovaBarcodeScanner.scan()
.then(function(barcodeData) {
alert('result = '+barcodeData.text);
alert('type = '+barcodeData.format);
alert('cancelled = '+barcodeData.cancelled);
}, function(error) {
alert(error);
});
});
}
});

How to $watch type="files" inside the cell of Datatable - AngularJS

I am trying to create a Upload button inside a cell of Datatable, I have a clickHandler for a click event. The Upload button is called Window Explorer and I can select files to upload. In order to upload files to the server, I am using $watch to check for changes on the model "files", if there is any change, it will trigger the upload function. This function will upload files to the server. But It seems like $watch never sees what is going on inside the datatable. Here is my codes:
I am using angular-file-upload, and it seems to work on other part of the websites where I am not using an upload button inside the datatable.
When I run the code, it pops up the alert("test") and window to select files, but it never calls the server. No network call can be seen on the browser.
Can someone tell me what is wrong with my codes?
Thank you.
View:
<td><a class="btn btn-primary" ng-file-select ng-model="files" ng-multiple="true">Upload documents</a> </td>
cell click event:
$('td:eq(11)', nRow).bind('click', function () {
$scope.$apply(function () {
$scope.clickHandler(aData);
});
});
$watch controller:
$scope.clickHandler = function (data) {
//alert('upload document');
var appList = JSON.parse($window.sessionStorage.getItem(Constant.ApplicationKey));
var id = data[0];
angular.forEach(appList, function (v) {
if (v.PersonID == id) {
$scope.selectedUserName = v.FirstName + " " + v.LastName;
return;
}
})
$scope.$watch('files', function () {
$scope.upload($scope.files);
alert("test");
})
}
Upload function
$scope.upload = function (files) {
if (files && files.length) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
$upload.upload({
url: Constant.BaseUrl + 'api/User/UploadFile',
params: { username: $scope.selectedUserName, Role: 'Student' },
file: file
}).progress(function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' +
evt.config.file.name);
}).success(function (data, status, headers, config) {
console.log('file ' + config.file.name + 'uploaded. Response: ' +
JSON.stringify(data));
});
}
}
};
You should not create a new watcher everytime you click on the button. $scope.$watch(...) will register a new watcher everytime and you will have probably have problems.
Couldn't you just upload the files once the user clicks? Also, try not using a jQuery event listener but an angular ng-click instead soyou don't have model/view inconsistencies:
HTML:
<td ng-click="onClick(files)"> ... </td>
controller:
$scope.onClick = function (files) {
console.log('Handler clicked with files:', files);
if (files) {
console.log('Uploading files');
$scope.upload(files);
}
};
Actually, you want to run the upload when you click where exactly? I guess you need to wait until the user has selected the file modal and selected the files he wants to upload.

Resources