0

I have implemented a remote validation directive which queries a specified JSON API endpoint once an input is blurred. It expects the response { valid: true|false }.

I now have to extend it to allow for it to send a request involving multiple values from the parent scope.

My tag definition looks as follows:

<input remote-validate endpoint="/api/action/:value" ng-model="MyInput" />

where :value is substituted with a urlencoded value of $scope.MyInput. This is working well.

What I require is given an endpoint like this /api/action/:value/:person/:thing, the :person and :thing substitutions are bound to the parent scope values. My initial thought is to have a bindings attribute which maps the parent scope to the endpoint.

<input remote-validate endpoint="/api/action/:value/:person/:thing" bindings="{person: 'firstName', thing: 'thingName'}" ng-model="MyInput" />
(...)
<input ng-model="firstName" />    <input ng-model="thingName" />

given var bindings = scope.$eval(attrs.bindings); is there any way to loop through the bindings object and create a two way binding to the parent scope?

EDIT: A workaround may be to do this:

<input validate-remotely 
    endpoint="api/action/:value/:param1/:param2" 
    param1="person" 
    param2="thing" />

which obviously means I can only use the number of parameters I specify in the scope definition. Which is a good work around for me here. Would be nice to know if there was a way to create these bindings dynamically at compile/link time.

I can provide a fiddle but I don't have the time right now, so I'm hoping that someone will have a good idea if/how this is possible.

Stan Bondi
  • 4,118
  • 3
  • 24
  • 35
  • why dont you just use bidiretional binding using '=' available with directives – Ajay Beniwal Jul 11 '13 at 12:08
  • Because I want to map arbitrarily named :tags in the url endpoint string to the parent scope. I know I could do something like this: , but then I would have to define 'a' in my directive scope. `scope: {a:'='}` - which would mean my directive isn't generic. – Stan Bondi Jul 11 '13 at 12:16

2 Answers2

0

As I learned yesterday, you can use $parse on your 'binding' attributes. See this post. This is a good way to $watch an attribute for changes that come from parent or children

Example:

<div parse-test bindings="{person: 'firstName', thing: 'thingName'}"></div>

// in your directive link function:
scope.bindings = $parse(attrs.bindings)(scope);
  scope.$watch('bindings', function(val){
    for (i in scope.bindings){
      scope[i] = scope.bindings[i];
    }  
  }, true);

Experiment with this plunk

Community
  • 1
  • 1
rGil
  • 3,719
  • 1
  • 22
  • 30
0

This is how I solved it in a not so ideal way.

See this plunker, which has a copy of the directive I use live and works well in my app but doesn't work in Plunker (I expect the randomly chosen plunker api endpoint to be called and fail, but it is not called due to some strange errors I don't care to debug).

The problem I had with the proposed workaround in the EDIT of this question is that it seems you cannot use scope and require in the same directive. I would love it if someone could elaborate on if this is the case and why.

The Workaround

I used a params attribute to specify additional data in object notation to be extended to go to the resource request. I used the handlebars syntax in the params attribute to dynamically change the object notation string.

<input ng-model="value2" ... />
<input validate-remotely 
       endpoint="/api/:value/?thing=:anotherValue" 
       params="{ anotherValue: '{{ value2 }}' }"  ... />

Not so great :/ but it works.

Then on blur of the validated input, I re-$eval the object string (app.js:40 in the plunker) and extend the resources data to include this object, which using ngResources colon (:) notation, replaces the URL.

The validation has 3 states:

remoteValidityPending: field validation fails because it is still being checked - good for showing a spinner.

remoteValidityUnchecked: The field has changed since it has been checked but has not yet blurred - ensures that any `ng-disable="form.$invalid"' submit buttons stay disabled until we know that the backend has returned a response.

remoteValidity: If this passes, the fields endpoint has been called and '{valid: true}' has been returned from the server.

I'm almost certain there are better/different ways to solve this, and I'll be happy to change the answer if someone improves this directive. I hope this helps someone out there.

Stan Bondi
  • 4,118
  • 3
  • 24
  • 35