骨干,javascript mvc - 使用 javascript 设计视图

发布于 2024-11-15 08:38:43 字数 1591 浏览 3 评论 0原文

我的一些视图需要将其文本区域转换为富文本编辑器。

我使用 jwysiwyg 作为编辑器。它要求当编辑器初始化时,它所附加的元素位于页面中,即当我调用 $(this.el).wysiwyg() 时,this.el 已经在文档中。

我的大多数视图实际上并没有将自己附加到 dom - 它们的渲染方法只是使用应用程序模板引擎设置它们的元素 html 内容,例如 $(this.el).html(this.template(content)

视图/控制器同时,当视图的模型发生变化时,

我如何确保编辑器在每次渲染时都附加到元素上,并且仍然确保编辑器是有效的。直到元素已经在显然

我可以将一些在这种特殊情况下工作的东西组合在一起,但我想要一个适用于所有情况的优雅的解决方案,

我们将不胜感激

编辑:这里的要点是该解决方案必须能够优雅地扩展。覆盖必须在渲染后设置样式的多个元素,并且在它们位于 DOM 中之前不得设置

样式编辑:如果我进行自顶向下渲染,这不是问题,但这很慢,我想要一个可以从以下位置渲染的解决方案从下往上,然后一次性插入完整视图顶部

编辑:

结合使用下面建议的一些技术,我正在考虑做如下的事情。任何意见/批评将不胜感激。

app/views/base_view.js:

initialize: function() {
  // wrap the render function to trigger 'render' events
  this.render = _.wrap(this.render, function() {
    this.trigger('render')
  });

  // bind this view to 'attach' events. 
  // 'attach' events must be triggered manually on any view being inserted into the dom
  this.bind('attach', function() {
    this.attached();
    // the refreshed event is only attached to the render event after the view has been attached
    this.bind('render', this.refreshed())
    // each view must keep a record of its child views and propagate the 'attach' events
    _.each(this.childViews, function(view) {
      view.trigger('attach')
    })
  })
}

// called when the view is first inserted to the dom
attached: function() {
  this.style();
}

// called if the view renders after it has been inserted
refreshed: function() {
  this.style();
}

style: function() {
  // default styling here, override or extend for custom
}

A few of my views need their textareas converted to rich text editors.

I'm using jwysiwyg as the editor. It requires that the element it is being attached to is in the page when the editor is initialized i.e. when I call $(this.el).wysiwyg(), this.el is already in the document.

Most of my views do not actually attach themselves to the dom - their render methods simply set their elements html content using the apps templating engine e.g. $(this.el).html(this.template(content)

Views/Controllers further up the chain look after actually inserting these child views into the page. At the same time, views do re-render themselves when their models change.

How do I ensure that the editor is attached to the element every time its rendered and still ensure that the editor is not attached until the element is already in the page?

Obviously I could hack something together that would work in this particular case but I would like an elegant solution that will work for all cases.

Any help would be much appreciated.

Edit: The main point here is that the solution must scale gracefully to cover multiple elements that must be styled after rendering and must not be styled until they are in the DOM

Edit: This is not an issue if I do top-down rendering but this is slow, I'd like a solution whereby I can render from the bottom up and then insert the complete view in one go at the top

Edit:

Using a combination of some of the techniques suggested below I'm thinking of doing something like the following. Any comments/critique would be appreciated.

app/views/base_view.js:

initialize: function() {
  // wrap the render function to trigger 'render' events
  this.render = _.wrap(this.render, function() {
    this.trigger('render')
  });

  // bind this view to 'attach' events. 
  // 'attach' events must be triggered manually on any view being inserted into the dom
  this.bind('attach', function() {
    this.attached();
    // the refreshed event is only attached to the render event after the view has been attached
    this.bind('render', this.refreshed())
    // each view must keep a record of its child views and propagate the 'attach' events
    _.each(this.childViews, function(view) {
      view.trigger('attach')
    })
  })
}

// called when the view is first inserted to the dom
attached: function() {
  this.style();
}

// called if the view renders after it has been inserted
refreshed: function() {
  this.style();
}

style: function() {
  // default styling here, override or extend for custom
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

沉睡月亮 2024-11-22 08:38:43

如果您使用 JQuery LiveQuery 插件 附加编辑器会怎么样?此类代码可以是模板代码的一部分,但不是作为 HTML,而是作为与模板关联的 Javascript。或者您可以在全球范围内添加它。代码可能如下所示(假设您已包含插件本身):

$('textarea.wysiwyg').livequery(function() {
   $(this).wysiwyg();
});

我尚未测试此代码,但理论上,当它出现在 DOM 中时,它应该与带有“wysiwyg”类的 textarea 元素的实例相匹配,并且调用 wysiwyg 函数来应用编辑器。

What if you used the JQuery LiveQuery Plugin to attach the editor? Such code could be a part of your template code, but not as HTML, but as Javascript associated with the template. Or you could add this globally. The code might look like this (assuming you've included the plugin itself):

$('textarea.wysiwyg').livequery(function() {
   $(this).wysiwyg();
});

I have not tested this code, but in theory it should match an instance of a textarea element with a class of 'wysiwyg' when it appears in the DOM and call the wysiwyg function to apply the editor.

战皆罪 2024-11-22 08:38:43

为了遵守 DRY 原则并获得优雅的解决方案,您需要一个专门用于确定文本区域是否具有所见即所得的函数,例如 wysiwygAdder 并在必要时添加它。然后,您可以使用 underscore 的 wrap 函数将所见即所得加法器附加到渲染函数的末尾。

var View = Backbone.View.extend({
    el: '#somewhere',

    initialize: function(){
        _.bind(this, "render");
        this.render = _.wrap(this.render, wysiwygAdder);
    },

    render: function(){
        //Do your regular templating
        return this;//allows wysiwygAdder to pick up context
    }
});

function wysiwygAdder(context){
    $('textarea', context).doYourStuff();
    //check for presence of WYSIWYG, add here
}

当视图初始化时,它会用你的渲染函数覆盖你的渲染函数,然后是wysiwygAdder。确保从渲染中return this;以提供上下文。

To adhere to DRY principle and get an elegant solution, you'll want a function dedicated to determining if a textarea has wysiwyg, let's say wysiwygAdder and add it if necessary. Then you can use underscore's wrap function to append your wysiwyg adder to the end of the render function.

var View = Backbone.View.extend({
    el: '#somewhere',

    initialize: function(){
        _.bind(this, "render");
        this.render = _.wrap(this.render, wysiwygAdder);
    },

    render: function(){
        //Do your regular templating
        return this;//allows wysiwygAdder to pick up context
    }
});

function wysiwygAdder(context){
    $('textarea', context).doYourStuff();
    //check for presence of WYSIWYG, add here
}

When the view is initialized, it overwrites your render function with your render function, followed by wysiwygAdder. Make sure to return this; from render to provide context.

岁月苍老的讽刺 2024-11-22 08:38:43

一种解决方案是使用 事件委托 并绑定焦点事件来检查富文本编辑器是否已被激活加载或未加载。这样,用户将在需要时获得文本编辑器(通过延迟加载,一个小的性能改进),否则您不必加载它。它还将消除需要担心何时附加富文本编辑器以及依赖于渲染链的问题。

如果您担心 FOUC(未样式化内容的闪烁),您可以简单地设置未修改的文本区域的样式以包含具有背景图像的元素,该元素看起来就像所见即所得控件,并让您的焦点绑定切换一个类以隐藏富文本编辑器接管后的外观。

这是我的想法的一个示例:

var View = Backbone.View.extend({
  el: '#thing',
  template: _.template($("#template").html()),

  render: function() {
    // render me
    $(this.el).html(this.template(context));

    // setup my textareas to launch a rich text area and hide the facade
    $(this.el).delegate('focus', 'textarea', function() {
      if(!$(this).hasRichTextEditor()) { // pseudocode
        $(this).wysiwyg();
        $(this).find('.facade').toggle();
      }
    });

  }
});

One solution would be to use event delegation and bind the focus event to check whether the rich text editor had been loaded or not. That way the user would get the text editor when they needed it (via the lazy loading, a minor performance improvement) and you wouldn't have to load it otherwise. It would also eliminate needing to worry about when to attach the rich text editor and that being dependent on the rendering chain.

If you're worried about the FOUC (flash of unstyled content) you could simply style the un-modified text areas to contain an element with a background image the looked just like the wysiwyg controls and have your focus binding toggle a class to hide the facade once the rich text editor had taken over.

Here's a sample of what I had in mind:

var View = Backbone.View.extend({
  el: '#thing',
  template: _.template($("#template").html()),

  render: function() {
    // render me
    $(this.el).html(this.template(context));

    // setup my textareas to launch a rich text area and hide the facade
    $(this.el).delegate('focus', 'textarea', function() {
      if(!$(this).hasRichTextEditor()) { // pseudocode
        $(this).wysiwyg();
        $(this).find('.facade').toggle();
      }
    });

  }
});
明月松间行 2024-11-22 08:38:43

好问题要解决!不太确定我已经掌握了全部要点,但是...您也许可以摆脱左侧的“construction_yard”(我刚刚提出这个术语),在那里构建和放置物品,然后移动当它们准备好放置时。大致如下:

.construction_yard {
    position: absolute;
    left: -10000000000px;
}

此解决方案可以解决可能出现的几个问题。例如,“隐藏”的东西上的 jquery 高度和宽度属性为 0,因此如果您按照这些方式设计样式,则必须等到它被放置为止,这更复杂,并且会使事情变得混乱。

然后你的视图需要按照(伪代码)做一些事情:

//parent
//do all your view-y stuff...
foreach (child = this.my_kids) {
  if child.is_ready() {
    put_that_child_in_its_place();
  }
}

同样,对于孩子来说,你会做类似的事情:

//child
foreach(parent = this.my_parents) {
  if parent.isnt_ready() {
    this.go_play_in_construction_yard();
  } else {
    this.go_to_parents_house();
  }
}

...而且,由于主干很容易扩展,你可以将它包裹起来一个更通用的类使用:

var ParentsAndChildrenView = Backbone.View.extend({blah: blah});

然后

var Wsywig = ParentsAndChildrenView.extend({blah: blah});

希望有帮助!

差点忘了注明我的来源:
http://jqueryui.com/demos/tabs/#...my_slider.2C_Google_Map.2C_sIFR_etc._not_work_when_placed_in_a_hidden_​​.28inactive.29_tab.3F

Great problem to solve! Not too sure I've got the entire jist but... You may be able to get away with a 'construction_yard' (I just made that term up) that's way off to the left, build and place items there, then just move them when they're ready to be placed. Something along the lines of:

.construction_yard {
    position: absolute;
    left: -10000000000px;
}

This solution may fix several problems that might crop up. For example jquery height and width attributes on something that's 'hidden' are 0, so if you are styling along those lines, you'd have to wait till it was placed, which is more complicated, and jumbles things up.

your views would then need to do something along the lines of (pseudo-code):

//parent
//do all your view-y stuff...
foreach (child = this.my_kids) {
  if child.is_ready() {
    put_that_child_in_its_place();
  }
}

Similarly, for children, you'd do a similar thing:

//child
foreach(parent = this.my_parents) {
  if parent.isnt_ready() {
    this.go_play_in_construction_yard();
  } else {
    this.go_to_parents_house();
  }
}

... and, since backbone is pretty easy to extend, you could wrap it up in a more generalized class using:

var ParentsAndChildrenView = Backbone.View.extend({blah: blah});

and then

var Wsywig = ParentsAndChildrenView.extend({blah: blah});

hope that helps!

Almost forgot to note my source:
http://jqueryui.com/demos/tabs/#...my_slider.2C_Google_Map.2C_sIFR_etc._not_work_when_placed_in_a_hidden_.28inactive.29_tab.3F

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文