Seeking advice on WYSIWYG architecture


#1

I’m having a hard time coming up with a solution for this problem.

I’m building a WYSIWYG designer, for micro sites. The templates for these microsites will be supplied by an intermediate user, and the end-user will manipulate these templates. So, there are really two groups of users of the app: template-buiders, and end-users. Think MailChimp.

This means my Ember app will start off with a template from a template-builder, say

<h1>An awesome product</h1>
<h2 contenteditable="true">Subtitle away</h2>
<p>{{#if optionA}} One thing {{else}} Another thing {{/if}}<p>

and the end user, having chosen this template, will then be able to customize it. There are a few requirements:

  • There will be static, uneditable portions of the page (h1 above)
  • There will be static, editable portions of the page (h2 above)
  • There will be options that affect the layout, style, etc. (p above)

So far, my attempts have lead me to build a handlebars helper that takes a string and a context, and returns a rendered template. This is because the above template will actually be coming over from a database, as a string - remember, it’s user-generated.

That means, in my application’s template, it would look like

<div class="preview">
  {{preview-userTemplate template context}}}
</div>

where

template: '<h1>An awesome product</h1><h2 contenteditable="true">Subtitle action</h2><p>{{#if optionA}} One thing {{else}} Another thing {{/if}}<p>',
context: {optionA: true}

Now, this actually works, in the sense that it will update if context is updated, so I can add controls along the sides for the end-user to manipulate those options. So I have those (more of less) under control.

It’s the WYSIWYG content editing that is giving me trouble. In the above template (from my template-builder users), I want them to be able to add contenteditable="true" to places in their templates where the end-users can change the content.

Now, these changes need to be saved. Ideally, I would be able to retrieve the instance view’s template back from the DOM, after the inline edits have been made. Is this possible? The original {{data}} placeholder would need to be preserved.

Alternatively, I could make a view/component, so the template-builder would do something like this:

<h1>An awesome product</h1>
{{#editable}}
  <h2>Subtitle away</h2>
{{/editable}}
<p>{{#if optionA}} One thing {{else}} Another thing {{/if}}<p>

but this seems like it would require a good deal of finagling to get the edits to stick - I’m not even sure right now how I’d go about it.

What do you guys think? Is there an easier solution that I’m overlooking? Thanks for any pointers!


#2

I’ve spent the last month on a similar (though actually much more complicated) problem. I’ve had moderate success with a two-way binding and re-rendering the component’s template (which automatically happens when the underlying observed value is changed - the only issue is you have to track the caret and recreate it again on didInsert). Each keypress modifies the binding, and then the bound content in the template updates. I have a feeling HTMLBars will make the process of building these types of controls much simpler and better, but that’s only my guess. At least, I hope so :wink: The promise of specialised custom textareas using contenteditable mode is very alluring :slight_smile: (and at present is a complete waking nightmare, at least it has been for me).

Let me just say: “metamorphtags are fun” :wink:


#3

Yeah, you can’t really use data-content type of stuff by declaring it on your handlebars helpers. It’s not setup to work that way. Instead you’re going to have to use attributeBindings. There’s a disclaimer somewhere in the Guides about this.


#4

@JulianLeviston Thanks for the advice, maybe I’ll start working with a somewhat bloated approach, and not worry too much about making it super clean, in anticipation of HTMLBars. So, it seems you went with a separate component for each editable region, and bound the content to a property?

@ulisesrmzroche this is what I was afraid of, was hoping my template-builders wouldn’t have to do something like {{content-editable value=data}}, but maybe it’s not such a huge deal for now. Perhaps once I get going a better solution will emerge. It’s hard to think about all this in the abstract: using templates to build a template-building application for end-users to customize templates. It’s kinda tough.


#5

Does this help out? http://emberjs.com/guides/templates/binding-element-attributes/#toc_adding-data-attributes


#6

Not quite. My ultimate goal is for my template-builders to give my app something like this:

<p contenteditable="true">Something awesome</p>
{{#if optionA}}
  <p>Another thing</p>
{{/if}}

and for the end users to be able to not only change optionA (which I know how to implement), but also the “Something awesome” text itself, via a WYSIWYG interface.

If the template builders gave me something like

<p>{{headingText}}</p>
{{#if optionA}}
  <p>Another thing</p>
{{/if}}

I could come up with an implementation, but it makes the experience less pleasant for the template-builders.


#7

Actually I went with the “render it all in one component” approach. The pain comes from getting Ember to re-render its metamorph tags that wrapper each individual component, and tracking the constituent elements individually. I’ve gone down several approaches over the last month… the most recent one has taken a relatively long time because I didn’t have sufficient test harnesses in place and I’m having to do things like have observers of particular properties that redefine other properties (similarly to the way the Ember.Select control works).

My app currently has the approach you mention but it has some annoying real world issues (such as pasting or copying large blocks of text), or the fact that sections of the content can’t be styled differently. The thing I’ve most recently been working on is the “next version” of this editor, basically.

You have the constraint that you’re parsing your database data in and out, which I thankfully don’t have. If I was doing what you’re doing, I’d probably implement it by having clickable portions of the page that turn into editors as you click on them. That’d save you a lot of headaches. The moment you start to edit contenteditable divs which have parts of their content as HTML that are also rendered by Ember, you get into some problems, because of the metamorphs. If they become munged, everything goes haywire.


#8

Oh… So you’re going to be getting your template builders to give you handlebars to be evaluated within your app’s rendering context!

That seems a bit like it might be a bit of a huge difficulty security-wise. I suppose if you gave them a locked down evaluation context it’d be okay… still…

Interesting!


#9

It’s about the innerText of the p tag. Thats why it’s not easily observable by Ember.


#10

@ulisesrmzroche exactly.

So @JulianLeviston you’re right about security, but the template-builders are actually internal. This app itself won’t be used by template-builders; the app will consume templates created by the template-builders, which will all come from a secure, internal database.

Think of MailChimp. You the user choose a template, and make modifications, and then send it out as your email campaign. But there’s employees of MailChimp who make the starter templates, to get you going. It’s the same thing here.


#11

@JulianLeviston this sounds like an absolute nightmare, and I definitely want to avoid it.

Because the templates that are coming in (from the template-builders) are internal, it actually is okay (but not ideal) if the template-building experience is a little rough for them. So while it may be nicer for them to give me a template with

<p contenteditable="true">Some text</p>

it may make everything much, much simpler if they give me a template with

{{content-editable tag="p" value=bodyContentA}}

Then I should be able to make a ContentEditableComponent that takes care of the WYSIWIG interface. Also, all the data will be cleanly separated from the template (the true template, the parts that don’t change), and the back-end would be able to render everything together to create a static site - which is the ultimate output of the whole process.


#12

Just to be clear, there’s no way to fetch a view’s template from the live DOM, correct? For example, if

<div id="ember140">
  ...
</div>

was a view that had metamorphs in it, I can’t do anything like

Em.$('#ember140').template()

to get back the handlebars template, based on the current DOM? In my mind this would be ideal, as it would allow me to let the user edit the static pieces of the template.


#13

Ah! haha okay… I imagined a person when you were saying template builders. :wink: But you were thinking of Objects. Understood now :slight_smile:

I think the thing about MailChimp, though, is that they have their own templating language… I think perhaps if you’re providing this to people to modify, you need to build yourself a mini-API (ie templating language) - even if you’re the only one consuming it at first.

I’m pretty sure you can’t fetch the template from the DOM. The reason I recommended having an edit mode toggle for each section of HTML is that you can track that internally against all events (basically hook up event trackers on your component, and keep track of which context the user is in, and every time they do something - ie create an event - simply update your internal representation of that particular object). This last technique is what I do to maintain state from browser (ie user) to objects in my system. So long as you have no metamorphs within that content editable, you’ll be fine. Hell, you could even turn all editable sections of the page on at once, and track them all. The user only ever has one focus (ie selection point), so you’d just have to keep track of which particular one they were, then let all key events track content updating on that particular object.

The only reason my thing is a nightmare is because I want a seamless open contenteditable div which they can do things like delete text across object boundaries, paste new object boundaries in, and generally do whatever they like, maintaining each “paragraph” as a separate object in my system. Fun :slight_smile:

Hey thanks very much for starting this convo. It’s nice to know I’m not alone in the WYSIWYG ember space :slight_smile: Who knows, maybe some day we can help each other build some nice WYSIWYG editor into Ember proper or some plugin so people don’t have to go through this frustration again. :smile:


#14

Interesting, thanks again for sharing your thoughts. So it seems like you are mostly implementing all the data syncing manually - am I right? Not using Ember’s data bindings to keep the data representation in sync with what’s rendered for the user?

And just to clarify, I did mean template-builders were people. So, I’m building this for (say) MailChimp. A MailChimp employee is a template builder: they’ll make pre-built templates for the system, say a 1-column, 2-column and 3-column template. The end-user will then start a new campaign, and choose one of those three templates to start out with. They’ll then be brought to a customization step, where they can change options specific to those templates, as well as change the copy via a WYSIWYG interface. So both template-builders and end-users are people, it’s just that the template-builders are all “internal” to the app; their experience isn’t actually as important as that of the end-user.

We absolutely should continue to collaborate! My thinking right now is leading me towards building a ContentEditable component. As soon as I get a proof of concept, I’ll share it with you and see what you think.


#15

bump!
@samselikoff, did you build a contenteditable component? If so, I’d love to have a look at it.


#16

I ended up doing something like this.ckeditor() in the view’s didInsertElement, and manually syncing the data across the iframe boundaries. Not too interesting, but my use case was pretty peculiar.

I saw an Ember Redactor component floating around a while back, you may want to check that one out.