为ApplicationRoute加载状态 [英] Loading state for ApplicationRoute

查看:65
本文介绍了为ApplicationRoute加载状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用:ember 1.7.0

Using: ember 1.7.0

我有一些服务器端数据,我想在任何路由转换到之前加载到我的ember应用程序。我的其他路由/控制器的多个(但不是全部)需要这些数据。我想我可以在ApplicationRoute 模型方法中加载这些数据。它工作正常,但不显示加载状态。

I have some server-side data that I want to load up in my ember app before any routes are transitioned to. Multiple (but not all) of my other routes / controllers need this data. I was thinking that I could just load up this data in the ApplicationRoute model method. It works fine, but does not display a loading state.

是否可以让ApplicationRoute显示加载状态,直到它模型 promise得到解决。

Is it possible to have the ApplicationRoute display a loading state until it's model promise gets resolved.

这是一个jsbin说明问题: http://jsbin.com/soqivo/1/

Here's a jsbin illustrating the problem: http://jsbin.com/soqivo/1/

感谢您的帮助!

推荐答案

更新:



截至 1.11.0 release ,应该可以定义一个加载

Update:

As of 1.11.0 release, it should be possible to define a loading substate for the application route.

我认为这是设计,但是不是设计缺陷。这个特殊的问题是因为在 ApplicationRoute#model 中发生长时间的模型请求,当它应该在 IndexRoute#model 。将该承诺/请求移动到索引路由中,这应该是正常的。如果您必须向应用程序控制器添加内容,请考虑这个,结合在应用程序等待时在你的index.html文件中显示加载的东西。

I think this is by design, but not a design flaw. This particular issue is happening because that long model request is taking place in ApplicationRoute#model when it should be in IndexRoute#model. Move that promise/request into the index route and it should be fine. If you must add stuff to the application controller, consider this, combined with something that says "loading" in your index.html file while the app is waiting.

Ember.Route 有一些钩子,我们经常重写,所以它做我们想要的而不是一个默认实现。最明显的钩子是模型 setupController 。但是有时候,我们根本不写 setuptController 方法,因为它执行了我们要做的工作(给定一个只是想设置 / code>到控制器)。但不管这些方法是否被覆盖,它们都将作为内部工作流的一部分运行。这个工作流程有许多不经常讨论的步骤,因为他们已经做了我们想要的事情,我们倾向于忘记他们的重要性,而对于这个特定问题,这些步骤方法在路由生命周期中被调用。

Ember.Route has a number of hooks that we often override so it does what we want instead of a default implementation. The most obvious hook being model and setupController. But sometimes we simply don't write the setuptController method because it does what we want it to do already (given one just wants to set the model into the controller). But regardless of these methods being overridden, they will run as part of an internal workflow anyway. This workflow has a number of steps that are not often discussed because they already do what we want and we tend to forget about them and their importance, and, as for this particular issue, the order in which these methods get called in the route life cycle.

App = Ember.Application.create();

App.logs = Ember.ArrayProxy.create({
  content: []
});

App.Router.map(function() {
  this.resource('posts', function() {});
});

function loggingAlias(property) {
  return function() {
    App.logs.pushObject(this._debugContainerKey + ' ' + property);
    return this._super.apply(this, arguments);
  };
}

App.LoggingRoute = Ember.Route.extend({
  enter: loggingAlias('enter (private)'),
  exit: loggingAlias('exit (private)'),
  activate: loggingAlias('activate'),
  deactivate: loggingAlias('deactivate'),
  serialize: loggingAlias('serialize'),
  deserialize: loggingAlias('deserialize (private)'),
  model: loggingAlias('model'),
  setupController: loggingAlias('setupController'),
  afterModel: loggingAlias('afterModel'),
  beforeModel: loggingAlias('beforeModel'),
  renderTemplate: loggingAlias('renderTemplate'),
  redirect: loggingAlias('redirect')
});

App.LogsController = Ember.ArrayController.extend({
  content: App.logs,
  actions: {
    clearLogs: function() {
      App.logs.clear();
    }
  }
});

App.ApplicationRoute = App.LoggingRoute.extend();
App.PostsRoute = App.LoggingRoute.extend();
App.PostsIndexRoute = App.LoggingRoute.extend();

/* Put your CSS here */

html,
body {
  margin: 20px;
}

<!DOCTYPE html>
<html>

<head>
  <meta name="description" content="Ember Route Hook Order" />
  <meta charset="utf-8">
  <title>Ember Route Hook Order</title>
  <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.2.1.js"></script>
  <script src="http://builds.emberjs.com/beta/ember.js"></script>
</head>

<body>

  <script type="text/x-handlebars">

    <strong>Note:</strong>  <em>
    MilkyWayJoe says: I didn't write this. I have found in jsbin at: <a href="http://jsbin.com/rolo/2/edit?output">http://jsbin.com/rolo/2/edit?output</a>
    <br /> 
    Added here in case the link goes kaput just to point out the order of Ember.Route internal workflow steps 
    </em>
    <br />
    <br />{{link-to 'Index' 'index'}} {{link-to 'Posts' 'posts'}} {{outlet}} {{render 'logs'}}
  </script>

  <script type="text/x-handlebars" id='logs'>
    <h3>Logged Method Calls</h3>

    <a href="#" {{action 'clearLogs'}}>Clear Logs</a>
    <ul>
      {{#each}}
      <li>{{this}}</li>
      {{/each}}
    </ul>
  </script>
</body>

</html>

由于 renderTemplate 是被调用的最后一个,所以只有在一个给定路线得到解决。

Since renderTemplate is the last to get called so it only makes sense that it doesn't render anything until the promise(s) within a given route get resolved.

对于子路由,这是完全正确的,因为他们的加载子代将具有某种类型的因为在此路线之前已经加载了父母已经加载的生活和呼吸路线,甚至被实例化。但是对于 ApplicationRoute 来说,这是不正确的,因为它没有父级路由或模板依赖,因此渲染一个空白页,直到所有承诺得到解决。

For child routes, that's totally fine because their loading substate will have some type of canvas to draw onto, since a parent-already-loaded-living-and-breathing-route has been loaded prior this route even being instantiated. But that's not true for the ApplicationRoute since it has no parent route or template to rely upon, thus rendering a blank page until all promises get resolved.

最好的事情是将任何长时间运行的请求移动到子路由。作为提出的解决方案,我已经将您的3秒承诺移动到 IndexRoute#model ,因为此路由将运行无论如何,是 ApplicationRoute 默认情况下。我会说保留应用程序路由或控制器来处理错误事件。

The next best thing is moving any long running requests to a child route. As the proposed solution, I've moved your 3 sec promise to IndexRoute#model since this route will run anyway and is the direct child of ApplicationRoute by default. I would say reserve the application route or controller for handling error events instead.

App = Em.Application.create({
  displayName: 'Test.App'
});

App.Router.map(function() {
  this.resource('files', function() {
    this.route('index', {path: '/'});
    this.resource('file', { path: ':file_id' }, function() {
      this.route('index', {path: '/'});
      this.route('detail');
    });
  });
});

App.FilesController = Em.ArrayController.extend();
App.FilesFileController = Em.ObjectController.extend();

App.Person = Ember.Object.extend({}); 

App.IndexRoute = Ember.Route.extend({
    model: function(params, transition){
      return new Ember.RSVP.Promise(function(resolve){
        Ember.run.later(function(){ 
          var model =  App.Person.create();
          resolve(model);
        }, 3000); 
      });
    } 
});

App.LoadingRoute = Em.Route.extend({
  renderTemplate: function() {
    this.render('loading', {
      into: 'application',
      outlet: 'loading'
    });
  }
});

App.FileLoadingRoute = App.LoadingRoute.extend();
 
App.FilesRoute = Em.Route.extend({
  model: function() {
    var selfie = this;
    return new Ember.RSVP.Promise(function(resolve){
        Ember.run.later(function() {  
            var model = selfie.store.find('file');
            resolve(model);
        }, 800);  
    });
  }
});

App.FilesIndexRoute = Em.Route.extend({
  model: function(){
    return this.store.all('file');
  }
});

App.FileRoute = Em.Route.extend({
  model: function(params) { 
    return this.store.find('file', params.file_id);
  } 
});

App.FileIndexRoute = Em.Route.extend({
  model: function() {  
    return this.modelFor('file');  
  },
  renderTemplate: function() {
    
    this.render('files/index', {
      into: 'application'
    });
    
    this.render('file/index', {
      into: 'files/index',
      outlet: 'file'
    });
    
  }
});

App.FileDetailRoute = Em.Route.extend({
  model: function() {
    var selfie = this;
    return new Ember.RSVP.Promise(function(resolve){
        Ember.run.later(function(){ 
            var file = selfie.modelFor('file');
            var model = selfie.store.find('fileDetail', file.id);
            resolve(model);
        }, 800);  
    });
  },
  renderTemplate: function() {
    
    this.render('files/index', {
      into: 'application'
    });
    
    this.render('file/index', {
      into: 'files/index',
      outlet: 'file'
    });
    
    this.render('file/detail', {
      into: 'file/index',
      outlet: 'detail'
    });
  },
  actions: {
    loading: function() {
      return true;
    }
  }
});

App.RlLoadIndicatorComponent = Em.Component.extend({
  
  classNames: ['rl-load-indicator'],
  classNameBindings: ['isLoading:rl-overlay:rl-silent'],
  overlay: true,
  spinner: true,
  message: 'Loading...',
  loading: false,
  
  isLoading: function() {
    return this.get('loading');
  }.property('loading'),
  
  spinnerClass: function() {
    if (this.get('loading')) {
      if (this.get('spinner')) {
        return 'rl-spinner';
      }
    }
    return "";
  }.property(),
  
  actions: {
    setLoading: function() {
      this.set('loading', true);
    },
    setDone: function() {
      this.set('loading', false);
    }
  }
  
});

App.ApplicationAdapter = DS.FixtureAdapter.extend();

App.File = DS.Model.extend({
  name: DS.attr('string'), 
  text: DS.attr('string'),
  detail: DS.belongsTo('fileDetail', {async: true})
});

App.FileDetail = DS.Model.extend({
  owner: DS.attr('string'),
  canEdit: DS.attr('bool'),
  file: DS.belongsTo('file'),
  property1: DS.attr('string'),
  property2: DS.attr('string'),
  property3: DS.attr('string'),
  property4: DS.attr('string'),
  property5: DS.attr('string')
});

App.File.FIXTURES = [
  {id: 1, name: 'File 1', text: 'Blah 1', detail: 1},
  {id: 2, name: 'File 2', text: 'Blah 2', detail: 2},
  {id: 3, name: 'File 3', text: 'Blah 3', detail: 3},
  {id: 4, name: 'File 4', text: 'Blah 4', detail: 4},
  {id: 5, name: 'File 5', text: 'Blah 5', detail: 5},
  {id: 6, name: 'File 6', text: 'Blah 6', detail: 6},
  {id: 7, name: 'File 7', text: 'Blah 7', detail: 7},
  {id: 8, name: 'File 8', text: 'Blah 8', detail: 8},
  {id: 9, name: 'File 9', text: 'Blah 9', detail: 9},
  {id: 10, name: 'File 10', text: 'Blah 10', detail: 10}
];

App.FileDetail.FIXTURES = [
  {
    id: 1, 
    owner: 'Spiderman', 
    canEdit: true, 
    file_id: 1, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'With great values, comes great bindings'
  },
  {
    id: 2, 
    owner: 'Iron Man', 
    canEdit: true, 
    file_id: 2, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Another Value'
  },
  {
    id: 3, 
    owner: 'Thor', 
    canEdit: false, 
    file_id: 3, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Another Value'
  },
  {
    id: 4, 
    owner: 'Captain America', 
    canEdit: false, 
    file_id: 4, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Another Value'
  },
  {
    id: 5, 
    owner: 'Neil DeGrasse Tyson', 
    canEdit: true, 
    file_id: 5, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Another Value'
  },
  {
    id: 6, 
    owner: 'Dr. Doom', 
    canEdit: false, 
    file_id: 6, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Another Value'
  },
  {
    id: 7, 
    owner: 'Reed Richards', 
    canEdit: true, 
    file_id: 7, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Another Value'
  },
  {
    id: 8, 
    owner: 'Walter White', 
    canEdit: true, 
    file_id: 8, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Say My Name!' 
  },
  {
    id: 9, 
    owner: 'Jesse Pinkmann', 
    canEdit: true, 
    file_id: 9, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Bitch'
  },
  {
    id: 10, 
    owner: 'Hawk Barton', 
    canEdit: false, 
    file_id: 10, 
    property1: 'Value 1', 
    property2: 'Value 2', 
    property3: 'Value 3', 
    property4: 'Value 4', 
    property5: 'Another Value' 
  }
];

/* Put your CSS here */
html, body {
  margin: 20px;
}

.rl-load-indicator {
  text-align: center;
}

.rl-overlay {
    position:fixed;
    top:0;
    left:0;
    right:0;
    bottom:0;
    background-color:rgba(0, 0, 0, 0.85);
    background: url(data:;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAABl0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuNUmK/OAAAAATSURBVBhXY2RgYNgHxGAAYuwDAA78AjwwRoQYAAAAAElFTkSuQmCC) repeat scroll transparent\9; /* ie fallback png background image */
    z-index:9999;
    color:white;
}

.rl-silent {
  display: none;
  visibility: hidden;
}

.rl-spinner {
  width: 30px;
  height: 30px;
  background-color: #27ae60;
  margin: 100px auto;
  margin-bottom: 8px;
  -webkit-animation: rotateplane 1.2s infinite ease-in-out;
  animation: rotateplane 1.2s infinite ease-in-out;
}

.arrow-right {
	width: 0; 
	height: 0; 
	border-top: 5px solid transparent;
	border-bottom: 5px solid transparent;
	border-left: 5px solid green;
}

@-webkit-keyframes rotateplane {
  0% { -webkit-transform: perspective(120px) }
  50% { -webkit-transform: perspective(120px) rotateY(180deg) }
  100% { -webkit-transform: perspective(120px) rotateY(180deg)  rotateX(180deg) }
}

@keyframes rotateplane {
  0% { 
    transform: perspective(120px) rotateX(0deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg) 
  } 50% { 
    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) 
  } 100% { 
    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  }
}

<!DOCTYPE html>
<html>
<head>
<meta name="description" content="Loading Thingy" />
  <meta charset="utf-8">
  <title>Ember Starter Kit</title>
  <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  <script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
  <script src="http://builds.emberjs.com/tags/v1.7.0/ember.js"></script>
  <script src="http://builds.emberjs.com/beta/ember-data.js"></script>
</head>
<body>
 
  <script type="text/x-handlebars">
    <h1>{{unbound App.displayName}}</h1>
    {{partial "menu"}}
    <hr />
    {{outlet}} 
    {{outlet "loading"}} 
  </script>
 
  <script type="text/x-handlebars" data-template-name="loading">
  {{rl-load-indicator loading=true}}
  </script>
  
  <script type="text/x-handlebars" data-template-name="_menu">
{{#link-to 'index'}}Home{{/link-to}} |    {{#link-to 'files.index'}}Files{{/link-to}}
  </script>
  
  <script type="text/x-handlebars" data-template-name="index">
    <h3>Index</h3>
    Content goes here
  </script>
  
  <script type="text/x-handlebars" data-template-name="files/index">
    <h3>Files</h3>
    <table class="table table-hover">
      <thead>
        <tr>
          <th>Id</th>
          <th>Name</th>
          <th>&nbsp;</th>
        </tr>
      </thead>
      <tbody>
      {{#each file in model}}
        <tr>
          <td>{{file.id}}</td>
          <td>{{file.name}}</td>
          <td>
            {{#link-to 'file.index' file}}
            <p class="arrow-right"></p>
            {{/link-to}}
          </td>
        </tr>
      {{/each}}
      </tbody>
    </table>
    {{outlet "file"}} 
  </script>
  
  <script type="text/x-handlebars" data-template-name="file/index">
    <h3>{{name}}</h3>
    {{text}}
    <hr />{{#link-to 'file.detail'}}Detail{{/link-to}}
    {{outlet "detail"}}
    
  </script>
  
    <script type="text/x-handlebars" data-template-name="file/detail">
    <h5>Details</h5>
    <hr />
    <ul>
    <li>owner: {{owner}}</li>
    <li>can edit: {{canEdit}}</li>
    <li>property 1: {{property1}}</li>
    <li>property 2: {{property3}}</li>
    <li>property 3: {{property3}}</li>
    <li>property 4: {{property4}}</li>
    <li>property 5: {{property5}}</li>
    </script>
  
  
  <script type="text/x-handlebars" data-template-name="components/rl-load-indicator">
  <div {{bind-attr class=spinnerClass}}></div>
  {{unbound message}}
  </script>

</body>
</html>

这篇关于为ApplicationRoute加载状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆