Angular, provider across multiple modules - javascript

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.

Related

How to access the .setup function on a feather-sequelize object?

I want to nest a service behind another one, just as described in the FAQ and this issue
As I understood, you need the .setup property to get access to the app object, on which you can add a listener that you link to your service. So far so good.
However, the service I would need to do that on is not a custom service, on which the setup property is readily available, but a feathers-sequelize service, which seems to be built else where, the .class.js is not even present.
Searching around, I ve seen you can still access the property with the protoype, but not I am reticent in modifying it without knowing it to be something supported.
TL:DR: How to nest a feather-sequelize service behind another one?
You can extend the existing Sequelize ES6 class as documented here:
const { Service } = require( 'feathers-sequelize');
class MyService extends Service {
setup(app, path) {
this.app = app;
// Do stuff here
}
}
app.use('/todos', new MyService({
paginate: {
default: 2,
max: 4
}
}));

Global Variable in Javascript for Laravel Routes - Is this a good idea?

I've created some code using a View Composer where I am passing my Route Collection through to the front end on all views, so I can access all of my laravel routes in Vuejs via the route named associated with them.
For example, to upload an image using a vue component, instead of passing my upload route into the Vue Component, it is listed as a part of a global variable:
var uploadRoute = _.find(globalRoutes, function(route) { return route.name == 'route-name.image.upload' });
$.post(uploadRoute, data) ... etc
My question is...is this sensible? I'm publically publishing my entire app's routes.
Thanks
I think your hunch about exposing your entire apps routes is legit. IMO you should explicitly pick out the routes that you need. So in thise case, you should only expose route-name.image.upload. You could create a tiny helper function to look up routes and output them along with the URL as JSON.
function json_routes(array $routes)
{
$return = [];
foreach($routes as $route)
{
$return[$route] = route($route);
}
return new \Illuminate\Support\HtmlString(json_encode($return));
}
And the, in your main view:
var routes = {{ json_routes(["route-name.image.upload"]) }};
Getting a route is simple:
routes['route-name.image.upload'];
This is the most basic exaple I can think of. You can optimize it quite a bit. Just some ideas:
Place the routes in a central place, fx. a config element: json_routes(config('app.json_routes'))
Build a command that generates a static .json file so that you don't iterate through the routes on each page load. Remember to re-generate when you add more routes.
Create a function instead of an object to get the route. That allows you to build in logic and gives a more Laravel-like feel in your js: function route(path){ return window.routes.hasOwnProperty(path) ? window.routes[path] : null ;}
(Advanced) Re-write Laravels router logic and hook into the options array, allowing you to do something like Route::get('dashboard', '...', ['as'=>'dashboard', 'expose'=>true]);, then dynamically generate the before mentioned json-file on all routes with the expose option.

Mediate and share data between different modules

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)
}

Using shared module (between separate modules)

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

In Ember.js, how do you hook an object into a generic application namespace

Let's say I declared an application namespace:
App = Ember.Application.create();
and later I write an arrayController instance that creates objects and hook it onto the app namespace on user event:
App.objController = Ember.ArrayController.create({
content: [],
createObj: function(){
// instantiate new object
var newObj = Ember.Object.create({ ... })
//give obj a name
var newObjName = this._getObjName( someParam );
// hook object to an app namespace -> this is where I have an issue
App[newObjName] = newObj
},
...
});
See I explicitly use App[newObjName] = newObj to hook the object onto the namespace, ideally I would like some sort of generic way to name the application namespace in case I use the objController for a different application later.
There has to be some way to do this though I am just not familiar enough with Ember to have encountered it.
Note: on a scale of 1 to JFGI, this question is definitely not a 1. On the other hand it's a free resolved checkmark for anyone that has a moment.
During the initialization phase, Ember will instantiate all of your controllers and inject three properties into each of them - "target", "controllers", "namespace". The "namespace" property is your application.
That said, instead of hard-coding the top-level object:
App[newObjName] = newObj
you can do the following:
this.get("namespace").set(newObjName, newObj);
Note - in order for this to work, your application needs a router. Also, you should define controller classes, not instances. Ember will instantiate all controllers for you. So, this
App.objController = Ember.ArrayController.create({/* code here */});
should be written as
App.ObjController = Ember.ArrayController.extend({/* code here */});
Note the capital "O" in "ObjController".
Consider using injections, which is the preferred way to add dependencies.
Ember.Application.registerInjection({
name: 'fooObject',
before: 'controllers',
injection: function(app, router, property) {
if (property === 'FooObject') {
app.set('fooObject', app[property].create());
}
}
});
So if you define a class as follows:
App.FooObject = Ember.Object.extend({
// ...
});
the injection will create an instance into App.fooObject. Although we still use the namespace App, however only once. You could further do:
Ember.FooObject = Ember.Object.extend({
// ...
});
and then in your App, App.FooObject = Ember.FooObject but I'm not sure if its useful.

Categories