angularjsshowdown

AngularJS compile a template and use it in Showdown extension


I'm attempting to create a Showdown extension in my Angular app which will show scope variables. I was able to get it setup to show basic scope variables easily enough, but now I'd like to get it to where I can use the results of an ng-repeat and I can't get anything other than [[object HTMLUListElement]] to show.

Here's my controller so far:

app.controller('MyCtrl', ['$scope', '$window', '$compile', function($scope, $window, $compile){
    $scope.machines = [
        { abbv: 'DNS', name: 'Did Not Supply' },
        { abbv: 'TDK', name: 'The Dark Knight' },
        { abbv: 'NGG', name: 'No Good Gofers'}
    ];
    $scope.machine = $scope.machines[0];

    $scope.machine_list = $compile('<ul><li ng-repeat="m in machines">{{m.abbv}}: {{m.name}}</li></ul>')($scope);
  $scope.md = "{{ machine_list }}";

    var scopevars = function(converter) {
        return [
            { type: 'lang', regex: '{{(.+?)}}', replace: function(match, scope_var){
                scope_var = scope_var.trim();

                return $scope.$eval(scope_var);
            }}
        ];
    };
    // Client-side export
    $window.Showdown.extensions.scopevars = scopevars;
}]);

Plunkr: code so far

I feel like I've got to be close, but now I don't know if I'm on the completely wrong track for this, or if it's a showdown thing or an angular thing or what.


Solution

  • I realized I was fighting Angular (and losing quiet badly) with the way I was doing that. DOM in a controller is a no-no. And now I'm kind of angry about how easy it is once I started thinking properly and looking at the directive.

    Now instead of trying to do the compile and everything within the controller, I included the $compile service in the directive I'm using, so:

    htmlText = converter.makeHtml(val);
    element.html(htmlText);
    

    became:

    htmlText = converter.makeHtml(val);
    element.html(htmlText);
    $compile(element.contents())(scope);
    

    With that change in place I no longer need the portion of the extension that just did the basic evaluation, since it's being compiled {{ machine.name }} is automatically converted.

    But that still left me not being able to specify a variable to insert a template, just variables. But now that the output is going to be compiled through angular I can put the template in a partial and use an extension to convert from a pattern to an ng-include which works.

    New Controller Code:

    app.controller('MyCtrl', ['$scope', '$window', '$compile', function($scope, $window, $compile){
        $scope.machines = [
            { abbv: 'DNS', name: 'Did Not Supply' },
            { abbv: 'TDK', name: 'The Dark Knight' },
            { abbv: 'NGG', name: 'No Good Gofers'},
            { abbv: 'TotAN', name:'Tales of the Arabian Nights'}
        ];
        $scope.machine = $scope.machines[0];
    
      $scope.tpls = {
        'machinelist': 'partials/ml.html'
      };
      $scope.md = "{{machines.length}}\n\n{{include machinelist}}";
    
        var scopevars = function(converter) {
            return [
              { type: 'lang', regex: '{{include(.+?)}}', replace: function(match, val){
                val = val.trim();
                if($scope.tpls[val] !== undefined){
                  return '<ng-include src="\''+$scope.tpls[val]+'\'"></ng-include>';
                } else {
                  return '<pre class="no-tpl">no tpl named '+val+'</pre>';
                }
              }}
            ];
        };
        // Client-side export
        $window.Showdown.extensions.scopevars = scopevars;
    }]);
    

    And of course the new plunkr

    Hope this can help someone later down the road

    wisdom of the ancients