Reputation: 11308
I asked this question on the Programmer's stack exchange, but didn't get any replies, so I thought I'd try my luck here...
I am working on a project where I would like to encapsulate a directive library and distribute it to other developers to use. I would like to keep the changes to the model within this encapsulated code, so I don't really want the dev's changing the scope variables outside of the lib.
In my code, I have 2 different approaches to communicating with my lib from the parent controller.
The first theory is to create a lib that contains a directive and a service. The parent controller would call the service, which would handle all of the changes to the lib model and the directive would react depending on these changes.
The second theory is to put all the functions to change the model in the directive itself and call the directive scope on the parent to make the changes.
Here is a plunker that shows what I'm asking in better detail. It's a simple example, but illustrates the 2 different methods.
http://plnkr.co/edit/CR350Vx7NiHs5tkjNWZL?p=preview
I'm leaning towards the second method as it just seems cleaner to implement from a development scenario.
Any advice from the Angular experts out there?
Plunker Html:
<body ng-app="myApp">
This is Example 1 - Using a service to modify directive
<div ng-controller="Example1Ctrl">
<example1-directive ng-model='example1Model'></example1-directive>
<br>
<br>
<input type="button" value="Change Example 1" ng-click='changeExample1()' />
</div>
<br>
<br>
This is Example 2 - Modifying directive in the scope of the directive
<div ng-controller="Example2Ctrl">
<example2-directive ng-model='example2Model'></example2-directive>
<br>
<br>
<input type="button" value="Change Example 2" ng-click='changeExample2()' />
</div>
</body>
Plunker js
var app = angular.module("myApp", []);
//--------------------------------------------------
//-------- This is example 1
//--------------------------------------------------
app.controller("Example1Ctrl", function($scope, example1Svc) {
$scope.example1Model = {
value: "Example 1 - Original Value"
}
$scope.changeExample1 = function() {
example1Svc.change($scope.example1Model, "Example 1 - Changed Value");
}
});
/// This part would be encapsulated in a lib
app.directive("example1Directive", function() {
return {
restrict: "E",
scope: {
model: "=ngModel"
},
template: "{{model.value}}"
}
});
app.service("example1Svc", function() {
this.change = function(example1Model, newValue) {
example1Model.value = newValue;
}
})
// End lib
//--------------------------------------------------
//-------- This is example 2
//--------------------------------------------------
app.controller("Example2Ctrl", function($scope, example1Svc) {
$scope.example2Model = {
value: "Example 2 - Original Value"
}
$scope.changeExample2 = function() {
$scope.example2Model.change("Example 2 - Changed Value");
}
});
/// This part would be encapsulated in a lib
app.directive("example2Directive", function() {
return {
restrict: "E",
scope: {
model: "=ngModel"
},
template: "{{model.value}}",
controller: function ($scope) {
$scope.model.change = function(newValue) {
$scope.model.value = newValue;
}
}
}
});
// end lib
Upvotes: 0
Views: 82
Reputation: 49590
I'm somewhat confused by your example #1. What does exampleSvc.change
do?
Example #2 definitely goes against MVVM best practice, as it couples the controller with the view. Controllers (of views) should be view-agnostic. They should only change the ViewModel to reflect the current state of the app. The View would then react (however the View chooses to) to changes in the ViewModel.
In particular, these lines "offend" the best practice in my mind:
$scope.model.change = function(newValue) {
$scope.model.value = newValue;
}
Now your controller relies on the view to define what the function does (or whether it is defined to begin with). Also, what if another directive decides to change the .change function?
EDIT: Take a look at this SO question, and in particular an answer by Mark.
EDIT #2: There is an interesting case for when some event needs to reach whichever directives or child controllers that could be interested. Use $scope.$broadcast (in controller) and $scope.$on (in directive) to handle. Here's a plunker
Upvotes: 2
Reputation: 17957
I'm going to agree with @New Dev and add a couple more thoughts. If you're building a directive library you don't want to also bundle controllers that the consumer of the library has to use. Your directives should be more or less self contained and provide enough of an api to be extensible and potentially used in other directives.
What does this mean? Your directives may want to define a controller so that they can be injected into other directives. E.g.
//your library
directiveModule.directive("example1Directive", function() {
return {
controller: function($scope, $element, $attrs) {
...
},
...
}
});
-
//application
app.directive("appDirective", function() {
return {
require: '?example1Directive',
link: function(scope, element, attrs, example1Directive) {
...
}
});
You may also want to specify various options that can be set on your directive with parameters e.g.
<div example1-directive="{opt1: 'val', opt2: scopeProp}"></div>
Your directive would then need to parse the the attribute and execute on the scope to generate the options. There's lots more you can do, I'd suggest taking a look at ngmodules.org and see what other people are doing.
Upvotes: 1