So I have a module I have created that does a kind of "state" routing for me. I made my own little version to get my exact intended effect, and it seems to be working great until I plug it into separate modules to test.
I inject it into the 2 separate modules, define the information in the .config of each module I need to use it, then call it in a controller to use my change state kind of effect.
It had been going pretty good until I plugged it into separate modules, and now what seems to be happening is the module I have created to handle all of this is creating separate instances for each module. Let me show you what I mean:
Here is an example of one of the modules using it for testing -
angular.
module('urlTesting2', [ 'urlTesting'])
.config(function($moduleObjectProvider) {
var callback = function(name, obj) {
console.log(name, obj);
}
$moduleObjectProvider.$get().set("module2", callback)
.addState("calender", ["day", "week", "month"]);
}).controller("testControl2", function($scope, checkUrl) {
$scope.addSecond = function() {
checkUrl.goState("module2", "calender", ["yes", "no", "maybe"]);
}
});
So it's injected, and in the config I call the provider and set a new modules with states. In the controller I just call goState. This works great when its just by itself. The issue is when I add a separate module in doing the same. I have a fiddle here showing the problem -
https://jsfiddle.net/7hn3ovgz/1/
So - I like to test this in my own browser window but fiddle seems to be the easiest way to share this. It will not change the actual url in the browser but it will still log all the effects.
Basically what I think is happening is when I click to change state in a module, it fires it twice and looks for the state in the other module too (which isn't there). My desired effect was that ALL modules setting a config would be all in one place. So when you do the .set - it just adds the object into a variable called currentModules in the provider. It seems like the configs are setting separate instances (like a closure) of this, instead of pushing all the config set() into one big object for reference.
Apologies if this is unclear, hopefully the fiddle will show clearly enough, and thank you for taking the time to read.
Seems like the issue is the injector for the provider, every time it is called it creates a new instance of that function, so all you should have to do is switch
function $moduleObjectProvider() {
var currentModules = {};
to
var currentModules = {};
function $moduleObjectProvider() {
or restructure the provider not to be an injected function if possible
Related
I am just trying to get my head around event driven JS, so please bear with me. There are different kinds of modules within my app. Some just encapsulate data, others manage a part of the DOM. Some modules depend on others, sometimes one module depends on the state of multiple other modules, but I don't want them to communicate directly or pass one module to the other just for easy access.
I tried to create the simplest scenario possible to illustrate my problem (the actual modules are much more complex of course):
I have a dataModule that just exposes some data:
var dataModule = { data: 3 };
There is a configModule that exposes modifiers for displaying that data:
var configModule = { factor: 2 };
Finally there is a displayModule that combines and renders the data from the two other modules:
var displayModule = {
display: function(data, factor) {
console.log(data * factor);
}
};
I also have a simple implementation of pub-sub, so I could just mediate between the modules like this:
pubsub.subscribe("init", function() {
displayModule.display(dataModule.data, configModule.factor);
});
pubsub.publish("init"); // output: 6
However this way I seem to end up with a mediator that has to know all of the module-instances explicitly - is there even a way to avoid that? Also I don't know how this would work if there are multiple instances of these modules. What is the best way to avoid global instance-variables? I guess my question is what would be the most flexible way to manage something like that? Am I on the right track, or is this completely wrong? Sorry for not being very precise with my question, I just need someone to push me in the right direction.
You are on the right track, I'll try to give you that extra push you're talking about:
It you want loose coupling, pub-sub is a good way to go.
But, you don't really need that "mediator", each module should ideally be autonomous and encapsulate its own logic.
This is done in the following way: each module depends on the pubsub service, subscribe to all relevant events and act upon them. Each module also publishes events which might be relevant to others (code samples in a minute, bear with me).
I think the bit you might be missing here is that modules, which use events, will hardly never be just plain models. They will have some logic in them and can also hold a model (which they update when receiving events).
So instead of a dataModule you are more likely to have a dataLoaderModule which will publish the data model (e.g. {data: 3}), once he finishes loading.
Another great requirement you set is sharing data while avoiding global instance-variables - this is a very important concept and also a step in the right direction. What you miss in your solution for this is - Dependency Injection or at least a module system which allows defining dependencies.
You see, having an event driven application doesn't necessarily mean that every piece of the code should communicate using events. An application configuration model or a utility service is definitely something I would inject (when using DI, like in Angular), require (when using AMD/CommonJS) or import (when using ES6 modules).
(i.e. rather then communicating with a utility using events).
In your example it's unclear whether configModule is a static app configuration or some knob I can tweak from the UI. If it's a static app config - I would inject it.
Now, let's see some examples:
Assuming the following:
Instead of a dataModule we have a dataLoaderModule
configModule is a static configuration model.
We are using AMD modules (and not ES6 modules, which I prefer), since I see you stuck to using only ES5 features (I see no classes or consts).
We would have:
data-loader.js (aka dataLoaderModule)
define(['pubsub'], function (pubsub) {
// ... load data using some logic...
// and publish it
pubsub.publish('data-loaded', {data: 3});
});
configuration.js (aka configModule)
define([], function () {
return {factor: 2};
});
display.js (aka displayModule)
define(['configuration', 'pubsub'], function (configuration, pubsub) {
var displayModule = {
display: function (data, factor) {
console.log(data * factor);
}
};
pubsub.subscribe('data-loaded', function (data) {
displayModule.display(data, configuration.factor);
});
});
That's it.
You will notice that we have no global variables here (not even pubsub), instead we are requiring (or injecting) our dependencies.
Here you might be asking: "and what if I meant for my config to change from the UI?", so let's see that too:
In this case, I rather rename configModule to settingsDisplayModule (following your naming convention).
Also, in a more realistic app, UI modules will usually hold a model, so let's do that too.
And lets also call them "views" instead of "displayModules", and we will have:
data-loader.js (aka dataLoaderModule)
define(['pubsub'], function (pubsub) {
// ... load data using some logic...
// and publish it
pubsub.publish('data-loaded', {data: 3});
});
settings-view.js (aka settingsDisplayModule, aka config)
define(['pubsub'], function (pubsub) {
var settingsModel = {factor: 2};
var settingsView = {
display: function () {
console.log(settingsModel);
// and when settings (aka config) changes due to user interaction,
// we publish the new settings ...
pubsub.publish('setting-changed', settingsModel);
}
};
});
data-view.js (aka displayModule)
define(['pubsub'], function (pubsub) {
var model = {
data: null,
factor: 0
};
var view = {
display: function () {
if (model.data && model.factor) {
console.log(model.data * model.factor);
} else {
// whatever you do/show when you don't have data
}
}
};
pubsub.subscribe('data-loaded', function (data) {
model.data = data;
view.display();
});
pubsub.subscribe('setting-changed', function (settings) {
model.factor = settings.factor;
view.display();
});
});
And that's it.
Hope it helps :)
If not - comment!
You do not need a mediator. Just import data, config, and display and call display(data, config) where you need to.
// import data
// import config
function render(){
display(data, config)
}
I have been looking in to this article for Jasmine unit testing. I found this example:
describe("Episode", function() {
beforeEach(function() {
this.episode = new Backbone.Model({
title: "Hollywood - Part 2"
});
});
it("should expose an attribute", function() {
expect(this.episode.get("title"))
.toEqual("Hollywood - Part 2");
});
});
This example is using this.episode in beforeEachand it. As far as I know JS doesn't work like this. this.episode doesn't work in describe block at all?
Jasmine introduced a new way to share variables between beforeEach, it, and afterEach through this keyword.
You should also know that each spec’s beforeEach, it, afterEach has this as the same empty object that is set back to empty for the next spec.
From Github
For every test (and their beforeEach/afterEach hooks), jasmine sets
the receiver of each function to an initially empty object. This
object, which is called userContext within Jasmine's source code, can
have properties assigned to it, and gets blown away at the end of each
test. In an attempt to address the issues we were having, we recently
switched over to assigning variables to this object, rather than
declaring them within describe and then assigning them.
This new approach is considered better because of:
No more global leaks
Clear meaning
Improved code reuse via dynamic invocation
Reduced Code Duplication via Lazy Evaluation
I'm trying to implement communication between my view models in a knockoutjs driven application. I scaffolded it using yeoman tool, and as you can see it uses AMD:
define(['knockout', 'signals', 'text!./nav-bar.html'], function(ko, Signal, template) {
function NavBarViewModel(params) {
this.route = params.route;
}
return { viewModel: NavBarViewModel, template: template };
});
I have to define an object that I would later use to dispatch events, right? Something like that:
var EventDispatcher = {
itemClicked: new Signal()
};
And then, whenever something happens in the NavBarViewModel I'd like to do:
EventDispatcher.itemClicked.dispatch();
The problem is - where should I put this EventDispatcher thing? It's obvious that it should be some kind of a global object, so that every VM could get a hold on it, but it would be ugly. Dependency injection came to mind, since everything else in this architecture I choose is done this way, but how to achieve that? I come from WPF, MVVM world, and so far I've used MVVMLight framework which had this great Messenger component. Is there anything like that in the JS world (and if it's js-signals lib I'm already using, then how should I use it to achieve my goal?)
I could also use the subscribable object built into the knockout fw, but the question still stands - where to put it (or how to share the instance between VMs)?
You'd quite simply inject it by including it in your define.
First, create a new file, EventDispatcher.js with your EventDispatcher code inside (and other relevant Knockout bits, like returning the view model and whatnot).
Then in your current file add it in:
define([ ... , ... , "EventDispatcher"], function( ... , ... , EventDispatcher )
Now you can simply call its methods within this file by using:
EventDispatcher.itemClicked.dispatch()
(Where EventDispatcher is what we've named it in our define parameters).
Do bear in mind though that your EventDispatcher.js file will also need the signals file passed to it through its own define wrapper.
I am using a provider to kind of try and play around with my own set of "state" saving. I have a provider set up in it's own module and injects into 2 separate "child modules" of the app I want to use it on. Each of them has a config set up where they send some information I would like to hold onto. The issue I am having right now is it does not seem to be holding the info as I was thinking it would. I basically want to create one big object in a provider, each module would contribute with it'd own config. Let me show what I mean.
The provider looks like so -
angular.
module('urlTesting.testProvider', [])
.provider("freshUrl", function(){
//our state object
var currentState = {};
return {
stateName : function(name){
//test function
this.state_name = name;
},
addState : function(modName, item, variables){
var newState = { "module" : modName, "customUrl" : [{ "name" : item, "options" : variables}]};
_.extend(currentState, newState);
console.log(currentState, modName);
},
$get: function(){
return {
state: currentState
};
}
}
});
And a config in a different module looks like so :
.config(function(freshUrlProvider){
//module name, state name, provided state variables
freshUrlProvider.addState("module1", "mod1", ["true", "false"]);
});
So I have 2 modules, both doing this, the only difference is the items in module 2 are names module2 and mod2.
So I have click functions controllers in each module that call the provider and as for the currentState (see the $get)
So they just do a :
console.log(freshUrl.state);
Now, where I am having a problem is - in module 1 when I click this I get back only the object for module 2 ( { "module": "module2 ...ect ), and when I click that in module 2, I get back undefined.
My thinking was the _.extend would combine all these objects into currentState, but it does not seem to be working how I thought. Could use some help as I seem to have hit a wall. Thanks for reading!
The underlying idea is (like routes) the modules would separately set their configs so I could use them upfront -- so I would like all the config objects being sent to be combined into one big currentState object, hence _.extend.
Edit : Maybe this is a job for a modularized factory, not a provider? I just thought it would be nice to let each module set its custom params in the config - but I can't seen to get this to create and use one big object across all modules.
Edit2: I found this article (http://garabagne.io/2014/06/08/the-life-and-times-of-the-angular-provider/) which was a pretty great read on providers, however I am still unsure if this is how I should approach this problem.
I think i've honed it down to the question - can you use the same provider across multiple modules (in module fashion) to have 1 place that will keep track of all config vars. It seems like the answer to this would be no, as they are considered singletons.
So I'm writing a whole bunch of vendor-specific files in node which all have a similar controller pattern, so it makes sense for me to cut them out and put into a common file.
You can see my common controller file here: https://gist.github.com/081a04073656bf28f46b
Now when I use them in my multiple modules, each consecutively loaded module is overwriting the first. This is because the file is only required once and passed dynamically through to each module on load (this allows me to add extra modules and these modules are able to add their own routes, for example). You can see an example module here: https://gist.github.com/2382bf93298e0fc58599
You can see here on line 53 I've realised that we need to create a seperate instance every time, so I've tried to create a new instance by copying the standardControllers object into a new object, then initialising the new object. This has zero impact on the code, and the code behaves in exactly the same way.
Any ideas guys? I'm in a bit of a jam with this one!
First thing I'd do is try to make things simpler and reduce coupling by invoking the single responsibility principle, et al.
http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html
Put those Schemas into their own files, eg
models/client.js
models/assistant.js
models/contact.js
I've also found that embedded docs + mongoose is generally a PITA. I'd probably promote all those to top level docs.
You don't need to enclose your object's keys in quotes.
routes = {
list: function() {} // no quotes is aok
}
Also 'list' in typical REST apps is called 'index'. Anyway.
Ok, I'd break this up differently. Since you're requiring stuff from the index.js file in the middleware, they become tightly coupled, which is bad. in fact, I think I'd rewrite this whole thing so it was tidier. Sorry.
I'd probably replace your 'middleware' file with an express-resource controller
https://github.com/visionmedia/express-resource (built by author of express). This is a good framework for restful controllers, such as what you're building. The auto-loader is really sweet.
You may also want to look at: http://mcavage.github.com/node-restify/ It's new, I haven't tried it out, but I've heard good things.
Since what you're building is basically an automated mongoose-crud system, with optional overriding, I'd create an express-resource controller as your base
/controllers/base_controller.js
and it might look like
var BaseController = function() {} // BaseController constructor
BaseController.prototype.index = function() {
// copy from your middleware
}
BaseController.prototype.show = function() {
// copy from your middleware
}
BaseController.prototype.create = function() {
// copy from your middleware
}
// etc
module.exports = BaseController
Then I'd do something like:
/controllers/some_resource_controller.js
which might look something like:
var BaseController = require('./base_controller')
var NewResourceController = function() {
// Apply BaseController constructor (i.e. call super())
BaseController.apply(this, arguments)
}
NewResourceController.prototype = new Base()
NewResourceController.prototype.create = function() {
// custom create method goes here
}
module.exports = NewResourceController
Then to use it, you can do:
var user = app.resource(myResourceName, new ResourceController());
…inside some loop which sets myResourceName to be whatever crud you're trying to set up.
Here's some links for you to read:
http://tobyho.com/2011/11/11/js-object-inheritance/
http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/
Also, it sounds like you're not writing tests. Write tests.
http://www.codinghorror.com/blog/2006/07/i-pity-the-fool-who-doesnt-write-unit-tests.html