草庐IT

javascript - 在哪一点上获取 View 所依赖的主干模型?

coder 2025-04-02 原文

我似乎经常遇到这样的情况:我正在渲染一个 View ,但该 View 所依赖的模型尚未加载。大多数情况下,我只有从 URL 中获取的模型 ID,例如对于假设的市场应用程序,用户使用该 URL 登陆该应用程序:
http://example.org/#/products/product0
在我的 ProductView , 我创建了一个 ProductModel并设置其 ID,product0然后我 fetch() .我用占位符渲染一次,当获取完成时,我重新渲染。但我希望有更好的方法。

在渲染任何东西之前等待模型加载感觉没有响应。重新渲染会导致闪烁,并且在各处添加“正在加载...请稍候”或微调器会使 View 模板变得非常复杂(特别是如果由于模型不存在而导致模型获取失败,或者用户无权查看这一页)。

那么,当您还没有模型时,渲染 View 的正确方法是什么?

我需要离开吗
来自标签 View 并使用 pushState ?服务器可以给我推一下吗?我都是耳朵。

从已经加载的页面加载:

我觉得当已经加载了一个页面而不是直接登陆 Product 时,您可以做更多的事情。页。

如果应用程序呈现指向产品页面的链接,例如呈现 ProductOrder收藏,还有什么可以做的吗?

<ul id="product-order-list">
  <li>Ordered 5 days ago. Product 0 <a href="#/products/product0">(see details)</a></li>
  <li>Ordered 1 month ago. Product 1 <a href="#/products/product1">(see details)</a></li>
</ul>

我处理这种链接到详细信息页面模式的自然方法是定义一个路由,它可以按照以下方式执行某些操作:

routes: {
  'products/:productid': 'showProduct'
  ...
}

showProduct: function (productid) {
  var model = new Product({_id: productid});
  var view = new ProductView({model: model});
  //just jam it in there -- for brevity
  $("#main").html(view.render().el);
}

然后我倾向于拨打 fetch() View 内部 initialize函数,并调用 this.render()来自 this.listenTo('change', ...)事件监听器。这会导致复杂的 render()案例,以及从视野中出现和消失的物体。例如,我的 View 模板 Product可能会为用户评论保留一些屏幕空间,但当且仅当产品上存在/启用评论时——并且在完全从服务器获取模型之前通常是不知道的。

现在,在哪里/什么时候最好进行提取?
  • 如果我在页面转换之前加载模型,它会导致直接的 View 代码,但会引入用户可察觉的延迟。用户将单击列表中的一个项目,并且必须等待(页面不发生变化)返回模型。响应时间很重要,我还没有对此进行可用性研究,但我认为用户习惯于在单击链接后立即看到页面更改。
  • 如果我在 ProductView 中加载模型的 initialize , 与 this.model.fetch()并监听模型事件,我被迫渲染两次, - 一次之前使用空占位符(因为否则您必须盯着白页),一次之后。如果在加载过程中发生错误,那么我必须删除 View (出现闪烁/故障)并显示一些错误。

  • 有没有我没有看到的另一个选项,也许涉及可以在 View 之间重复使用的过渡加载页面?或者总是首先拨打 render() 是一个好习惯。显示一些微调器/加载指示器?

    编辑:通过 collection.fetch() 加载

    有人可能会建议,因为这些项目已经是列出的集合(用于呈现链接列表的集合)的一部分,所以可以在单击链接之前获取它们,使用 collection.fetch() .如果该集合确实是 Product 的集合,那么渲染产品 View 就很容易了。
    Collection用于生成列表的可能不是 ProductCollection然而。可能是 ProductOrderCollection或其他仅包含对产品 ID 的引用(或足够数量的产品信息以呈现指向它的链接)的其他内容。

    获取所有 Product通过 collection.fetch()如果 Product 也可能被禁止模型很大,尤其是。万一产品链接之一被点击。

    鸡还是鸡蛋? collection.fetch()对于直接导航到产品页面的用户来说,这种方法也没有真正解决问题......在这种情况下,我们仍然需要渲染 ProductView需要 Product 的页面仅从 id(或产品页面 URL 中的任何内容)获取的模型。

    最佳答案

    好的,所以在我看来,有很多方法可以解决这个问题。我将列出我想到的所有内容,希望有人能与您合作,或者至少会激发您找到最佳解决方案。

    我并不完全反对 T J 的回答。如果您在网站加载时继续对所有产品执行 collection.fetch() 操作(用户通常希望涉及一些加载时间),那么您就拥有了所有数据,您可以像这样传递这些数据他提到。他的建议与我通常所做的唯一区别是,我通常在所有 View 中都引用了 app。所以,例如在我的 中初始化 函数在 app.js 我会做这样的事情。

    initialize: function() {
        var options = {app: this}
    
        var views = {
            productsView: new ProductsView(options)
        };
    
        this.collections = {
            products: new Products()
        }
    
        // This session model is just a sandbox object that I use
        // to store information about the user's session. I would
        // typically store things like currentlySelectedProductId
        // or lastViewedProduct or things like that. Then, I just
        // hang it off the app for ease of access.
        this.models = {
            session: new Session()
        }
    }
    

    然后在我的 productsView.js 初始化 功能我会这样做:
    initialize: function(options) {
        this.app = options.app;
    
        this.views = {
            headerView: new HeaderView(options),
            productsListView: new ProductsListView(options),
            footerView: new FooterView(options)
        }; 
    }
    

    我在 productsView.js 的初始化中创建的 subview 是任意的。我主要只是想证明我继续将该选项对象传递给 View 的 subview 。

    这样做是允许每个 View ,无论是顶级 View 还是深层嵌套的 subview ,每个 View 都知道每个其他 View ,并且每个 View 都可以引用应用程序数据。

    这两个代码示例还介绍了尽可能精确地确定功能范围的概念。不要试图拥有一个可以做所有事情的 View 。将功能传递给其他 View ,以便每个 View 都有一个特定的目的。这也将促进 View 的重用。尤其是复杂的模态。

    现在回到手头的实际主题。如果您要继续并预先加载所有产品,您应该在哪里获取它们?因为就像你说的,你不希望一个空白页面只是坐在你的用户面前。所以,我的建议是欺骗他们。加载尽可能多的页面,并且只阻止加载需要数据的部分。这样对用户来说,页面看起来像是在加载,而您实际上是在幕后工作。如果您可以欺骗用户认为页面正在稳定加载,那么他们就不太可能对页面加载感到不耐烦。

    因此,从 productsView.js 引用初始化,您可以继续让 headerView 和 footerView 呈现。然后,您可以在 productsListView 的渲染中进行提取。

    现在,我知道你在想什么。我是不是失去了理智。如果您在渲染函数中执行提取,那么在我们到达实际渲染 productsViewList 模板的行之前,调用将没有时间返回。好吧,幸运的是有几种方法可以解决这个问题。一种方法是使用 Promises .但是,我通常这样做的方法是将渲染函数用作自己的回调。让我演示给你看。
    render: function(everythingLoaded) {
        var _this = this;
    
        if(!!everythingLoaded) {
            this.$el.html(_.template(this.template(this)));
        }
        else {
            // load a spinner template here if you want a spinner
    
            this.app.collection.products.fetch()
                .done(function(data) {
                    _this.render(true);
                })
                .fail(function(data) {
                    console.warn('Error: ' + data.status);
                });
        }
    
        return this;
    }
    

    现在,通过以这种方式构建我们的渲染,在数据完全加载之前,实际模板不会加载。

    虽然我们在这里有一个渲染函数,但我想介绍另一个我在任何地方都使用的概念。我称之为 postRender。这是一个函数,一旦模板完成加载,我就会执行任何依赖于 DOM 元素的代码。如果您只是编写一个普通的 .html 页面,那么这就是传统上位于 中的代码。 $(document).ready(function() {}); .值得注意的是,我的模板不使用 .html 文件。我用 嵌入的 javascript 文件 (.ejs) .继续,postRender 函数是我基本上添加到我的样板代码中的函数。因此,每当我为代码库中的 View 调用 render 时,我都会立即将 postRender 链接到它上面。我也使用 postRender 作为对自身的回调,就像我对渲染所做的那样。所以,基本上前面的代码示例在我的代码库中看起来像这样。
    render: function(everythingLoaded) {
        var _this = this;
    
        if(!!everythingLoaded) {
            this.$el.html(_.template(this.template(this)));
        }
        else {
            // load a spinner template here if you want a spinner
    
            this.app.collection.products.fetch()
                .done(function(data) {
                    _this.render(true).postRender(true);
                })
                .fail(function(data) {
                    console.warn('Error: ' + data.status);
                });
        }
    
        return this;
    },
    
    postRender: function(everythingLoaded) {
        if(!!everythingLoaded) {
            // do any DOM manipulation to the products list after 
            // it's loaded and rendered
        }
        else {
            // put code to start spinner
        }
    
        return this;
    }
    

    通过像这样链接这些函数,我们保证它们将按顺序运行。

    ================================================== ========================

    所以,这是解决问题的一种方法。但是,您提到您不必预先加载所有产品,因为担心请求可能需要太长时间。

    旁注:您真的应该考虑取出与产品调用相关的任何可能导致调用花费大量时间的信息,并将较大的信息作为单独的请求。我有一种感觉,如果您能够真正快速地为他们提供核心信息,并且如果与每个产品相关的缩略图需要更长的时间来加载它不应该那么结束世界。这只是我的意见。

    解决此问题的另一种方法是,如果您只想转到特定的产品页面,则只需实现 渲染/后渲染模式 我上面在单个 productView 上概述的。但是请注意您的 productView.js 可能必须看起来像这样:
    initialize: function(options) {
        this.app = options.app;
        this.productId = options.productId;
    
        this.views = {
            headerView: new HeaderView(options),
            productsListView: new ProductsListView(options),
            footerView: new FooterView(options)
        }; 
    }
    
    render: function(everythingLoaded) {
        var _this = this;
    
        if(!!everythingLoaded) {
            this.$el.html(_.template(this.template(this)));
        }
        else {
            // load a spinner template here if you want a spinner
    
            this.app.collection.products.get(this.productId).fetch()
                .done(function(data) {
                    _this.render(true).postRender(true);
                })
                .fail(function(data) {
                    console.warn('Error: ' + data.status);
                });
        }
    
        return this;
    },
    
    postRender: function(everythingLoaded) {
        if(!!everythingLoaded) {
            // do any DOM manipulation to the product after it's 
            // loaded and rendered
        }
        else {
            // put code to start spinner
        }
    
        return this;
    }
    

    这里唯一的区别是 productId 在选项对象中传递给初始化,然后将其拉出并在渲染函数的 .fetch 中使用。

    ================================================== ========================
    总之,我希望这会有所帮助。我不确定我是否已经回答了您的所有问题,但我认为我已经很好地解决了这些问题。为了让这个时间太长,我现在先停在这里,让您消化一下并提出您的任何问题。我想我可能必须对这篇文章进行至少 1 次更新以进一步清除它。

    关于javascript - 在哪一点上获取 View 所依赖的主干模型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33748907/

    有关javascript - 在哪一点上获取 View 所依赖的主干模型?的更多相关文章

    1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    2. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    3. ruby-on-rails - 渲染另一个 Controller 的 View - 2

      我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

    4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

      我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

    5. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

      我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

    6. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

      我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

    7. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

      我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

    8. ruby-on-rails - 如何将验证与模型分开 - 2

      我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

    9. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

      我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

    10. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

      我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

    随机推荐