AngularJS do link function after $http response re

2019-07-20 17:52发布

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".

1条回答
Melony?
2楼-- · 2019-07-20 18:25

It is not clear what you are trying to achieve (I'm pretty 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.

查看更多
登录 后发表回答