MeteorFlux Flow

Ohhhh, ok, sorry.

Like I said, I would like to make it work with the official supported tools.

Besides, I prefer Blaze over React. I think it is simpler to use and you have to write less code to do the same things. But if you prefer React, you can use it instead.

I have added another example: a simple shopping cart.


http://cartflux.meteor.com/

This time, it has two stores and some error handling (when adding a product to the catalog).

I will add another version with accounts and routing as soon as I have more free time!

@luisherranz thanks for sharing the examples. It’s really interesting indeed.
One question about your implementation compare to space:UI. How do you check the MAGIC callback strings correctness. Looks like dispatcher doesn’t care about the actual actionName, if I rename actionType only on one side of dispatcher interaction from dispatcher perspective everything still works. With mediator you at least get JS error during test run, while with current dispatcher you need to have a full functional test for every action. Which can be challenging sometimes if it’s something like a mouse drag event.

You are correct, you can use another name or not use a name at all.

That gives you less immunity against typo errors but more freedom.
For example, you can solve your concern with something like:

Dispatcher.register(function(payload){
  if (payload.actionType === undefined) {
    console.log("You forgot to set an actionType!");
}});

By the way, this is Facebook’s implementation but ported to Meteor.
You can check the original repo in:

Their API is bare-bones but powerful enough to create full Flux applications.
Once you figure out how it works, building helpers on top of that is very easy.

Facebook people use ActionCreators and ActionConstants as well, but you don’t need an API for that.
You just do:

SomeConstants.SOMETHING_HAPPENED = "SOMETHING_HAPPENED";
// and...
var somethingHappened = function(arg1, arg2){
  Dispatcher.dispatch({ 
    actionType: SomeConstants.SOMETHING_HAPPENED, 
    stuff: arg1, 
    more_stuff: arg2
})};

My goal now is not to extend the basic API, but figuring out the best way to use Flux within Meteor :blush:

signals solves the magic string problem
http://millermedeiros.github.io/js-signals/
I really like it, I’m using it in a Meteor app.

However, there is one downside with Signals, and most event based frameworks which is that “events” “signals” , “actions” , (whatever you want to call the message object), these are sent via DOM events. So, if you want to send or receive messages from the perspective of a particular object, it must be on the DOM.

Is this true for this Flux Meteor port ?

One way around this is to create a single DOM object, add it to the document body. Then all objects ( whether on the dom or not ) can use this single DOM object to relay all messages.

Ideally, providing the reference to this single DOM message relay object can be done automatically. Otherwise it’s a lot of boilerplate for every view that needs to tap into the messaging system.

thanks,
dave

I have updated the shopping cart with materializecss (it’s great, by the way):
http://cartflux.meteor.com/

I added a modal for the Add Product form. Click on the blue googlish button to see it.

I am controlling it with actions: “OPEN_ADD_PRODUCT” and “CLOSE_ADD_PRODUCT”.
The store receives those actions and saves the state in a Session variable called “Catalog.OpenAddProduct”.
Then the View (AddProductView) react to that Session variable to open or close the modal.

The Store can closed it in other situations, for example, when the action “ADD_PRODUCT” adds a product successfully. It just sets “Catalog.OpenAddProduct” to false.

I am not sure if that’s the best way to deal with this kind of things. In a way, the Store is modifying the App State, which is great (and survives hot code pushes) but in other way, the Store is too responsible about what the View should or shouldn’t show, which doesn’t seem good.

Any thoughts?

1 Like

I’ve solved this issue changing the names of the actions and the Session variable.

Now they are "USER_IS_ADDING_PRODUCT", "USER_IS_NOT_ADDING_PRODUCT" and Session.get("Catalog.userIsAddingProduct").

That way, the Stores doesn’t know if there is a modal in the view or not, it just knows if the user is currently adding a product.

The view can do whatever it needs to render a form to add a new product, not matter if it’s an in-page form, a modal or some other thing.

If the user added a product successfully, the Store knows he is not adding a product anymore, and therefore it sets the Catalog.userIsAddingProduct to false.

Then, the views act accordingly and reset or remove the forms and the errors, close the modal and so on.

So in the end, the view is exposed to data (like the products of the Catalog collection) and app state (like the Session variable Catalog.userIsAddingProduct).

This is a good practice I think. For example, I like that translate.google.com writes all the state in the url so you can easily copy and send it:
https://translate.google.com/#en/es/All%20the%20state%20is%20saved%20on%20the%20url%20so%20I%20can%20copy%20and%20send%20it

Next step: Publications!

I’m really interested in where this is going, keep it up!

1 Like

I have added publications:
http://cartflux.meteor.com/

I don’t know how this would work once I have added a router, but without one yet, it is quite easy.
The Views trigger actions and the Stores (which control the subscription) decide to change it or not.

For example, to change the subscription based on a search query, something as simple as this works well:

Template.NavBarView.events({
  'keyup #search': function(event,template){
    Dispatcher.dispatch({
      actionType: "USER_HAS_SEARCHED_PRODUCTS",
      search: event.target.value
    });
  }
});

And in the Store:

 // CatalogStore Subscriptions
 if (Meteor.isClient){
   Tracker.autorun(function (){
     Meteor.subscribe('Catalog.catalogSearch', 
         Session.get("Catalog.searchProductsQuery"));
   });
 }

 // CatalogStore Publications
 if (Meteor.isServer) {
   Meteor.publish('Catalog.catalogSearch', function(search) {
     var regexp = new RegExp(search, 'i');
     return Catalog.find({name: regexp});
   });
 }

 // Method triggered by "USER_HAS_SEARCHED_PRODUCTS"
CatalogStore.searchProducts: function(search){
  Session.set("Catalog.searchProductsQuery", search);
}

Next step: The Router! (First IronRouter, then FlowRouter).

EDIT: Ideally, your Store would be smart enough to add to the collections only what the Views need to show, like in this great tutorial by Pieter Soudan http://meteorpatterns.com/Sewdn/query-collections

2 Likes

Thanks! Feel free to share your thoughts about all this : )

I’ve been studying publications and subscriptions a bit more.

It looks like subscriptions are smarter when they are put inside a Tracker.autorun.

If you do something like this:

var catalogSub = Meteor.subscribe('Catalog.catalogSearch', "");

var searchProducts = function(search){
  catalogSub.stop();
  catalogSub = Meteor.subscribe('Catalog.catalogSearch', search);
};

All the data is thrown away and then sent again.

But if you do something like this:

Tracker.autorun(function(){
  Meteor.subscribe("Catalog.catalogSearch",Session.get("Catalog.searchQuery"));
});

var searchProducts = function(search){
  Session.set("Catalog.searchQuery", search);
};

Meteor diffs the data and sends only what is necessary.
It seems like there is no way to do that without Tracker and a reactive variable.


In the new Meteor 1.0.4 you can set subscriptions in Templates and Meteor creates/destroys them automatically when the Template is created/destroyed. It gives you a helper called Template.subscriptionsReady as well.

I’ve been thinking if it is ok to move subscriptions from Stores to Views in a Flux app.

If Views (Template.helpers) are allowed to decide what to retrieve from Minimongo:

Template.SomeView.helpers({
  some_helper: function(){
    return SomeCollection.find({name: "something"});
  }
});

They should be allowed to decide what to retrieve from MongoDB, shouldn’t they?

Template.SomeView.onCreated(function () {
  var self = this;
  self.autorun(function () {
    self.subscribe("someSubscription", Session.get("somethingReactive"));
  });
});

I am not 100% sure yet, but I think it may work.

Views are still not allowed to insert/update data in Collections or set Session variables. If a View wants to modify a subscription it has to send an Action and a Store has to change the App State (Session or Collection which affects the subscription). So App State is still managed in Stores.

I would prefer if you don’t have to include a reactive variable in the subscription but it looks there’s no way around it.

I have refactored and updated the repo and the demo:
http://cartflux.meteor.com

By the way, before routers, I am going to:

  • Remove insecure package
  • Include user accounts
  • Implement pagination

At this point, I wanted to try “the Flux way” of retrieving data.

What I’ve been calling “App State” in Meteor (Collections+Sessions) doesn’t exist in Flux because it only provides a dispatcher and a frontend (React). So, when the frontend needs data, it asks the Stores, not the “App State”.

I have refactored the shopping cart app to try this. You can check the new code here:


http://cartflux.meteor.com

Now, when a View needs data, it calls a Store method like:

Template.CatalogView.helpers({
  catalog_products: function(){
    return CatalogStore.getSearchedProducts();
  }
});

It doesn’t access the Collection directly anymore.
As you can see, the logic about what is a “SearchedProduct” is now on the Store and not on the View.

Besides, I added the reactive-var package so now every reactive var is local to the Store.

var adding_product = new ReactiveVar(false);

Stores expose them with methods, like:

getUserIsAddingProduct: function(){
  return adding_product.get();
}

And Views retrieve them with:

CatalogStore.getUserIsAddingProduct();

Pros:

  • Stores now control how to update the data and what to expose.
  • No more confusing “global” Session variables.
  • All the data is retrieved with getter methods, no matter if it comes from Collections or Sessions.
  • Views are less coupled to the model.
  • Views need less logic when retrieving data.
  • It’s easier to add unit tests.

Cons:

  • You have to write slightly more code.

My conclusion:
I thought it was going to be a lot more code, but it’s not that much, really.
This way the Flux pattern is easier to understand and the app flow is easier to follow. I like it.

Any thoughts?

2 Likes

Very interesting. Keep it up.

1 Like

Awesome posts. Would be great to see this as serious of blog articles.
One question I have is how do you deal with latency compensation when using the dispatcher. Does it still work ?

1 Like

Sure, I will create a proper readme and a website for the package when I finish this research.

And I want to use it for my next project so I will update things if I learn more using it in production.

Yeah, everything works just like in any other Meteor app.

I’ve been very busy last week but I’m going to start working on this again.

But I read about Facebook’s new framework called Relay (and GraphQL).

It’s very interesting because the way they solved data fetching is incredibly similar to Meteor.

If you look at this image, you can see that what they call GraphQL Query is in fact a Mongo Query and what they call Relay Store is our Minimongo.

There is a video where they explain all this:

This is not 100% similar, because with the GraphQL query they do a kind of “Meteor subscription and Mongo Query” at the same time, but in the end, the approach is the same.

So in Flux it is fine if a React Component (Meteor Template) declares the data it needs with queries to the server. Actually, they think it is the best way to do it.

The conclusion is: Templates can do subscriptions and fetch data without using Stores and this reinforces my idea that Meteor and Flux may be the perfect couple.

By the way, I’ve been working with user accounts in the Shopping example but it’s not finished yet. I will publish it as soon as I can.

5 Likes

Hey @luisherranz! Thanks for your feedback about space:ui :wink:

The fun fact about your exploration here is, that if you look at the history of the space:ui package, it followed exactly the same paths that you lay out here. It started very simplistic with the 150 lines facebook flux dispatcher, string-based actions with anonymous payload and some sugar classes for view mediation and stores etc.

The problem is always the same: With the small TodoMVC examples these things look super awesome and work quite well, but as soon as you write a real world application with hundreds of views and stores stuff tends to get tricky and other aspects become more important. The reason why space:ui moved away from the flux dispatcher is that it provides no messaging contracts. You just throw objects around the app without a runtime check for correctness.

I have been there and it was not super awesome to refactor front-end code, changing the params of actions etc. In my opinion you should either have very good messaging contracts or avoid messaging completely. This is basically an initial drawback of Javascript, as it is not statically typed. In other languages you would automatically get a message contract by defining which params an event / action takes.

So space:ui moved away from flux dispatcher because it doesn’t solve a single problem that we have in Meteor apart from sending messages around. With space:messaging (used by space:ui) we introduced a messaging solution that makes your code really solid by using declarative message contracts based on Meteors awesome check and Match which have the side-effect of being EJSON serializable, but of course also adds a little bit of overhead because you have to define your events.

The example you picked should probably be re-written, because the mediator could already send the command to the server to clear all completed todos without ever touching the store. Beware of over-using an architecture, because I also was like you: “follow the rules of flux” and sometimes Meteor has simpler answers than action/event based messaging :wink:

1 Like

TLDR: The building blocks for a Meteor Flux architecture are already there.

I use a Flux architecture in the new Meteor editor centric app that I develop. I’ve started with space:ui 3.4.3 that already implements a Dispatcher, Stores with reactive state and Action factories that reduce the boilerplate code. I’ve decided to not use the template mediator and use Meteor templates directly with a small base template mixin.

I think the Meteorhacks packages work great in addition:

The iron:router can also work but you should disable the reactive route behavior by using Tracker.afterFlush in the route action.

What I have learned so far:

  • The router should just change the layout and emit an action so that stores can do other app state changes

  • Having the domain specific business logic in one place (Stores) reduced the mental overhead when you must change or add something to it.

1 Like

Hey @CodeAdventure, thanks for chiming in :relaxed:

Meteor is very very simple so I want to keep things as simple as possible. Space:ui remembers me to PureMVC and man, I don’t want that again. I have a hard time reading CS so I didn’t take a exhaust look at it, but I don’t like things like mediators, constants, commands, controllers, sugar classes…

And I don’t feel like going far from regular Meteor is the way to go and the less I introduce, the better.

I think the point of Flux is that you cannot make something difficult to debug and therefore your application doesn’t have to be so tight.

Anyway, that’s my way to see things right now and all this is helping me understand both Meteor and Flux better so I’m going to continue until I have introduced as much elements as I can.

2 Likes

Sure! I’m just trying to figure out myself where every Meteor element should be.

Yes, I think FlowRouter and FlowLayout are going to be great in this. I don’t like FlowComponents that much because I prefer to use the official Blaze.

100% agree.