【问题标题】:Ember.js - Creating a multistep wizard - How to?Ember.js - 创建一个多步骤向导 - 如何?
【发布时间】:2013-03-13 11:20:16
【问题描述】:

我需要创建一个包含多个步骤的向导。每个步骤都会显示一个带有选项的表单,根据用户的选择,向导应该转到某个步骤,并将用户偏好(选择)存储在某个位置。

这些首选项不保存在模型中,它们只与模型创建的步骤有关。

为了给出一些上下文,这里的目标是:

  • 向用户提出一些关于其营业时间的问题。例如:周末营业吗?夏天不一样吗?
  • 根据这些问题的答案,将显示最终表格以创建时间表模型。

问题是,在 Ember 中实现这一目标的最佳方式是什么?

这是我的 -Ember 新手 - 想法:

  • 为每个向导步骤创建一个模板。
  • 跟踪当前步骤。 在哪里?控制器?路线?
  • 将这些模板显示到outlets,应根据当前步骤动态呈现。 这是我完全迷路的地方。这该怎么做?每个步骤是否应该有不同的路线?
  • 控制器中跟踪用户的回答。
  • 向导完成后,加载表单模板,该模板将读取存储在控制器中的用户首选项。

正在使用的版本:

  • Ember.VERSION : 1.0.0-rc.1 application.js:9268
  • Handlebars.VERSION : 1.0.0-rc.3 application.js:9268
  • jQuery.VERSION : 1.9.1

【问题讨论】:

  • 你似乎已经有了一些想法。为什么不先尝试实施它们呢?我认为当你努力解决问题时,人们更有可能帮助你。
  • @DaMainBoss 实际上我已经尝试了几个小时,现在仍在尝试。从控制器的connectOutlet 开始,它说控​​制器没有connectOutlet 方法,然后将其移至视图,然后尝试使用路由。所有意图在某个时候都失败了,这就是为什么我要求架构指南而不是真正的代码示例,因为我觉得我在黑暗中刺伤。此外,已经看到了那个链接的问题,但它与我的问题并不像最初看起来那样相关。
  • 好的。我认为迈克在下面的回答是一个很好的开始!

标签: model-view-controller ember.js


【解决方案1】:

听起来你在正确的轨道上。

为每个向导步骤创建一个模板。

是的,这是一个好的开始。

跟踪当前步骤。在哪里?控制器?路线?

控制器或路由都可以工作。如果您希望为每个步骤和后退/前进工作添加可收藏的 url,Route 最有意义,并且可能是最直接的解决方案。将假定您已选择路线。

将这些模板显示到出口中,应根据当前步骤动态呈现。这是我完全迷失的地方。这该怎么做?每个步骤是否应该有不同的路线?

由于每一步都是一条路线,ember 会自动渲染适当的模板。

在控制器中跟踪用户的回答。

向导完成后,加载表单模板,该模板将读取存储在控制器中的用户首选项。

将“完成”视为又一个步骤。每个步骤都有自己的控制器,用于记录用户响应。最后一个控制器使用“需要”来访问早期的控制器,以便根据对向导的响应自定义行为。

【讨论】:

  • 但是当你想在下一步之前保存数据时,如何跟踪?
【解决方案2】:

我知道这是一个旧线程,可能对 OP 没有帮助,但 stackoverflow 比其他任何方法都提供更多答案。我刚刚为此写了blog post

看看这个JSBin,看看我做了什么。我在这里总结一下。

模板中的每一步路由:

Router.map(function() {
  this.resource('wizard', function(){
    this.route('index');
    this.route('review');
    this.route('complete');
  });
});

当路由改变时改变值的自定义计算属性(在这种情况下,当向导的步骤改变时)

import Ember from 'ember';  
var get = Ember.get;  
var computed = Ember.computed;

export function routeVal(routeVals, prop){  
    return computed('currentPath', function(){
        var currentRoute = get(this, 'currentPath');
        var routeValues = get(this, routeVals);
        for (var i = 0; i < routeValues.length; i++) {
            if (routeValues[i].route === currentRoute) {
                return routeValues[i][prop];
            }
        }
    });
}

route-value 对象:

export default Ember.Object.extend({
    route: null
    //all other props can be added dynamically
});

用于了解当前路由的控制器 mixin:

export default Ember.Mixin.create({  
    needs: ['application'],
    currentPath: Ember.computed.alias("controllers.application.currentPath")

});

资源控制器:

import CurrentRouteAware from 'path/to/mixin'; 
import {routeVal} from 'app_name/utils/macros';
export default Ember.Controller.extend(CurrentRouteAware, {
  routeValues: [
        RouteVal.create({
            route: 'wizard.index',
            step: 'Create',
            next: 'Review',
            nextTransition: 'wizard.review',
            prevTransition: 'wizard.index',
            showNext: true,
            showPrev: false
        }),
        RouteVal.create({
            route: 'wizard.review',
            step: 'Review',
            next: 'Complete',
            prev: 'Create',
            nextTransition: 'wizard.complete',
            prevTransition: 'wizard.index',
            showNext: true,
            showPrev: true
        }),
        RouteVal.create({
            route: 'wizard.complete',
            step: 'Complete',
            next: 'Make Another',
            prev: 'Review',
            nextTransition: 'wizard.complete',
            prevTransition: 'wizard.review',
            showNext: false,
            showPrev: true
        })
    ], 
    nextButton: routeVal('routeValues', 'next'),
    prevButton: routeVal('routeValues', 'prev'),
    nextTransition: routeVal('routeValues', 'nextTransition'),
    showButtons: routeVal('routeValues', 'showButtons'),
    prevTransition: routeVal('routeValues', 'prevTransition'),
    showNext: routeVal('routeValues', 'showNext'),
    showPrev: routeVal('routeValues', 'showPrev'),
    actions: {
        next: function(){
            this.transitionToRoute(this.get('nextTransition'));
        },
        prev: function(){
            this.transitionToRoute(this.get('prevTransition'));
        }
    }
});

认为路由值对象的含义是,“当路由等于 routeVal.route 时,以下属性将具有这些值”例如“当当前活动的路由是 'wizard.index' 时,下一个转换是 'wizard.index'。 review',下一个按钮文本是'Review',上一个按钮应该被隐藏,等等"

最后,你的资源模板:

<div class="wizard" id="wizardIllustration">
    {{form-wizard steps=routeValues currentPath=currentPath}}
  <div class="actions">
  {{#if showPrev}}
    <button type="button" {{action 'prev'}}class="btn btn-default btn-prev"><span class="glyphicon glyphicon-arrow-left"></span>{{prevButton}}</button>
    {{/if}}
    {{#if showNext}}
    <button {{action 'next'}}type="button" class="btn btn-default btn-next" >{{nextButton}}<span class="glyphicon glyphicon-arrow-right"></span></button>
    {{/if}}
  </div>
  <div class="step-content">
    {{outlet}}
  </div>

</div>

您可以查看 jsbin 以了解 form-wizard 组件是什么(只是对 Fuelux 向导的 css 的包装,它使活动类根据路线保持在正确的步骤上)。向导的主体是每个子路由的模板。下一个/上一个按钮的文本会根据路线而变化,它们的转换也是如此(因为转换取决于向导的当前状态)。它或多或少是一个 FSM

【讨论】:

  • +1 非常有帮助!在引用的博客文章和 JS Bin 之间,我能够在 CLI 项目中将其拼凑起来。棘手的部分是合并一个“自定义计算机属性”。它写得好像应该被导入,但我只是把原始函数放在我的控制器定义之上。非常感谢!
  • 好吧,我在很多地方都使用了这个函数,因此我使用了一个名为 macros.js 的实用程序类来计算属性。导入函数的语法略有不同,所以我会用它来更新答案。你用{fn_name}
【解决方案3】:

回答一个老问题,以防有人需要一些答案。

你可以使用这样的东西 Multi-step form (or multiple "pages") in one route using Ember.js

这使用 1 条路线,每个步骤/页面使用 {{#if}} 显示或隐藏 为了回答 Nino 的问题,当我实现这个时,我的控制器中有一个对象来跟踪表单字段的所有值并在我单击下一步时更新它。当您到达提交的最后一页时,您可以像这样简单地将这个对象插入到控制器的提交函数中

submitSurvey: function() {
  var survey = this.store.createRecord('survey', this.get('surveyObj'));
  // surveyObj contains all the form field's values that you've been
  // keeping track of everytime you go from one step to another
  survey.save();
  this.transitionToRoute('somewhere'); // go somewhere after submitting the form
  this.set('firstPage', true); // make first page the page to show
  this.set('lastPage', false);
  this.init();
  // constructor's init contains code that empties all the form fields alternatively
  // you can create a separate function instead of using init
}

适用于多页表单的简单实现。我遇到的问题是我有 jQuery UI 代码,该代码与视图的 didInsertElement 挂钩,当我从一个步骤转到另一个步骤并返回时(每个页面都有“下一个”和“上一个”按钮),我发现在 didInsertElement 处运行的任何代码都将被撤消。就像这段 html 代码不只是隐藏和重新显示一样。它已重新加载,因此所有效果都消失了。

【讨论】:

    猜你喜欢
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 2012-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多