【问题标题】:How to organize a Javascript UI?如何组织 Javascript UI?
【发布时间】:2009-10-16 09:51:49
【问题描述】:

我需要有关程序架构的良好示例和最佳实践。

我正在尝试为适用于 Google.Maps 的应用构建 JS UI。在第一稿中,用户应该能够以类似于 G.M. 的方式在地图上绘制几何形状。然后通过 AJAX 发送形状并显示响应。

问题是代码变得复杂,只是编辑多边形。

受 Joel 的“Duct-tape Programmer”的启发,我尝试编写一个简单的代码来生成操作和切换事件处理程序,以避免出现大的 if-else 树。 “新多边形”按钮为 map.onclick 创建一个观察者,更改其他按钮的事件处理程序或隐藏它们,并隐藏自身等。

这种方法的缺点是数据处理代码与接口混合在一起。创建 div 容器以在新多边形上显示数据的代码位于处理 w/ G.M 或 w/ 形状数据的代码旁边。如果我想修改 UI,我需要处理整个应用程序。

我可以稍后查看它并将这个生成 UI 的代码移到其他地方,但后来我的首席程序员来了。相反,他坚持使用“消息传递”方法:一个简单的事件系统,其中对象订阅事件并触发它们。接口代码可以与数据处理和与 G.M 对话完美隔离,但现在每个侦听器都必须仔细检查此消息是否发给它。

例如,单击地图上的多边形必须突出显示它并开始编辑。但如果正在绘制另一个多边形,则不会。所以,你在和我说话吗?代码无处不在。

我会欣赏 UI 架构方法的优秀示例。

【问题讨论】:

    标签: javascript


    【解决方案1】:

    向您建议的事件处理思路是一个很好的方法。

    这里还有一些想法:

    • 将形状绘制的东西变成一个组件
    • 形状绘制组件向其他代码发送事件,以对“编辑”或“点击”等内容做出反应
    • 该组件还可以处理编辑部分 - 它向控制器发送“点击”事件,控制器告诉组件进入编辑模式
    • 在编辑模式下,组件在编辑关闭之前不会发送正常的“点击”事件,避免了您必须检查的问题

    一般来说,最好有一个“视图”层,它只处理显示数据并将有关用户对该数据的操作(即点击等)的事件发送到“控制器”层,然后决定做什么 - 例如,它可以决定将视图更改为编辑模式。

    【讨论】:

      【解决方案2】:

      我不知道这是否题外话。但我把它当作我所有 javascript 项目的圣殿。

      (function () {
      var window = this,
          $ = jQuery,
          controller,
          view,
          model;
      
      controller = {
          addEventForMenu : function () {
              // Add event function for menu
          }
      };
      
      view = {
          content : {
              doStuff : function () {
      
              }
          },
      
          menu : {
              openMenuItem : function () {
      
              }
          }
      };
      
      model = {
          data :  {
              makeJson : function () {
                  // make json of string
              },
      
              doAjax : function () {
      
              },
      
              handleResponse : function () {
      
              }
          }
      }
      
      $.extend(true, $.view, view);
      })();
      

      这里的好处是它只是将视图对象扩展到了 DOM,其余的都保存在匿名范围内。

      同样在错误项目中,我为每个部分创建这些文件,即 map.js、content.js、editor.js

      如果您只是介意视图对象中方法的命名,您可以在开发过程中拥有任意数量的文件。当项目设置到生产环境中时,我只需将其制作为一个文件并缩小它。

      ..弗雷德里克

      【讨论】:

        【解决方案3】:

        简而言之,发布者-订阅者范式可以很好地制作几何形状。首先制作基本多边形发布者发布的命令行。 Canvas 对象在这里绘制似乎很明显,通常方法 repaint() 用于更新客户端视图(通常在 C 中进行事件驱动编程,您可以查看例如 opengl 或 glut 事件驱动图形),结合我也使用的一般 gmap API,发布者 - 订阅者无论图形实现如何,模式或工厂都是很好的设计模式。棘手的 gmaps 具体事情是 json 和持久层之间数组中的纬度和经度切换位置,还没有服务器端反向地理编码,命名有点不一致,并且对于多语言应用程序的名称都改变了相对的用户人类语言并且加倍(Paris in Text,法国巴黎...),.看看你是否喜欢我的实现,使用 geoip worldwide 注册地理名称和空间协调的相对用户

        function wAdd(response){
        map.clearOverlays();
        if(!response||response.Status.code!=200){
        
        }
        else{
            try{
                place=response.Placemark[0];
                point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
                marker=new GMarker(point);
                map.addOverlay(marker);
                marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.AdministrativeArea.Locality.LocalityName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
            }
            catch(e){
                try{
                    place=response.Placemark[0];
                    point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
                    marker=new GMarker(point);
                    map.addOverlay(marker);
                    marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
                } 
                catch(e){
        try {
                place=response.Placemark[0];
                        point=new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
                        marker=new GMarker(point);
                        map.addOverlay(marker); 
                        marker.openInfoWindowHtml('<a href="/li?lat='+place.Point.coordinates[1]+'&lon='+place.Point.coordinates[0]+'&cc='+place.AddressDetails.Country.CountryNameCode+'">'+place.AddressDetails.Country.CountryName+'<span id="wr2"></span> '+ nads( place.Point.coordinates[1],place.Point.coordinates[0] )+' ' +'<img src="http://geoip.wtanaka.com/flag/'+place.AddressDetails.Country.CountryNameCode.toLowerCase()+'.gif">');
                    }
           catch(e){ 
        place=response.Placemark[0];
              marker=new GMarker(point);
                        map.addOverlay(marker);
        marker.openInfoWindowHtml('<a href="/li">'+place.address+'</a>');
        }
        
                } }
            }map.addOverlay(geoXml);
        }
        

        【讨论】:

          【解决方案4】:

          我建议使用一些包含状态的对象变量(0、绘图、编辑……任何其他需要)——这将帮助您决定是允许事件处理还是仅仅在绘图完成并单击时将其掩埋在可编辑的多边形上发生。

          至于 UI - 我不确定您的问题是针对您的 - 开发脚本还是针对用户,因为您在这里混合了两件事。

          请记住,对于用户而言,一切都应尽可能简单:如果他正在编辑,则不要让他画画。如果他正在绘图,请不要让他编辑(可能会发生多边形重叠)。但是 - 允许用户快速从编辑(例如右键单击?)切换到绘图 - 或者换句话说,取消当前状态。

          【讨论】:

            【解决方案5】:

            我要做的第一件事是创建一个覆盖 google API 的服务。这是为了以后如果您需要更改地图服务(Windows 地图或雅虎地图)。然后你可以在google service 上放置一个外观。然后您可能希望在您的服务上放置一些包装器并将其拆分为视图(输出)和模型(输入),并使用控制器/演示器进行管理。查看维基百科上的模型视图控制器/模型视图演示者/演示者优先/谦虚对话框。它应该讨论您正在寻找的分离。 Martin Fowler 网页也进入了演示模式。你应该看看我的旧博客ugly-lisp-code。我参考了事件驱动/事件消息传递。

            如果您有一个一对一的发布/订阅,只需在要触发事件的对象中存储一个事件处理程序(闭​​包/lambda/一阶函数)。

            如果您有一个一对多的 pub/sub,那么您将需要一个更复杂的对象来存储您的闭包。

            哈哈!现在我一直在研究同样的问题。我将在JavaScript on my blog 中写有关使用演示者优先的文章。 presentermodel 开始。

            [编辑] 你可能想看看这个stackoverflow question。答案之一与将关注点分离到 MVC 中有关。链接在 A List Apart 上。

            【讨论】:

              猜你喜欢
              • 2018-11-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-01-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多