meteor-space / ui

Pattern-agnostic base UI package to gain control over your Meteor UI
MIT License
121 stars 15 forks source link

Stores are not initialized on the app start #17

Closed ghost closed 9 years ago

ghost commented 9 years ago

I use space:ui 3.4.4.

The store singletons are lazily initialized when another component depend on it. However a component can emit an action without depending on the store. I think the action emitter shouldn't need to know what stores listen for the action. So the store can miss the action when it wasn't initialized.

I've fixed this by adding the following code parts to my Space.Application subclass:

MyApp.Application = (function() {
  extend(Application, Space.Application);

  function Application () {
    this._stores = [];
    Application.__super__.constructor.apply(this, arguments);
  }

  _.extend(Application.prototype, {

    configure: function () {
      this.configureStores();
    },

    configureStores: function () {
      this.mapStore('MyStore', MyStoreConstructor);
    },

    mapStore: function (name, store) {
      this.injector.map(name).toSingleton(store);
      this._stores.push(name);
    },

    initializeStores: function () {
      var injector = this.injector;
      this._stores.forEach(function (storeName) {
        injector.get(storeName);
      });
    },

    run: function () {
      this.initializeStores();
    }

  });

  return Application;
})();
DominikGuzei commented 9 years ago

Hey there! You can just initialize your stores in the application run method:

MyApp.Application = Space.Application.extend({
  configure: function() {
    this.injector.map('MyApp.MyStore').asSingleton(); // Map the store as singleton
  },

  run: function() {
    Space.Application.run.call(this); // This calls run method on all dependent modules
    this.injector.create('MyApp.MyStore'); // Create instance here
  }
});

// later
var myApp = new MyApp.Application();
myApp.run(); // Your store will be initialized immediately -> same goes for routers etc.

Please also note the new syntactic sugar for extending classes in javascript that is now possible with the newer releases of space:base :wink: you can read more about the basic capabilities of Space here: space:base. Please note that space:ui is currently only a very thin layer on top of that. So your issue actually is more related to space:base

Side note: I generally avoid to initialize anything by default, which means a little bit more typing but also much more control over the bootstrap process in your app. This is also due to the fact that the Space.Application shouldn't know about Space.ui.Stores because they are living in separate packages and there might be many different ways to structure your app in the future :wink: space:ui, with its stores and mediators is just a tiny detail in you overall app architecture.

DominikGuzei commented 9 years ago

Hey @Sanjo, just wanna check if this worked out for you?

ghost commented 9 years ago

I just saw you response. Will check it later today. Thanks for the answer.

DominikGuzei commented 9 years ago

Ok great :+1:

DominikGuzei commented 9 years ago

Hey @Sanjo, thanks again for the input – I just pushed out version 4.1.0 with a new Space.ui.Application class that provides a simpler API to define your stores, mediators and controllers. It uses pretty much the same idea that you showed above but with a slightly different API :wink:

You can take a look at the updated TodoMVC example app but basically it looks like this now:

class @TodoMVC extends Space.ui.Application

  RequiredModules: ['Space.ui']
  Stores: ['TodosStore']
  Mediators: ['InputMediator', 'TodoListMediator', 'FooterMediator']
  Controllers: ['IndexController']

  configure: ->
    super
    @injector.map('Todos').to new Mongo.Collection 'todos'
    @injector.map('Router').to Router # Use iron:router for this example app

or in Javascript

TodoMVC = Space.ui.Application.extend({

  RequiredModules: ['Space.ui'],
  Stores: ['TodosStore'],
  Mediators: ['InputMediator', 'TodoListMediator', 'FooterMediator'],
  Controllers: ['IndexController'],

  configure: function() {
    Space.ui.Application.prototype.configure.call(this);
    this.injector.map('Todos').to(new Mongo.Collection('todos'));
    this.injector.map('Router').to(Router); // Use iron:router for this example app
  }
});

I think this is a reasonable amount of automation and should save you a ton of typing down the road!

ghost commented 9 years ago

Cool, thanks. :+1: Good timing. Just got time to test your suggestion.

And also thanks for your other hints.