Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I need to perform a link function in a directive after an http response returns. The idea is something like this:

<input type="text" my-field>
<script>
 angular.module("mine")
 .controller ('myCtrl', function ($scope) {
  $http.get("/my/service").success(function (data, status, headers, config) {
    // OK, done with the query... now I know my field name to bind to. Somehow
    // I have to get it down to the link function below...
  });
 })
 .directive ('myField', function ($compile) {
  return {
    link: function (scope, element, attrs) {
      var my_field = attrs.myField;
      element.removeAttr('my-field');

      // Somehow figure out the field here in ngFieldSpec
      element.attr('ng-model', ngFieldSpec);
      $compile(element)(scope);
    };
   });
</script>

Here, I need to bind the input field to an element of the response, but I don't know what the element will be called until I get the response. But when I run it, the directive's link runs before $http gets done: the actual sequence is

  • $http.get starts
  • directive's link function run
  • $http.get returns success

I'm somewhat familiar with $q, but am not sure how that would be used to do what needs to be done. BTW, I have shown only one input field invoking the myField directive, but there are potentially many of them on the page, and they all need the same information.

Edited to add more information in response to request:

I have a service that returns a JSON data structure. I do not know in advance exactly what that data structure will look like, but I can figure it out and match the fields up with my page's input fields. I'm attempting to do this matching up in the link function. I'm glad to do it somewhere else; I could do it in the $http.success function, but that would be doing DOM manipulation in a controller; and my understanding is that DOM manipulation should only be done in a directive.

Here's what my HTML needs to look like:

<input type="text" my-field="[MY_EXTENSION_NAME]myFieldName">
<input type="text" my-field="[MY_EXTENSION_NAME]myFieldName2">
<input type="text" my-field="[MY_EXTENSION_NAME_2]myFieldName">

The response from the server will be something like:

{
    realField1: "Diddly",
    realField2: "Squat",
    extensions: [
      {
        name: "MY_EXTENSION_NAME",
        fields: [
          { name="myFieldName" value="Foo" },
          { name="myFieldName2" value="Bar" }
        ]
      },
      {
        name: "MY_EXTENSION_NAME_2",
        fields: [
          { name="myFieldName" value="Baz" },
          { name="myFieldName2" value="Buz" }
        ]
      }
    ]
 }

The server's response may vary because:

  • There may be any number of extensions ("MY_EXTENSION_NAME", etc.)
  • The extensions may be returned in any order
  • There may be any number of fields
  • The fields may be returned in any order

The whole problem here is I want to convert "[MY_EXTENSION_NAME]myFieldName" into the ng-model "model.extensions[0].fields[0].value. However, I am now thinking transforming the data into a canonical form during reading will be easier, so ng-model can just be "model.my_extension_name.myFieldName".

share|improve this question
    
Not sure, but I think that $watch could help. –  Beterraba Jan 11 '14 at 19:25
    
why are you using $compile inside a link function? you can probably achieve what you need with an isolated scope using '@'. please describe what you want to do. –  Ilan Frumer Jan 11 '14 at 19:32
    
Sorry, I thought I did explain what I want to do. I'll update. –  fool4jesus Jan 11 '14 at 19:40
    
By the way, I am calling $compile in link because that was suggested by somebody else in another SO and it works, but I'd be happy to have a better way to do that, also. –  fool4jesus Jan 11 '14 at 20:04

1 Answer 1

up vote 2 down vote accepted

It is not clear what you are trying to achieve (it is quite sure there will be some better way), but you could do it like this:

1.
Define a promise in your scope:

app.controller('myCtrl', function ($http, $scope) {
    $scope.model = {
        promise: $http.get('/my/service'),
        myField01: 'Hello, world, from 01 !',
        myField02: 'Hello, world, from 02 !',
        myField03: 'Hello, world, form 03 !'
    };
});

2.
From your HTML, reference that promise in order to pass it to your directive:

<input type="text" my-field="model.promise" />

3.
Get this promise into your directive's isolate scope:

app.directive ('myField', function ($compile) {
    return {
        scope: { promise: '=myField' },
        ...

4.
In your link function, register a callback for when the promise gets resolved (i.e. you get a response to your request) and do all necessary manipulation:

...
link: function (scope, elem, attrs) {
    scope.promise.success(function (data) {
        elem.removeAttr('my-field');
        elem.attr('ng-model', 'model.' + data.fieldName);
        $compile(elem)(scope.$parent);
    });
}

See, also, this short demo.

share|improve this answer
    
Thanks for the concrete suggestion. On the bigger picture, I am pretty new to Angular and I am happy to take alternate suggestions. What would that better way be? I previously edited my question to make it clear exactly what it is I'm trying to do - I need to bind some input fields to the data, but I don't know exactly what the data fields are called until the $http.get succeeds. I'm not sure how to explain it more clearly than that. –  fool4jesus Jan 11 '14 at 20:03
    
What I meant is that it is not clear what you are trying to achieve at a higher level. I.e. why you need to have some input fields that you don't know what to bind to etc. –  ExpertSystem Jan 11 '14 at 21:10
    
Sorry. Because the server might return the data structured in different ways. There's a two-level hierarchy, and each level of the hierarchy has something like <item><name>foo</name><value>bar</value></item>. All I know in the attribute is "foo.bar"... so that might map to an ng-model attribute like (say) "model.extensions[3].fields[4].value. One alternate way be to remap the data into two levels of hashes and then back again when saving; but I don't think that would really help, because when the directive is called, extensions[3] would not yet exist. Suggestions are welcomed though. :-) –  fool4jesus Jan 13 '14 at 13:32
    
Instead of having some DOM elements you don't know what to bind to (thus having to wait for a response from the server to tell you what to bind each element to), why not receiving the data from the server and then insert the appropriate DOM elements (bound to the appropriate values) ? (Sorry, if my suggestion does not make sense - I must admit I can't 100% picture your setup yet.) –  ExpertSystem Jan 13 '14 at 14:36
    
Thanks for staying with me - interesting idea. The thing is that the HTML author writes the DOM elements right into the Angular template, and he knows what the server will be returning at runtime; he just does not know exactly the data format. One possibility might be to have the author write the elements, but not create a directive on the fields at all, but just a regular attribute. Then after we read from the server, go through the fields, build the ng-model attributes, and $compile them. That means DOM manipulation in the controller, though. Still, it would be fairly minimal. –  fool4jesus Jan 13 '14 at 14:54

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.