Using AngularJS 1.6 and braintree-web 3.6.2 Hosted Fields. I've wrapped the Braintree callbacks in promises using $q (though new Promise() works fine, too). Right now, I'm simulating $scope.$apply() using $timeout with no time parameter.
Here's a snippet of ES6 code from the class for my service:
'use strict';
import braintree from 'braintree-web';
export class Cart {
constructor($q, $log) {
this.$q = $q;
this.$log = $log;
}
// Handle the callback as a promise
braintreeHostedFieldsCreate(clientInstance) {
return this.$q((resolve, reject) => {
braintree.hostedFields.create({
client: clientInstance,
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: '10/2019'
}
},
styles: {
input: {
'font-size': '14px',
'font-family': 'Helvetica Neue, Helvetica, Arial, sans-serif',
color: '#555'
},
':focus': {
'border-color': '#66afe9'
},
'input.invalid': {
color: 'red'
},
'input.valid': {
color: 'green'
}
}
}, (hostedFieldsErr, hostedFieldsInstance) => {
// Make event handlers run digest cycle using $timeout (simulate $scope.apply())
hostedFieldsInstance.on('blur', event => this.$timeout(() => event));
hostedFieldsInstance.on('focus', event => this.$timeout(() => event));
hostedFieldsInstance.on('validityChange', event => this.$timeout(() => event));
hostedFieldsInstance.on('empty', event => this.$timeout(() => event));
hostedFieldsInstance.on('notEmpty', event => this.$timeout(() => event));
// Reject or resolve the promise
if(hostedFieldsErr) {
this.$log.error('Not able to create the hosted fields with Braintree.', hostedFieldsErr);
return reject(hostedFieldsErr);
} else {
this.hostedFieldsInstance = hostedFieldsInstance;
return resolve(hostedFieldsInstance);
}
});
});
}
}
Is using $timeout in this situation a good substitute for $scope.$apply() as the latter's use after AngularJS 1.5 appears to be discouraged?
If an Angular service wraps code that uses for example DOM events, it should be the serviceĀ“s responsibility to make sure the digest loop is started (if needed).
Using
$apply/$digest
is still the correct way to do this.The service can simply inject the
$rootScope
and wrap the event handler functionality in a call to$apply
, keeping it blackboxed from the users.An alternative is to demand the service user to pass a scope and instead call
$digest
, which would limit the amount of watchers processed. In my mind however the (most likely negligible) performance increase from this wouldn't be worth the added complexity.While
$timeout
is also a possibility, it would just postpone the execution unnecessarily and in the end call$apply
on the$rootScope
anyway.