sparksuite / simplemde-markdown-editor

A simple, beautiful, and embeddable JavaScript Markdown editor. Delightful editing for beginners and experts alike. Features built-in autosaving and spell checking.
https://simplemde.com
MIT License
9.92k stars 1.12k forks source link

Angular support? #367

Open dleute opened 8 years ago

dleute commented 8 years ago

I was going to drop this editor into an angular project of mine, but it seems to break my app just by existing on the page. Other angular form inputs stop working and there are a number of errors. The errors are not typical angular errors with info.

Furthermore, things like ng-model can't bind to this because it creates an entirely new UI. I tried doing forceSync, but that only solves the problem one direction.

So, my question is: has anyone thought about turning this into an angular directive?

Thanks!

WesCossick commented 8 years ago

I don't use Angular, so someone would need to submit a PR to fix bugs in Angular.

sandershihacker commented 8 years ago

How can I import the module into an Angular 2 Typescript app? "import { SimpleMDE } from 'simplemde' " doesn't seem to work.

dleute commented 8 years ago

I'm not enough of a javascript or angular developer to make this angular ready. But if there is other interest, I would happily test.

several27 commented 8 years ago

@sandershihacker In order to import the module simply use import * as SimpleMDE from 'simplemde';. Then, you should be able to do normally new SimpleMDE(...).

As for support for angularjs, it doesn't break my app, but still doesn't seem to work properly.

dleute commented 8 years ago

SimpleMDE worked in my app. but all other angular stuff stopped working. Material fields are ignored after SimpleMDE loads. I wonder if material is the problem.

several27 commented 8 years ago

@dleute How did you set it up? I'm using AngularJS v1.5.7 and Bootstrap v3.3.6. Later today, I might try to install it with angular2 and material to see how it works there.

dleute commented 8 years ago

I'm on 1.5.7 and material 1.0.9. I do have some other angular libraries loaded. But basically added the SimpleMDE js and put the required code to get it working in the template. This created all kinds of non decipherable angular errors. (I don't have an error handy at the moment as I stopped trying to use SimpleMDE)

Errors usually have a link to click and a site to read about them. I didn't experiment much with it. Maybe if I put it in an isolated scope directive, whatever is fighting with it will stop it.

dleute commented 8 years ago

I am also using multiple SimpleMDE's on a single page. That may be contributing.

WesCossick commented 8 years ago

It may be helpful to use the debug script for more intelligible error messages.

several27 commented 8 years ago

Alright, I've found what was causing the problem for me. I was using the wrong css file: the one that is inside the src directory, instead of the one in debug or dist. After fixing that everything is working fine (even when using multiple editors).

Normal angular directives are not working when applied as attribute to textarea (e.g. ngChange or ngModel) but that behaviour can be fixed using simplemde.codemirror.on('change', () => { ... });.

@dleute I haven't tried with material yet, but angular seems to be fine. Be sure to include everything properly and also I'm using node-browserify, maybe that could solve your problem?

@WesCossick Debug script is not logging any errors for me and as mentioned above it's just working fine.

hansottowirtz commented 8 years ago

I made a very simple library to bind SimpleMDE to ng-model: https://github.com/hansottowirtz/simplemde-angular

Please contribute! (It's actually just a very, very simple directive)

DerekTBrown commented 8 years ago

For those on Angular2, you can import it using a regular requires statement:

https://gist.github.com/DerekTBrown/706dae91176cba97e6ef5b1cbda0e5b5

In the above example I chose to simply ignore typings. However, DefinitelyTyped has an existing .d.ts file for simplemde. Not sure how out of date it is, however:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a2c5e146eb703147870f4c8be4daabe72a7d9212/simplemde/simplemde.d.ts

I would look into adding an updated version of the simplemde.d.ts file to the repository itself, just so that it can be updated as the SimpleMDE API is updated. In addition, it would be good to have an actual module interface internal to this library to make things easier.

dae721 commented 8 years ago

@DerekTBrown thanks for posting, very helpful. I did notice a small problem. I just left a comment on your gist explaining.

As I was experimenting with this, I was having a "bang my head against the wall problem"...the editor was too tall (taller than the browser window) and it wasn't auto-resizing. I created a plunk to see if I could reproduce the problem. It worked fine there. After line-by-line comparison I finally found it. Turns out index.html in the angular2 quickstart. which I based my experiment on, doesn't have <!DOCTYPE html>. As soon as I added that, the editor behaved as expected.

In case it's helpful to anyone else, here it is: http://plnkr.co/edit/MibLSr?p=preview

trevorhreed commented 7 years ago

Here's an Angular Directive implementation that works for me:

ngm.directive('simpleMde', function(){
  return {
    restrict: 'E',
    require: '?ngModel',
    template: `<textarea></textarea>`,
    link: function(scope, element, attrs, ngModelCtrl){
      var editor = new SimpleMDE({
        element: element[0].querySelector('textarea'),
        forceSync: true,
        status: false
      });
      editor.codemirror.on('change', function(){
        ngModelCtrl && ngModelCtrl.$setViewValue(editor.codemirror.getValue());
      });
      ngModelCtrl.$render = function(){
        console.log(ngModelCtrl.$viewValue);
        editor.codemirror.setValue(ngModelCtrl.$viewValue);
      };
    }
  }
})

Usage:

<simple-mde ng-model="vm.content"></simple-mde>
IllyaMoskvin commented 7 years ago

I modified @trevorhreed's directive slightly to clear the history when the model changes. I found this useful when swapping the object to which my model points. This is quick-and-dirty, but we should likely be pushing this to @hansottowirtz's repo.

app.directive('simpleMde', function(){
    return {
        restrict: 'E',
        require: '?ngModel',
        template: `<textarea></textarea>`,
        link: function($scope, $element, $attrs, $ngModelCtrl){
            var clear = (typeof $attrs.clear !== 'undefined') && ($attrs.clear !== "false");
            var editor = new SimpleMDE({
                element: $element[0].querySelector('textarea'),
                forceSync: true,
                status: false
            });
            editor.codemirror.on('change', function(){
                $ngModelCtrl && $ngModelCtrl.$setViewValue(editor.codemirror.getValue());
            });
            $ngModelCtrl.$render = function(){
                editor.codemirror.setValue($ngModelCtrl.$viewValue || '');
                clear && editor.codemirror.clearHistory();
                $ngModelCtrl.$setPristine();
            };
        }
    }
});

Usage:

<!-- History will be preserved when the model changes -->
<simple-mde ng-model="vm.content"></simple-mde>
<simple-mde ng-model="vm.content" clear="false"></simple-mde>

<!-- History will be reset when the model changes -->
<simple-mde ng-model="vm.content" clear></simple-mde>
<simple-mde ng-model="vm.content" clear="true"></simple-mde>

Edit: Added fallback to empty string if the model value is null.

hantsy commented 7 years ago

@several27 I am using SimpleMDE in my Angular project which is using Webpack/ES6,

After install simplemde by npm:

npm install --save simplemde

When I import it in file by:

import * as SimpleMDE from 'simplemde/src/js/simplemde';

or

import * as SimpleMDE from 'simplemde';

And use new SimpleMDE(options), and get error in Chrome console.

Uncaught ReferenceError: SimpleMDE is not defined
hantsy commented 7 years ago

@WesCossick I have added post on stackoverflow, http://stackoverflow.com/questions/41506841/how-to-import-simplemde-in-es6, can you help me? Thanks

DerekTBrown commented 7 years ago

@hantsy https://gist.github.com/DerekTBrown/706dae91176cba97e6ef5b1cbda0e5b5

doxiaodong commented 7 years ago

https://github.com/doxiaodong/ng2-simplemde hope helpful

DerekTBrown commented 7 years ago

@doxiaodong beautiful

DerekTBrown commented 7 years ago

move to close

IllyaMoskvin commented 7 years ago

Would @WesCossick be down for including a small section in the readme that says something to the effect of "AngularJS and Angular 2 are not officially supported (#367), but here are some resources and repositories to get you started:" with a list of links? I'm not sure what impression that gives, but it might be helpful. Just throwing the thought out there.

dae721 commented 7 years ago

Just added a new issue, #558, which I thought I'd mention here in case it helps anyone else. As explained in the issue, simplemde adds a keydown event listener to document, which is never removed. Since our app creates new simplemde instances each time the user navigates to a new item, many listeners are registered, and eventually typing becomes very slow, especially in IE and Edge.

I've worked around this in two ways. First, I create simplemde to run outside angular's zone:

        this.ngZone.runOutsideAngular(() => {
            // code which configures and creates SimpleMDE
        });

This helped immensely. As I understand it this will prevent change detection, which we don't need anyway.

And second, until this gets fixed, I added a complete hack (surprised that it even works), to temporarily replace document.addEventListener with a function that does nothing. When calling new SimpleMDEEx(options), the hijacked addEventListener gets called. Then I immediately put the original addEventlistener back. In this way, the keydown event listener never gets added. The only downside is that Esc to exit full screen mode no longer works if the editor doesn't have focus. Not a big deal for me.

I just mention this since it seems like others may have the same problem (and if not, maybe that's an indication I'm doing something wrong!).

ghost commented 7 years ago

In case anyone is trying to implement this on Angular 2 or 4 this worked for me, I also integrated ngModel

These are my file definitions

// mdeditor.ts
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';

const SimpleMDE: any = require('simplemde');

@Component({
  selector: 'app-mdeditor',
  templateUrl: './mdeditor.html',
})
export class MDEditorComponent implements AfterViewInit {
  @Input() model: any;
  @Output() modelChange = new EventEmitter<string>();
  @ViewChild('simplemde') textarea: ElementRef;

  constructor(private elementRef: ElementRef) { }

  ngAfterViewInit() {
    const modelChange = this.modelChange;
    const mde = new SimpleMDE({
      element: this.textarea.nativeElement,
      forceSync: true,
      status: false
    });

    mde.codemirror.on('change', function() {
      const value = mde.codemirror.getValue();
      modelChange.emit(value);
    });

    if (this.model) {
      mde.codemirror.setValue(this.model);
    }
  }
}
// mdeditor.html
<textarea [(ngModel)]="model" name="markdowneditor" #simplemde></textarea>

And this is how you use it

<app-mdeditor [(model)]="yourModel"></app-mdeditor>

Thanks to https://gist.github.com/DerekTBrown/706dae91176cba97e6ef5b1cbda0e5b5 and https://github.com/sparksuite/simplemde-markdown-editor/issues/367#issuecomment-262341397