10

Trying to implement a textarea component with emoticons support while writing.

I want to be able to backup the original text (ascii chars only) while presenting the filtered/generated html outcome (with an angular emoticons filter) on a div.

My initial solution is to

<textarea ng-model="text" ng-change="..." ng-focus="..."></textarea>
<div ng-bind-html="text | myEmoticonsFilter"></div>

but I'm having trouble getting to the part of using a hidden textarea. Also, with this I wouldn't be able to mouse-select text and delete or copy/paste safely.

I also thought of using a <div contenteditable="true"> but ng-focus and ng-change wouldn't be handled.

Does anyone have any sugestion on how to continue this?

Edit 1: here is a jsfiddle with an attempt on what I'm doing. Up until now, able to replace the first occurrence, but the behavior remains erratic since that. I'm using a contenteditable directive for 2-way data binding and to filter the emoticon pattern.

Edit 2: regarding my statement saying that ng-focus and ng-change wouldn't be handled, that is not true - ng-focus works natively on <div contenteditable="true"> and ng-change will work as long as a directive is declared using the ngModel and setting the appropriate $modelValue and $viewValue (an example is provided in the jsfiddle in Edit 1).

nuno
  • 1,771
  • 1
  • 19
  • 48
  • I don't have the answer to your question, but you can add change/focus events to contenteditable by using the code provided in [this example](https://docs.angularjs.org/api/ng/type/ngModel.NgModelController#custom-control-example) from the official Angular documentation – pasine Oct 19 '15 at 13:45
  • I believe the solution will be meddling directly with `window.getSelection` and `Range` objects when a Node is physically altered in the `contenteditable` – nuno Oct 19 '15 at 14:40
  • What about using bower dependency https://github.com/globaldev/angular-emoji-filter It is not actually same mathod you want to use, but on the other hand that is a simple and also powerfull solution. If you need custom icons than you sholud consider doing it different. – Miha2255 Oct 20 '15 at 20:01
  • @Miha2255 my problem is not the emoticons filter itself - my problem is making any kind of replacement on a `contenteditable` div. The `scope.$apply(...)` and the html replacement messes with cursor positioning. – nuno Oct 20 '15 at 21:52

1 Answers1

5

The only way to do this in a consistently cross-browser manner is to use a WYSIWYG field that converts emoji to images.

There's a jQuery plugin jquery-emojiarea that does what you need, so you'd just need to create a directive that wraps this plugin and you're off to the races. Since it inputs into a hidden textarea with emoji syntax :smile: angular should have no difficulty binding.

Here's a working directive I threw together. http://jsfiddle.net/dboskovic/g8x8xs2t/

var app = angular.module('app', []);
app.controller('BaseController', function ($scope) {
    $scope.text = 'This is pretty awesome. :smile: :laughing:';
});
app.directive('emojiInput', function ($timeout) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function ($scope, $el, $attr, ngModel) {
            $.emojiarea.path = 'https://s3-us-west-1.amazonaws.com/dboskovic/jquery-emojiarea-master/packs/basic';
            $.emojiarea.icons = {
                ':smile:': 'smile.png',
                ':angry:': 'angry.png',
                ':flushed:': 'flushed.png',
                ':neckbeard:': 'neckbeard.png',
                 ':laughing:': 'laughing.png'
            };
            var options = $scope.$eval($attr.emojiInput);
            var $wysiwyg = $($el[0]).emojiarea(options);
            $wysiwyg.on('change', function () {
                ngModel.$setViewValue($wysiwyg.val());
                $scope.$apply();
            });
            ngModel.$formatters.push(function (data) {
                // emojiarea doesn't have a proper destroy :( so we have to remove and rebuild
                $wysiwyg.siblings('.emoji-wysiwyg-editor, .emoji-button').remove();
                $timeout(function () {
                    $wysiwyg.emojiarea(options);
                }, 0);
                return data;
            });
        }
    };
});

And usage:

<textarea ng-model="text" emoji-input="{buttonLabel:'Insert Emoji',wysiwyg:true}"></textarea>

If you want the editable field to convert text like :( as you type you'll need to fork that jquery plugin and modify it slightly to parse input text on change as well as on init. (like, a couple lines of code)

David Boskovic
  • 1,469
  • 9
  • 14
  • Thanks David, I actually went this way using the [`jquery-emojiarea`](https://github.com/diy/jquery-emojiarea) and based a few of my decisions in [angular-emoji-popup](https://github.com/Coraza/angular-emoji-popup) code, which is in its turn uses `jquery-emojiarea` – nuno Oct 22 '15 at 08:33
  • However I had to implement my own code because I want custom emojis and menu. Regarding changes *while* typing, I had to implement event handlers on `keyup` to process the surroundings words for the current caret position with [`Range API`](https://developer.mozilla.org/en-US/docs/Web/API/Range) – nuno Oct 22 '15 at 08:36
  • 1
    Interesting! @nuno if you want to collaborate on turning that into an angular directive for the rest of the world, I'd be happy to help! – David Boskovic Oct 22 '15 at 09:05