I am trying to require in some classes in Node, but I require them in through a single file which holds an object of all the individual requires. Like so -
File: /classes/controller.js
module.exports = class Controller {}
File: /classes/model.js
module.exports = class Model {}
File: /classes/classes.js
module.exports = {
Controller: require('./controller.js'),
Model: require('./model.js')
}
Then in app.js
const classes = require('./classes/classes.js');
....code.....
....more code.....
const someController = new classes['Controller'];
The thing is, this works just fine - but only for the first file that requires in the 'classes' variable. Any subsequent files that need to also require 'classes' in, ends up holding only an empty object inside of the 'classes' variable (and no, the actual variable name does not matter, as I tried using a different variable name in the second file but it still wound up storing an empty object). For instance, I often need my Controller to implement a Model class.
So once app.js calls controller.js, inside of that -
File: /classes/controller.js
const classes = require('./classes.js');
module.exports = class Controller {
constructor(var) {}
someMethod() {
....code....
const someModel = classes['Model'];
}
}
I can call a 'console.log(classes)', and it will print out '{}', but again this is only if it is done inside of 'controller.js' and only after being called from inside of 'app.js'. It will print out all of the classes as expected if the 'console.log()' is done either inside of 'app.js', or if 'controller.js' is called directly instead of through 'app.js'. Which is how I know where/how the error is occurring. The specific error being thrown is -
classes.Model is not a constructor
So that is where my question/problem comes in, because as I said I have called other objects in multiple files without ever running into this problem. So it must have something to do with how classes are implemented, correct? And although I do not understand the specifics of how it works, I roughly know that when a file is required in, in Node, after that it uses that same reference for any subsequent files - correct? So I was thinking it must somehow be the interplay of those two things, but I have no idea after that fact nor if I am even correct in assuming that is the problem.
You have a circular dependency of require() statements. This causes one of the require() to just return an empty object. You can't do that.
Inside of controller.js, you have require('./classes.js');.
Inside of classes.js, you have require('./controller.js').
So, each refers to the other which is what causes the circular dependency.
You will need to restructure the way code is laid out into files so you don't have this. Sometimes, the simplest fix is to just combine a couple functions into one file rather than having them all in separate files that are vulnerable to this issue. I don't quite understand your overall design to know what best to suggest in this case.
If nobody else besides controller.js is really going to use classes.js, then you can just move that code into controller.js (where you don't need to require in controller.js any more).
Related
I have a category dropdown(in parent js) whose subcategory fills on parent page load as well on dropdown change . subcategory will fill from child js method .I have to create child js instance twice . on page load and on dropdown down change.
I dont want to create object in document.ready or as global variable
where should i create child class object exactly so that it can be used all over ?
problem is that jquery not letting me call
$.getScript('../Reports/assets/js/BookingReports.js'
twice as it send error that child class name(BookingReports) identifier as already created .
class ReportsInterface extends ReportBase {
constructor() {
super();
this.fillSubCategory;
}
init() {
this.Categories();
}
Categories() {
//fill category
this.FillSubCategory();
}
FillSubCategory() {
if(!this.fillSubCategory) {
$.getScript(
'../Reports/assets/js/BookingReports.js',
function() {
this.fillSubCategory=new FillSubCategory("1");
obj.GetSubCategory();
}
)
}
}
}
$(document).ready(function() {
$("#ddlcategory").on('change', function() {
(new ReportsInterface()).showReportBooking();
})
})
i also tried to save object in parent class property but .cannot use it as object later on. how can I call child class method twice without creating any global variable ?
If you are using ES6, I would recommend not using JQuery to import separate files but rather using the ES6 import/export syntax.
I imagine the issue is that since $.getScript makes an http request to redownload the script file, it is actually running the script file twice (one for each download); in the second download, it will run into the naming conflict. ES6 import/exports would solve this issue for you, preventing BookingReport from being redefined.
You should be aware of a couple of things however:
(1) Using your JQuery setup, you get the benefit of lazy loading. To get the same in ES6, you'd have to use the slightly more complicated dynamic imports (see that same link above) -- for this app, however, it doesn't really look like you'd need that.
(2) You might want to familiarize yourself with a bundler like Webpack as this will do ahead-of-time importing and leave you with a single file to download rather than having to ping-pong back and forth from the server as you try to download all of the modularized files.
I have a js file in my Angular application, data.js . This js file has some variables declared in it, something like below.
var data = 'test'
Now I have to access these variables and their values in my component (app.component.ts).
I read some where that declaring them as exports make them into modules and those can be accessed anywhere, But I'm not sure how this can be done.
This is the structure of my application. I have data.js in assets->js folder.I need to modify the variable value in app.component.ts.
I'm very new to Angular. Is this even possible?
With the file in your assets, I am guessing you are declaring it on the window. You will need the include the script in your index.html, and then access it on the window within your component via window.data. This is not really the recommended way of doing this unless your use case dictates it. The module approach you mentioned is preferred.
Next to your app.component.ts, create a file called data.ts, with:
export let data: string = 'data';
In your app.component.ts, import it using:
import { data } from './data.ts';
If you plan to not mutate that data, consider using the const keyword instead (in data.ts).
Directory structure
/app.component.ts
/data.ts
/...
Edit: Show Global Approach
You will need to include your script outside of the context of the Angular application. If you bootstrapped your application using the Angular CLI, you can add a reference to it in the cli configuration file. See this documentation on the topic.
That file will be included and will be available for access within your component on the window. The tricky part comes with typing and the Window. And example may look like this.
class AppComponent extends Component {
private data: string;
constructor() {
// Explicitly cast window as an any type. Would be better to type this, but this should work for you.
this.data = (<any>window).data;
}
}
(referrring to https://stackoverflow.com/a/42682160)
first you have to include the script into your src/index.html like
< script src="/assets/js/data.js">< /script>
important is that the above statement is placed before your angular root component tags
(< root-component>< /root-component> or < ion-app>< /ion-app> or something like that)
then you can simply write (for example inside app.component.ts ngOnInit function)
let varFromJsFile = window["data"] // varFromJsFile = 'test'
You want the variable to be a member of a Component class, not just a variable declared anywhere within a module.
If this doesn't make sense right away, you need to look more carefully at some basic Angular code samples.
Also, as long as you're using Angular and therefore TypeScript, it's better the declare variables using let or const.
What is the best practice way to define static class private properties in JavaScript ES6 in Node.js?
I have the following Log class:
'use strict';
const moment = require('moment');
const LOG_DATE_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
module.exports = class {
static debug(txt) {
console.log(`${moment().utc().format(LOG_DATE_FORMAT)} [debug] ${txt}`);
}
static info(txt) {
console.info(`${moment().utc().format(LOG_DATE_FORMAT)} [info] ${txt}`);
}
static warn(txt) {
console.warn(`${moment().utc().format(LOG_DATE_FORMAT)} [warn] ${txt}`);
}
static error(txt) {
console.error(`${moment().utc().format(LOG_DATE_FORMAT)} [error] ${txt}`);
}
};
Is defining moment and DATE_FORMAT outside of the module.exports and thus class definition the correct way of doing this?
Yes, it's an acceptable way of doing things. Since you want to define some variables that you share among all your static methods, you have basically four choices:
Define them as statics on the class also so you can reference them using the class name as a prefix. This has the benefit or drawback (depending upon what you want) that it makes them publicly accessible on your exported class.
Define them as module level variables as you've done. This makes them accessible by all the static methods, but keeps their use private to your module.
Redefine them separately inside of each method.
Define them as globals.
Option #3 seems like a lot of repetition and certainly wouldn't be very DRY.
Option #4 does not seem like a good way to go since there's no reason to make these globals (you pretty much never want to use globals with node.js anyway).
So, it comes down to option #1 or #2 and which to choose totally depends upon whether you want their values to be accessible outside the module or not. If you want them exported also, then make them static properties on your class and refer to them with the class prefix. If you only want to use them inside the module, then what you have done is perfect.
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
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