Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Markdown with AngularJS directives

I am trying to make a directive for HTML using AngularJS so that I can render Markdown in the browser. What I want is to have a <markdown> tag with a src attribute that will load the file specified and render it correctly.

I have partially implemented this as follows -

function Main($scope) {
    $scope.theContent = '#asgakfgajgfas\n##akfaljfqpo\ndhvkajvlbndvm';
};

angular.module('myApp', [])
    .directive("markdown", function ($compile) {
    return {
        restrict: 'E',
        require: 'model',
        scope: {
            value: "=model"
        },
        template: '<div ng-bind-html-unsafe="value | markdown"></div>'
    };
}).filter('markdown', function () {
    var converter = new Showdown.converter();
    return function (value) {
        return converter.makeHtml(value || '');
    };
});

And the corresponding HTML -

<div ng-controller="Main">
    <markdown model="theContent"></markdown>
</div>

Here is the jsFiddle link(based on John Linquist's example) for the above code. This does not work with the src attribute, however it is able to load a markdown text string specified in a model.

Could you tell me how I can change this code to load the file specified in the src tag. I was thinking of using the $http provided by AngularJS but couldn't get my head around actually using it inside the directive definition.

What I would like to achieve is <markdown src="a/b/c.md" />

like image 813
ssb Avatar asked Feb 20 '13 14:02

ssb


3 Answers

I have finally come up with a solution to this.

// Render markdown in the HTML page
app.directive("markdown", function ($compile, $http) {
    var converter = new Showdown.converter();
    return {
        restrict: 'E',
        replace: true,
        link: function (scope, element, attrs) {
            if ("src" in attrs) {
                $http.get(attrs.src).then(function(data) {
                    element.html(converter.makeHtml(data.data));
                });
            } else {
                element.html(converter.makeHtml(element.text()));
            }
        }
    };
});

I define a custom link function which either takes the content of the <markdown> tags and renders them correctly, or it gets the content of the file given in the src attribute and renders that.

like image 178
ssb Avatar answered Nov 04 '22 09:11

ssb


Another alternative, based on the answer above. This one uses angular eval, watch and sanitize, which means that it is useful for live editing, sanitizes the html, which is necessary as markdown leaves inline html such as a script tag as-is, and uses an angular expression to access the data.

Use

Controller:

 $scope.data = {markdown:"#H1"} 

View:

<div markdown="data.markdown">
</div>

<textarea ng-model="data.markdown"></textarea>

Code:

angular.module('markdown',['ngSanitize']).directive('markdown', function ($sanitize) {
    var converter = new Showdown.converter();
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            function renderMarkdown() {
                var htmlText = converter.makeHtml(scope.$eval(attrs.markdown)  || '');
                element.html($sanitize(htmlText));
            }
            scope.$watch(attrs.markdown, function(){
                renderMarkdown();
            });
            renderMarkdown();
        }
    }
});
like image 21
Gudlaugur Egilsson Avatar answered Nov 04 '22 09:11

Gudlaugur Egilsson


Here is the simplest generic solution I could muster as using $sanatize or $sce is not required:

http://plnkr.co/edit/6OK0RcMChmD1S3JtfItp?p=preview

  <body ng-app="myApp">

    <textarea ng-model="test"></textarea>
    <div markdown="test"></div>
    
    <script src="//code.angularjs.org/1.3.0/angular.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script>

    <script>
      angular.module('myApp', [])
        .run(function($rootScope) {
          $rootScope.test = '#foo\r- bar\r- baz'
        })
        .directive('markdown', function () {
              var converter = new Showdown.converter();
              return {
                  restrict: 'A',
                  link: function (scope, element, attrs) {
                      function renderMarkdown() {
                          var htmlText = converter.makeHtml(scope.$eval(attrs.markdown)  || '');
                          element.html(htmlText);
                      }
                      scope.$watch(attrs.markdown, renderMarkdown);
                      renderMarkdown();
                  }
              };
          });
    </script>
  </body>

RESULT:

foo

  • bar
  • baz
like image 3
John Culviner Avatar answered Nov 04 '22 09:11

John Culviner