Cordova contact plugin not working - angularjs

After calling this function I am getting the following error:
"TypeError: Cannot read property 'pickContact' of undefined"
$scope.pickContact = function() {
navigator.contacts.pickContact(function(contact) {
if(contact) {
$scope.requestData.guestName = contact.displayName;
if(contact.phoneNumbers && contact.phoneNumbers.length > 0) {
$scope.requestData.phoneNo = contact.phoneNumbers[0].value;
} else {
$scope.requestData.phoneNo = null;
}
$scope.$apply();
} else {
$ionicPopup.alert({
title: 'Error!',
template: 'Unable to get contact details'
});
}
}, function(err) {
console.log('Error: ' + err);
$ionicPopup.alert({
title: 'Error!',
template: 'Unable to get contact details'
});
});
};

Use the $cordovaContacts plugin for get contacts and inject the dependency in your controller.
This plugin is available only on devices, not in the browser please do test on device.
For this plugin first you need to install ngCordova, this will support you for many more plugins and implementations.
Install plugin using following command,
cordova plugin add cordova-plugin-contacts
Example :
.controller('MyCtrl', function($scope, $cordovaContacts, $ionicPlatform) {
$scope.addContact = function() {
$cordovaContacts.save($scope.contactForm).then(function(result) {
// Contact saved
}, function(err) {
// Contact error
});
};
$scope.getAllContacts = function() {
$cordovaContacts.find().then(function(allContacts) { //omitting parameter to .find() causes all contacts to be returned
$scope.contacts = allContacts;
}
};
$scope.findContactsBySearchTerm = function (searchTerm) {
var opts = { //search options
filter : searchTerm, // 'Bob'
multiple: true, // Yes, return any contact that matches criteria
fields: [ 'displayName', 'name' ] // These are the fields to search for 'bob'.
desiredFields: [id]; //return fields.
};
if ($ionicPlatform.isAndroid()) {
opts.hasPhoneNumber = true; //hasPhoneNumber only works for android.
};
$cordovaContacts.find(opts).then(function (contactsFound) {
$scope.contacts = contactsFound;
};
}
$scope.pickContactUsingNativeUI = function () {
$cordovaContacts.pickContact().then(function (contactPicked) {
$scope.contact = contactPicked;
}
}
});
Hope this will help you !!

Related

call function synchronously inside an angular for each

I'm using ngCordova File Transfer plugin in an ionic project to download set of images from urls. Here is the code i'm using for that.
// Save a image file in a given directory
$scope.saveImage = function(dir,imgUrl,imageName) {
var url = imgUrl;
var targetPath = cordova.file.dataDirectory+ dir+"/" + imageName;
var trustHosts = true;
var options = {};
// Download the image using cordovafiletransfer plugin
$cordovaFileTransfer.download(url, targetPath, options, trustHosts)
.then(function(result) {
$scope.loadedCount ++;
$ionicLoading.show({template : "<ion-spinner class='spinner-energized'></ion-spinner><p> Downloading pages : "+ $scope.loadedCount+" of "+ $scope.pages.length+ "</p><p>Please wait...</p><p><button class=\"button button-block button-positive\">continue in background</button></p>"});
if($scope.loadedCount == $scope.pages.length) {
$ionicLoading.hide();
$scope.showDownloadSuccessAlert = function() {
var alertPopup = $ionicPopup.alert({
title: 'Success!',
template: "Your magazine successfully downloaded. You can view it on Downloads!"
});
};
$scope.showDownloadSuccessAlert();
}
}, function(err) {
alert(JSON.stringify(err));
}, function (progress) {
if($scope.loadedCount > 80) {
}
});
};
// Download the current magazine
$scope.downloadMagazine = function() {
if($rootScope.user.user_id == undefined) {
$scope.showLoginAlert = function() {
var alertPopup = $ionicPopup.alert({
title: 'Oops!',
template: "Your must login to download magazines"
});
};
$scope.showLoginAlert();
return;
}
document.addEventListener('deviceready', function () {
var dirName = $rootScope.currentIssue.slug+'_VOL_'+$rootScope.currentIssue.vol+'_ISU_'+$rootScope.currentIssue.issue;
// First create the directory
$cordovaFile.createDir(cordova.file.dataDirectory, dirName, false)
.then(function (success) {
var count = 1;
$scope.loadedCount = 0;
angular.forEach($scope.pages, function(value, key) {
var imgName = count+".png";
$scope.saveImage(dirName,value.link,imgName); // Then save images one by one to the created directory.
count++;
});
}, function (error) {
// Directory already exists means that the magazine is already downloaded.
$scope.showDownloadedAlert = function() {
var alertPopup = $ionicPopup.alert({
title: 'Why worry!',
template: "Your have already downloaded this magazine. You can view it on downloads"
});
};
$scope.showDownloadedAlert();
});
}, false);
};
})
Problem here is that program try to download everything at once without waiting for previous one to finish. How to wait for one file to finish downloading and then start the other?
Thanks
If you want to do that automatically (you're not the first one : How can I execute array of promises in sequential order?), you could try reducing the list of address to a single Promise that will do the whole chain.
$scope.pages.reduce(function (curr,next) {
return curr.then(function(){
return $scope.saveImage(dirName, curr.link, imgName);
});
}, Promise.resolve()).then(function(result) {
$ionicLoading.show({template : "<ion-spinner class='spinner-energized'></ion-spinner><p> Downloading pages : "+ $scope.loadedCount+" of "+ $scope.pages.length+ "</p><p>Please wait...</p><p><button class=\"button button-block button-positive\">continue in background</button></p>"});
if($scope.loadedCount == $scope.pages.length) {
$ionicLoading.hide();
$scope.showDownloadSuccessAlert = function() {
var alertPopup = $ionicPopup.alert({
title: 'Success!',
template: "Your magazine successfully downloaded. You can view it on Downloads!"
});
};
$scope.showDownloadSuccessAlert();
}
});
And don't forget to make your saveImage async which returns a promise.
UPDATE:
You will need to remove the then logic from your save method and return the download method call:
return $cordovaFileTransfer.download(url, targetPath, options, trustHosts).promise;
Then you can put your download handler into Promise.resolve()).then. See above.
There's no other way other than chaining your promises. Here's an example:
angular.module('app', [])
.service('fakeDownloadService', function($timeout) {
this.download = (file) => $timeout(() => file, 100);
return this;
})
.run(function($rootScope, $q, fakeDownloadService) {
var listOfFiles = [];
for (var i = 0; i < 10; i++)
listOfFiles.push('file' + i);
$rootScope.log = [];
$rootScope.download = () => {
listOfFiles
.reduce((prev, curr) => {
return prev.then((result) => {
if(result)
$rootScope.log.push(result + ' downloaded');
return fakeDownloadService.download(curr);
});
}, $q.resolve())
.then(() => $rootScope.log.push('all done'));
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js"></script>
<div ng-app="app">
<button ng-click="download()">Start</button>
<div>Log:</div>
<ul>
<li ng-repeat="entry in log track by $index">
{{entry}}
</li>
</ul>
</div>

Pass parameter from controller to services in angularjs

How can i have one service or factory receiving two parameters from many controllers?
One parameter for the url, other for the file name to be stored on the filesystem.
I will have many controllers using this service, each passing his own url and filenames that reads the url and generate a pdf.
I will always store the last downloaded pdf providing an "open last pdf" button, that will use the name parameter.
I will have a "generate new pdf" button coming from the url.
I do follow this tutorial https://blog.nraboy.com/2014/09/manage-files-in-android-and-ios-using-ionicframework/ and everything works fine.
I am using cordova file-transfer and inappbrowser cordova plugins
These sections will receive the parameters :
dirEntry.getFile("pdf-number-1.pdf",
ft.download(encodeURI("http://www.someservice.com"),p,
My attempt always trigger the message unknow pdfService provider
Wich concepts of angular i am missing ? How can i fix it ?
In services.js i have :
.service('pdfService', function($scope, $ionicLoading){
if( window.cordova && window.cordova.InAppBrowser ){
window.open = window.cordova.InAppBrowser.open;
console.log("InAppBrowser available");
} else {
console.log("InAppBrowser not available");
}
this.download = function() {
$ionicLoading.show({
template: 'Loading...'
});
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
fs.root.getDirectory("ExampleProject",{create: true},
function(dirEntry) {
dirEntry.getFile(
"pdf-number-1.pdf",
{
create: true,
exclusive: false
},
function gotFileEntry(fe) {
var p = fe.toURL();
fe.remove();
ft = new FileTransfer();
ft.download(
encodeURI("http://www.someservice.com"),
p,
function(entry) {
$ionicLoading.hide();
$scope.imgFile = entry.toURL();
},
function(error) {
$ionicLoading.hide();
alert("Download Error Source -> " + error.source);
},
false,
null
);
},
function() {
$ionicLoading.hide();
console.log("Get file failed");
}
);
}
);
},
function() {
$ionicLoading.hide();
console.log("Request for filesystem failed");
});
}
this.load = function() {
$ionicLoading.show({
template: 'Loading...'
});
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs) {
fs.root.getDirectory(
"ExampleProject",
{
create: false
},
function(dirEntry) {
dirEntry.getFile(
"pdf-number-1.pdf",
{
create: false,
exclusive: false
},
function gotFileEntry(fe) {
$ionicLoading.hide();
$scope.imgFile = fe.toURL();
alert(fe.toURL());
window.open(fe.toURL(), '_system', 'location=no,toolbar=yes,closebuttoncaption=Close PDF,enableViewportScale=yes');
},
function(error) {
$ionicLoading.hide();
console.log("Error getting file");
}
);
}
);
},
function() {
$ionicLoading.hide();
console.log("Error requesting filesystem");
});
}
});
And in the controller :
.controller('SomeCtrl', function($scope, $ionicPopup, pdfService) {
...
pdfService.download = function(url) {
console.log('pdfService download');
}
pdfService.load = function() {
console.log('pdfService load');
}
You will need to inject the service to your controllers and call a function with the two params you want as your arguments.
eg.
.service('pdfService', function(){
var lastUrl;
var lastFileName
return {
createPdf(url, fileName){
//do processing
lastUrl = url;
lastFileName = fileName
},
loadLastPdf(){
//use lastUrl and lastFileName
}
}
}
and in your controller:
.controller('SomeCtrl', function(pdfService) {
pdfService.createPdf('http://example.com', 'file.pdf');
// or pdfService.loadLastPdf();
}
That being said, the error you are reporting means that the DI is unable to find a service with the name pdfService to inject to your controller. This might be because you forgot to include the service.js file to your html as a script tag (if you are doing it like that) or you forgot to add it as a dependency using require (if you are using sth like browserify) or maybe if you are minifying your code since you are not using the minsafe syntax

how to test inner controller which loads data for select control in angular-formly

I have a ui-select field
{
key: 'data_id',
type: 'ui-select',
templateOptions: {
required: true,
label: 'Select label',
options: [],
valueProp: 'id',
labelProp: 'name'
},
controller: function($scope, DataService) {
DataService.getSelectData().then(function(response) {
$scope.to.options = response.data;
});
}
}
How can I access that inner controller in my unit tests and check that data loading for the select field actually works ?
UPDATE:
An example of a test could be as such:
var initializePageController = function() {
return $controller('PageCtrl', {
'$state': $state,
'$stateParams': $stateParams
});
};
var initializeSelectController = function(selectElement) {
return $controller(selectElement.controller, {
'$scope': $scope
});
};
Then test case looks like:
it('should be able to get list of data....', function() {
$scope.to = {};
var vm = initializePageController();
$httpBackend.expectGET(/\/api\/v1\/data...../).respond([
{id: 1, name: 'Data 1'},
{id: 2, name: 'Data 2'}
]);
initializeSelectController(vm.fields[1]);
$httpBackend.flush();
expect($scope.to.options.length).to.equal(2);
});
You could do it a few ways. One option would be to test the controller that contains this configuration. So, if you have the field configuration set to $scope.fields like so:
$scope.fields = [ { /* your field config you have above */ } ];
Then in your test you could do something like:
$controller($scope.fields[0].controller, { mockScope, mockDataService });
Then do your assertions.
I recently wrote some test for a type that uses ui-select. I actually create a formly-form and then run the tests there. I use the following helpers
function compileFormlyForm(){
var html = '<formly-form model="model" fields="fields"></formly-form>';
var element = compile(html)(scope, function (clonedElement) {
sandboxEl.html(clonedElement);
});
scope.$digest();
timeout.flush();
return element;
}
function getSelectController(fieldElement){
return fieldElement.find('.ui-select-container').controller('uiSelect');
}
function getSelectMultipleController(fieldElement){
return fieldElement.find('.ui-select-container').scope().$selectMultiple;
}
function triggerEntry(selectController, inputStr) {
selectController.search = inputStr;
scope.$digest();
try {
timeout.flush();
} catch(exception){
// there is no way to flush and not throw errors if there is nothing to flush.
}
}
// accepts either an element or a select controller
function triggerShowOptions(select){
var selectController = select;
if(angular.isElement(select)){
selectController = getSelectController(select);
}
selectController.activate();
scope.$digest();
}
An example of one of the tests
it('should call typeaheadMethod when the input value changes', function(){
scope.fields = [
{
key: 'selectOneThing',
type: 'singleSelect'
},
{
key: 'selectManyThings',
type: 'multipleSelect'
}
];
scope.model = {};
var formlyForm = compileFormlyForm();
var selects = formlyForm.find('.formly-field');
var singleSelectCtrl = getSelectController(selects.eq(0));
triggerEntry(singleSelectCtrl, 'woo');
expect(selectResourceManagerMock.searchAll.calls.count()).toEqual(1);
var multiSelectCtrl = getSelectController(selects.eq(1));
triggerEntry(multiSelectCtrl, 'woo');
expect(selectResourceManagerMock.searchAll.calls.count()).toEqual(2);
});

Angular translate inside service

I am struggling with angular translate for my ionic app. The thing is that I have a service with data to share between views but I need to translate this data. Unfortunately I just see blank screen without any errors in console.
I would appreciate if someone could help if there is something wrong with that code (I use useStaticFilesLoader):
app.service('customService', function($q, $rootScope, $filter, $translate) {
$rootScope.$on('$translateChangeSuccess', function () {
var $translate = $filter('translate');
return {
items: [
{
id: '1',
title:$translate('TITLE');
}
]
],
getItems: function() {
return this.items;
},
getItem: function(itemId) {
var dfd = $q.defer();
this.items.forEach(function(item) {
if (item.id === itemId) dfd.resolve(item);
});
return dfd.promise;
}
};
});
});
Try something like this:
app.factory('customService', function($rootScope, $translate) {
var items = [],
updateItems = function() {
items.length = 0;
$translate('TITLE').then(function(title) {
items.push({
id: '1',
title: title;
});
});
};
updateItems();
$rootScope.$on('$translateChangeSuccess', updateItems);
return {
items: items,
getItem: function(itemId) {
var result;
items.forEach(function(item) {
if (item.id === itemId) {
result = item;
}
});
return result;
}
}
});

How to manually add a datasource to Angularjs-select2 when select2 is set up as <input/> to load remote data?

I am using the Select2 as a typeahead control. The code below works very well when the user types in the search term.
However, when loading data into the page, I need to be able to manually set the value of the search box.
Ideally something like: $scope.selectedProducerId = {id:1, text:"Existing producer}
However, since no data has been retrieved the Select2 data source is empty.
So what I really need to be able to do is to add a new array of data to the datasource and then set the $scope.selectedProducerId, something like: $scope.producersLookupsSelectOptions.addNewData({id:1, text:"Existing producer}) and then
$scope.selectedProducerId = 1;
Researching this I have seen various suggestions to use initSelection(), but I can't see how to get this to work.
I have also tried to set createSearchChoice(term), but the term is not appearing in the input box.
I would be most grateful for any assistance.
Thanks
This is the html
<div class="col-sm-4">
<input type="text" ui-select2="producersLookupsSelectOptions" ng- model="selectedProducerId" class="form-control" placeholder="[Produtor]" ng-change="selectedProducerIdChanged()"/>
</div>
This is the controller
angular.module("home").controller("TestLookupsCtrl", [
"$scope", "$routeParams", "AddressBookService",
function($scope, $routeParams, AddressBookService) {
$scope.producersLookupsSelectOptions = AddressBookService.producersLookupsSelectOptions();
}
]);
This is the service:
angular.module("addressBook").service("AddressBookService", [
"$http", "$q", function($http, $q) {
var routePrefix = "/api/apiAddressBook/";
//var fetchProducers = function(queryParams) {
// return $http.get(routePrefix + "GetClientsLookup/" + queryParams.data.query).then(queryParams.success);
//};
var _getSelectLookupOptions = function(url, minimumInputLength, idField, textField) {
var _dataSource = [];
var _queryParams;
return {
allowClear: true,
minimumInputLength: minimumInputLength || 3,
ajax: {
data: function(term, page) {
return {
query: term
};
},
quietMillis: 500,
transport: function(queryParams) {
_queryParams = queryParams;
return $http.get(url + queryParams.data.query).success(queryParams.success);
},
results: function(data, page) {
var firstItem = data[0];
if (firstItem) {
if (!firstItem[idField]) {
throw "[id] " + idField + " does not exist in datasource";
}
if (!firstItem[textField]) {
throw "[text] " + textField + " field does not exist in datasource";
}
}
var arr = [];
_.each(data, function(returnedData) {
arr.push({
id: returnedData[idField],
text: returnedData[textField],
data: returnedData
});
});
_dataSource = arr;
return { results: arr };
}
},
dataSource: function() {
return _dataSource;
},
getText: function (id) {
if (_dataSource.length === 0) {
throw ("AddressBookService.getText(): Since the control was not automatically loaded the dataSource has no content");
}
return _.find(_dataSource, { id: id }).text;
}
//initSelection: function(element, callback) {
// callback($(element).data('$ngModelController').$modelValue);
//},
//createSearchChoice:function(term) {
// return term;
//},
addNewData:function(data) {
this.ajax.results(data,1);
};
};
return {
producersLookupsSelectOptions: function() {
var url = routePrefix + "GetClientsLookup/";
return _getSelectLookupOptions(url, 2, "Id", "Name");
},
}
}
]);

Resources