Open juristr opened 9 years ago
Great question! There are two main reasons behind this.
Firstly, the boxes property of the BoxService
should have been marked private. I was going to use a the underscore convention with this._boxes
. However, this was the main part I choked on the most while practicing. I also didn't want to get too much into detail on why it should be marked private.
Secondly, services shouldn't be stateful. This BoxService
is okay when the boxes
property is marked private, however it could be better. Below I refactored the BoxService
to a BoxFactory
out of preference. The big difference is that I now have a factory that doesn't store state. To call add
you have to pass in an array of boxes. Dealing with state is tricky, its best to remove it where possible.
function BoxFactory() {
return {
_boxSubject: new Rx.ReplaySubject(),
subscribe: function(subscription) {
this._boxSubject.subscribe(subscription);
},
add: function(boxes) {
boxes.push({ value: boxes.length + 1 });
this._boxSubject.onNext(boxes);
}
};
}
Then inside of my controller I can subscribe.
function BoxCtrl(boxFactory) {
this.boxes = [{ value: 1}];
boxFactory.subscribe(function(boxes) {
this.boxes = boxes;
}.bind(this));
}
BoxCtrl.$inject = ['boxFactory'];
Finally, in my directive all I have to do is pass the boxes I passed from the controller.
function BoxListDirective() {
return {
controllerAs: 'boxListCtrl',
controller: ['$scope', '$attrs', 'boxFactory', function($scope, $attrs, boxFactory) {
this.boxes = $scope.$eval($attrs.boxes);
this.add = function add() {
boxFactory.add(this.boxes);
}.bind(this);
}]
};
}
Awesome answer! Thx a lot for the time, really appreciate it :smiley:
So what you're basically suggesting is to have stateless services which are basically reduced to containing the pure logic (which in this stupid sample is a one-liner) and which serve as a hub for event registration (through Rx). The state is kept on the single UI elements which keep themselves updated through the observables mechanism.
Sounds great. I'll think about that and see what that'd mean on my existing Angular 1.x app. Maybe I'm coming back with some question again (if u don't mind :wink: )
Thx again!
I had the same question after watching the video. The updated code might be better at reducing state (actually now lives in the controller... I'd argue it should live in a model). But there is still a strange mixing of concerns. I think this is the original concern from @juristr
The box app has a subscription to the boxFactory
which updates this.boxes
which is passed down to the BoxListDirective
. The BoxListDirective
then also requires boxFactory
so that it knows how to update. If the BoxListDirective
needs to have such deep knowledge of the boxFactory, why involve a higher party?
At my work, I've been separating UI components out of applications - in a different repository even. This means that UI components cannot have any idea about how to get data - it is only passed down. This prevents the mess of 2-way data binding (also how to update data - ex: observables, immutable, etc), components just notify that something interesting happened (like events in Angular2). This makes those UI components truly reusable in the application as well as other applications - no assumptions of the data model are made.
Hi,
I was just looking u'r Angular U video about preparing for the NG 2 upgrade. Great video, thx. I was just curious about a thing.
Here: https://github.com/davideast/angularu-a2-migration/blob/master/a1/app.js#L29 Was there a reason you didn't directly bind to
boxService.boxes
?In that case you wouldn't have the need for a
$watch
at all, as Angular would be watching implicitly. Tried it here: http://plnkr.co/edit/bLTZhfhL4eaYYjsLG9oZ?p=previewWas that just for the purpose of keeping the demo simple, as I know there are more complex scenarios where you would have to introduce watches and where your Rx observable strategy is great.
thx for your response :smile: