Where to place a (class) dependency in another javascript class? - javascript

I have a class called Tour that has a method with a dependency on another class called City
I can put the dependency at the top of the file, as in
const City = require './City'
class Tour {
...
but it seems a bit odd to have the dependency listed before the class like that.
I can move it to the method where I use it, as in:
insertAfter(afterCityName, newCityName, appendAtEnd=true) {
const City = require('./City');
const cities = this.cities;
...
but having it within a method also seems not ideal.
Ideally I would think the constructor but every attempt I've made has been the wrong syntax, for examples:
constructor(cities=[]) {
this.cities = cities;
...
const City = require('./City'); // No, assigned but never used
City = require('./City'); // No, City is not defined
this.City = require('./City'); // No, City is not defined
}
How to place it in the constructor (and still Capitalize The Class) ?

The convention I've seen and prefer is at the very top of the file. This makes it abundantly clear what your imports are. "Sneaky imports" in the middle of the file can lead to surprises.
When you're refactoring things and need to move files around it's easy to click through one file to the next quickly inspecting the top to check that the import paths are set correctly. This is not the case with inline imports.
Keep in mind require() is being retired, it's import from here on out, where import works best at the top of the file, as in:
import City from './City';
This style fits in well with other conventions like Python, Ruby, or C and C++ which use conventions like #include <city.h>.

Related

Requiring In Classes in Node

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

Allowing users to modify imported ES6 module functions in context

I'm getting rather confused as to if something is possible or not.
I create a module that contains the following:
export function logText(){
console.log('some text');
}
export class Example {
constructor(){
logText();
}
}
The intent is for the user to call new Example to start off the module logic.
import { logText, Example } from 'example';
// Do some magic here to modify the functionality of logText
new Example();
Is it possible for the end user to modify logText?
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
I frequently see repos with lots of functions exported that are useless without the users having to remake almost all the functionality manually, making it rather pointless to do. One good example is this repo whre theuy even call the exported functions their 'API'. In that example these are rather pointless exports and at worse would just cause issues if someone tried to use them in conjunction with the main function. But if you could modify them and have them still run then it would make sense to me.
Given this:
import { logText, Example } from 'example';
Is it possible for the end user to modify logText?
Since you aren't being very specific about what you mean by "modify logText", I'll go through several options:
Can you reassign some other function to the variable logText?
No. You cannot do that. When you use import, it creates a variable that is const and cannot be assigned to. Even if it wasn't const, it's just a local symbol that wouldn't affect the other module's use of its logText() anyway. The import mechanism is designed this way on purpose. A loader of your module is not supposed to be able to replace internal implementation pieces of the module that weren't specifically designed to be replaced.
Can you modify the code inside of the logText function from outside of the module that contains it?
No, you cannot. The code within modules lives inside it's own function scope which gives it privacy. You cannot modify code within that module from outside the module.
Can you replace the logText() function inside the module such that the implementation of Example inside that class will use your logText() function?
No, you cannot do that from outside the module. You would have to actually modify the module's code itself or someone would have to design the Example interface to have a replaceable or modifiable logText() function that the Example object used.
For example, logText() could be made a method on Example and then you could override it with your own implementation which would cause Example's implementation to use your override.
Code in the module that you do not modify:
export class Example {
constructor(){
this.logText();
}
logText() {
console.log('some text');
}
}
Code doing the import:
import { Example } from 'example';
class MyExample extends Example {
constructor() {
super();
}
logText() {
console.log("my own text");
}
}
let o = new MyExample();
Can you create your own version of logText and use it locally?
Sure, you can do that.
function myLogText() {
do your own thing
}
And, you could even NOT import logText so that you could use the symbol name logText() locally if you wanted. But, that won't affect what Example does at all.
Are there ways to design the example module so that that logText() can be easily replaced.
Yes, there are lots of ways to do that. I showed one above that makes logText a method that can be overriden. It could also be passed as an optional argument to the Example constructor.
There could even be an exported object that allowed the caller to replace properties on that object. For example:
export const api = {
logText: function logText(){
console.log('some text');
}
};
export class Example {
constructor(){
api.logText();
}
}
Then, use it like this:
import { api, Example } from 'example';
api.logText = function() {
console.log('my Text');
};
I would generally not recommend this because it sets you up for usage conflicts between multiple users of the same module where each one tries to modify it globally in ways that conflict with each other. The subclassing model (mentioned above) lets each use of the module customize in its own way without conflicting with other usages of the module.
Is it possible for the end user to modify logText?
No, that's not possible, import bindings are immutable, and function objects are basically immutable wrt the code they contain.
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
Why not make the log function an optional argument in the constructor? Usually when something is variable it becomes a parameter.
export class Example {
constructor(log=logText){
log();
}
}

Access javascript variable in component in Angular 5

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.

js get filename of file containing class definition

I would need to get the filename of an imported class:
fileA.js
export default class User {
}
fileB.js
import User from './fileA'
function getClassFilename(constructor) {
// do something like __filename, but to get the filename where User is defined rather than the current filename
}
console.log(getClassFilename(User.constructor)) // fileA.js
This is the general idea. However the actual use case is based on decorators:
fileA.js
import someDecorator from './decorator'
#someDecorator
class User {
}
decorator.js
export default function (target) {
// can I somehow get the target filename without passing it as a property?
}
That information isn't available to you by default, the module in question would have to provide a means of accessing the information.
You've mentioned __filename so I'm assuming you're using Node. The module providing User could provide that information like this:
export const SourceFilename = __filename;
Note taht there's no in-spec way to do that without Node's __filename (but there's one under consideration and reasonably far down the path toward being added).
Updated answer for updated question: There's nothing stored on the class (constructor) User that provides this information. So again, the code defining User would need to provide that information (as a property on User, as something you can get from the module and pass separately to the decorator, etc.). Otherwise, it simply isn't available to you.

Node.JS - Using prototype in a module

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

Categories