Reputation: 1516
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?
Upvotes: 0
Views: 456
Reputation: 38490
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.
Upvotes: 1