Reputation: 4704
I have learned much about recursive directives recently, but there are still a few things I just don't get yet.
This post in particular solved most of my problem: Recursion in Angular directives (Thanks!!)
I have managed to build a recursive rule editor directive based on this technique that does most of what I want. It successfully edits a complex JSON structure that describes a rule for processing messages, allowing you to add and remove levels of hierarchy and edit values.
The directive is designed to be concisely instantiated like this:
<rule-element rule="<scope variable>"></rule-element>
Ideally, I want this directive to behave as part of a form, with signaling for validation, and everything I read is telling me I need to use ngModel for my binding. However, the example, and thus my code, don't use ngModel, opting instead to use attributes and local isolate scopes.
I've read that using ngModel with isolate scopes is tricky (Isolated scope pitfall with ng-model dependency), and my whack-a-mole path to getting here wasn't very successful using ngModel, so I ended up just using bi-directional binding on an attribute.
What's the best way to add the hooks necessary for this element to report to the form it's contained in so I can use angular validation to present messages and enable/disable the submit button?
I'm sure I could hack this somehow, but my main motivation for doing this is to learn the right way, so, uh, here I am.
Here's a plunkr of my work-in-progress directive: http://plnkr.co/edit/02b9zTS1O81wgVapn3eg?p=preview
Any suggestions?
Upvotes: 4
Views: 648
Reputation: 4704
This is sort of a half-answer.
I was hoping to have the element self-validate, but I couldn't figure out a way to have the form attached to it without the form becoming part of the recursion and creating new problems that I'm not ready to solve.
So instead I ended up building a validation directive that can be attached to the element to access the model:
app.directive('ruleValid', function () {
return {
restrict: "A",
require: "ngModel",
link: function (scope, element, attrs, ngModel) {
scope.$watch(attrs.ngModel, function(thing) {
ngModel.$setValidity(attrs.ngModel, validate(thing));
}, true);
}
};
});
The thing I don't like is that it has it's own recursion to validate the entire tree (see the plunkr for the validate() function) It seems like there's a better solution where the element itself reports validation as it recurses.
So, this works, but isn't as elegant as I hoped it would be.
Here's the updated plunkr: http://plnkr.co/edit/7I0fBZnTEU8Ss0ZYphsB?p=preview
Upvotes: 0
Reputation: 2158
I also had complex validation requirements. In my case I get a json from the server containing error messages for the associated input fields. It just relies on the field name (also arrays and nesting with ng-repeat like attr[34].part. That's why I replace brackets and dots for valid identifiers).
In this case I have a scope object containing error messages for all attributes (I have another function resetting the validity manually because it would be still there after another submit).
I don't know if this is useful for you but it might give you an idea.
$scope.setValidationErrors = function ( error ) {
$scope.myForm.$setValidity( "validation", false );
if ( error.data ) {
// Add error messages
for ( var err in error.data ) {
var sanitizedErr = err.replace( /[.\[\]]/g, '-' );
if ( error.data.hasOwnProperty( err ) && $scope.showProviderForm[sanitizedErr] ) {
$scope.myForm[sanitizedErr].$setValidity( "validation", false );
$scope.myForm[sanitizedErr].$setPristine();
}
}
$scope.errors = error.data;
}
};
Upvotes: 1