1

I am creating a templateparser that can parse multiple templates and return the generated html content. For binding the html and view information I am using the Angular $compile service. The problem I am encountering is that the promise .then() is called before the promise is resolved (and thus results in undefined).

AngularJS version: 1.6.3


The parser function

/**
 * Using the $compile function, this function generates a full HTML page based on the given process and template
 * It does this by binding the given process to the template $scope and uses $compile to generate a HTML page
 * @param {Afhandeling} process - The process that can bind to the template
 * @param {string} templatePath - The location of the template that should be used
 * @param {boolean} [useCtrlCall=true] - Whether or not the process should be a sub part of a $ctrl object. If the template is used
 * for more then only an email template this could be the case (EXAMPLE: $ctrl.<process name>.timestamp)
 * @return {IPromise<string>} A full HTML page
 */
public createEmail(process: Afhandeling, templatePath: string, useCtrlCall = true): ng.IPromise<string> {
    let processScope = {};

    if (useCtrlCall) { //Create scope object | Most templates are called with $ctrl.<process name>
       const controller = "$ctrl";
       processScope[controller] = {};
       processScope[controller][process.__className.toLowerCase()] = process;
    } else {
       processScope = process;
    }

    return this.$http.get(templatePath)
        .then((response) => {
            let template = response.data;
            let scope = this.$rootScope.$new();
            angular.extend(scope, processScope);

            let generatedTemplate = this.$compile(jQuery(template))(scope);

            let waitForRenderCompletion = () => {
                if (scope.$$phase || this.$http.pendingRequests.length) {
                    console.warn("Time for a timeout.");
                    this.$timeout(waitForRenderCompletion);
                } else {
                    console.warn("Lets return the template.");
                    return generatedTemplate[0].innerHTML;
                }
            };
            waitForRenderCompletion();

      })
      .catch((exception) => {
          console.error(exception);
          this.logger.error(
             TemplateParser.getOnderdeel(process),
              "Email template creation",
              (<Error>exception).message
          );
          return null;
      });
}

The function call

this.templateParser.createEmail(
    this.model,                
    "<template url>"
).then((template: string) => {
   console.warn(template); //Results in 'undefined'
});

The reason I am watchting $$phase for changes is because of $compile not giving any feedback on when it is done compiling. The template can consist of an undefinite number of templates bound together by ng-include's. ng-includes are also async so I cannot think of any other way to check when the $compile is done (My question about a better solution then this).


What I am thinking

When I look at the console output I get the following:

Time for a timeout.
undefined
(2) Time for a timeout.
Lets return the template.

So it seems like the promise is automatically resolved when the first $timeout resolves. Yet this doesn't make any sense, since I am not returning anything.

Any help is appreciated.


Answer

Thanks @charlietfl for the hint. The working code is below. I'm now returning the function, so that I have a return value in my promise. I am also returning the $timeout, so that the function can be called recursively.

The code:

let waitForRenderCompletion = () => {
   if (scope.$$phase || this.$http.pendingRequests.length) {
       console.warn("Time for a timeout.");
       return this.$timeout(waitForRenderCompletion);
       });
   } else {
       console.warn("Lets return the template.");
       return generatedTemplate[0].innerHTML;
   }
};
return waitForRenderCompletion();
Mr.wiseguy
  • 4,092
  • 10
  • 35
  • 67
  • 1
    because nothing is returned from the request `then()`. You need to return a promise since `$timeout` is asynchronous. Also note that the return inside `waitForRenderCompletion ()` does not return to the outer function – charlietfl Jun 14 '17 at 10:14
  • @charlietfl updated the question with your answer. Unfortunately that was not the solution. Also I get that the outer function is not returned. But I do not know how I should return that function, since it is recursive. Any tips on how to do that? – Mr.wiseguy Jun 14 '17 at 11:22
  • still not returning anything to the request `then()` – charlietfl Jun 14 '17 at 11:31
  • @charlietfl "Also I get that the outer function is not returned. But I do not know how I should return that function, since it is recursive. Any tips on how to do that?" – Mr.wiseguy Jun 14 '17 at 11:48
  • 1
    Take a look at this example: http://plnkr.co/edit/OA4i156BQaRLKIpc75Ja?p=preview. I commented what gets returned where – charlietfl Jun 14 '17 at 11:59
  • @charlietfl That worked! Could you make an answer out of it? Then I can accept it as best answer. – Mr.wiseguy Jun 15 '17 at 05:58

0 Answers0