I'm trying to create an instance of a class, this class only sets up listeners but there is no need to call any methods manually in the class.
Below is the module I load in my html file, I create a simple MVC-Pattern but I get an eslint error on line 4.
I need the constructor of the class GameController to run in order to setup the listeners but for that I need an instance of it.
I've tried making a dummy function in the GameController class and calling that. Gets rid of the error but obviously that's not the way to go, I'm looking for a clean alternative.
function init() {
let data = new GameData(),
view = new GameView(data),
controller = new GameController(data, view);
}
init();
class GameController {
constructor(data, view) {
this.data = data;
this.view = view;
this.letterGenerator = LetterGenerator();
this.view.addEventListener(Config.EVENT.CONSONANT, this.onConsonantClicked
.bind(this));
this.view.addEventListener(Config.EVENT.VOWEL, this.onVowelClicked.bind(
this));
this.view.addEventListener(Config.EVENT.STOP, this.onGameEnd.bind(this));
}
onVowelClicked() {
...
}
onConsonantClicked() {
...
}
onGameEnd(event) {
...
}
}
What I want is instantiate a GameController but not use it, eslint basically forces me to make a dummy function in the GameController class that I need to call to get rid of the error.
Related
I'm trying to make a non-component class in ReactJS. Most tutorials I've followed on JavaScript show that when you're making a class, you can create properties simply by specifying them with this.[name].
Something really doesn't like this, and it throws me a no-undef error for my properties.
Here is the code in question:
class MicroBitConnection {
constructor(device) {
this.device = device;
this.connectDAP();
}
connectDAP() {
this.transport = new DAPjs.WebUSB(device);
this.dap = new DAPjs.DAPLink(transport);
}
}
Right now I don't even instantiate the class anywhere, it gives me this error right off the bat:
Line 11:43: 'device' is not defined no-undef
Line 12:44: 'transport' is not defined no-undef
It doesn't seem like a linting problem, because even with this warning off using /* eslint no-undef: 0 */ // --> OFF at the top of the file, actually running the code with the class instantiated somewhere gives the same error:
Error: ReferenceError: device is not defined
In your connectDAP method you are referencing undefined variables device and transport. You probably want to use the properties defined on the instance this:
class MicroBitConnection {
constructor(device) {
this.device = device;
this.connectDAP();
}
connectDAP() {
this.transport = new DAPjs.WebUSB(this.device);
this.dap = new DAPjs.DAPLink(this.transport);
}
}
Please also note that this is not automatically bound to the instance, e.g. when using an instance method as an event handler. To make sure it's bound to the instance in all circumstances, call Function.prototype.bind in the constructor:
this.connectDAP = this.connectDAP.bind(this);
If you want to omit this you need to locally declare the variables, which can easily be done using destructuring:
class MicroBitConnection {
constructor(device) {
this.device = device;
this.connectDAP();
}
connectDAP() {
const { device } = this;
this.transport = new DAPjs.WebUSB(device);
const { transport } = this;
this.dap = new DAPjs.DAPLink(transport);
}
}
A third alternative (not recommended) is using with statement:
class MicroBitConnection {
constructor(device) {
this.device = device;
this.transport = null;
this.dap = null;
this.connectDAP();
}
connectDAP() {
with (this) {
transport = new DAPjs.WebUSB(device);
dap = new DAPjs.DAPLink(transport);
}
}
}
I'm not sure this last version does what you expect, just listing it for completeness' sake and to make you aware of the existence of the with statement.
I have the following scenario.
I have a simple angular 2 app with a service which I add to providers in app.module. When I click on a button the app should load a javascript file and execute a function e.g function A defined in this javascript file. So the question is how can I access the service within this function A. My consideration is to append the service to the global window variable.
Are there better ways to achieve it?
Code:
export class MyService{
public editProjectDone = new Subject<string>();
}
app.module.ts
{
...providers: [MyService]
}
app.component.html
<button (click)="editProject()">Edit project</button>
app.component.ts
function editProject(){
... load Javascript file
call javascript file
js.editProject("projectId") // call function defined in javascript file
}
javascript file
{
function editProject(projectId)
{
//do calculation
// fire event that calculation is done
// the calcuation is not done in typescript, but here
MyService.editProjectDone.next()
// The question is here how to access the event and fire it
}
}
So you want to access angular service method in javascript function A().
For example:
Your service class:
export class SettingsService{
getLanguage() {
return 'en-GB';
}
}
Your javascript file
function A() {
settingsService.getLanguage();
}
Solution: Custom Event.
Basically you define a custom event handler in javascript file. And define the Custom Event and dispatchEvent the Custom Event in Angular click event function.
app.component.html:
<input type="button" value='log' (click)="logclick($event)">
app.component.ts
constructor(private settings: SettingsService){}
logclick(event){
// define custom event
var customevent = new CustomEvent(
"newMessage",
{
detail: {
message: "Hello World!",
time: new Date(),
myservice: this.settings //passing SettingsService reference
},
bubbles: true,
cancelable: true
}
);
event.target.dispatchEvent(customevent); //dispatch custom event
}
javascript file:
// event handler function
function newMessageHandler(e) {
console.log(
"Event subscriber on "+e.currentTarget.nodeName+", "
+e.detail.time.toLocaleString()+": "+e.detail.message
);
//calling SettingsService.getLanguage()
console.log(e.detail.myservice.getLanguage());
}
//adding listener to custom event.
document.addEventListener("newMessage", newMessageHandler, false);
Example output:
Event subscriber on #document, 9/11/2018, 11:31:36 AM: Hello World!
en-GB
Note: I have not added section for dynamically loading javascript file. I assume you are already able to do that from your comments.
Declare variable as public using window object but in proper way. export only some functions not whole service and in some standard way like below.
In Angular
export class AbcService {
constructor() {
const exportFunctions = {
xyzFunction: this.xyzFunction.bind(this),
pqrFunction: this.pqrFunction.bind(this)
}; // must use .bind(this)
window['ngLib']['abcService'] = exportFunctions;
}
xyzFunction(param1, param2) {
// code
}
pqrFunction() {
// code
}
private oneFunction() {
// code
}
private twoFunction() {
// code
}
}
In Javascript
ngLib.abcService.xyzFunction(value1, value2);
ngLib.abcService.pqrFunction();
Firstly you need to import the js file before calling the service and that can be done by:
TS
let js: any = require('JSFileNAME')['default']; //Something lik this in constructer
then once the file is imported you need to create a instantiate of the js in your Ts
something like
this.newObjectOfJs = new js(); // pass any paramater if its required.
hereafter you will be able to access the methods and service from the JSfile.
You have to use service variable to access functions and variable of particular services.
Here I demonstrate how you can do it.
export class AppComponent {
name = 'Angular';
constructor(private myServices: MyServices){
this.myServices.sayHello();
}
}
Whole code available here : https://stackblitz.com/edit/angular-services-example
Say I have a .js file where the class is exported and the constructor is built like this:
constructor(info) {
this.info = info;
}
And on another .js file, I want to call that variable and change it, and I want the changes to be reflected in the original .js file the variable comes from, so I import the class well and inject it:
#inject(ContactGateway, Router, TestElement)
export class LoginNormal {
constructor(contactGateway, router, testelement) {
this.contactGateway = contactGateway;
this.router = router;
this.testelement = testelement;
}
And then on this same .js file, inside a function, I change the original variable:
TestInfo() {
let testinfo = this.testelement;
testinfo.info= true;
}
Upon further testing, I see that the original variable isn't being changed at all, what am I doing wrong while trying to change the original variable's boolean through a function in another file?
Thanks in advance.
You're probably just getting a different instance of TestElement injected. Custom Elements are usually scoped to a specific child container or view (depending on the context).
If you want to ensure you get the same instance everywhere, and you're sure you'll only ever need one instance, you could manually register that custom element's class as a singleton on the root container.
It's better though to simply have a separate service / state class where you keep that info property. Register that state class as a singleton / instance on the root container, and inject it in both your TestElement and LoginNormal classes. That's the recommended way to pass information around for reading/modifying between different html resources.
Something like this should typically do the trick (split/move/rename accordingly):
#inject(ApplicationState)
export class TestElement {
constructor(state) {
this.state = state;
}
}
#inject(ContactGateway, Router, ApplicationState)
export class LoginNormal {
constructor(contactGateway, router, state) {
this.contactGateway = contactGateway;
this.router = router;
this.state = state;
}
TestInfo() {
this.state.info = true; // will be changed in your TestElement too
}
}
// the hard way
export class ApplicationState {
constructor(info) {
this.info = info;
}
}
export function configure(config) {
config.instance(ApplicationState, new ApplicationState(false))
}
// or the easy way (no configure required, but needs parameterless constructor)
#singleton()
export class ApplicationState {
constructor() {
this.info = false;
}
}
I am creating a PDF like this inside a react Component.
export class Test extends React.PureComponent {
savePDF() {
const source = document.getElementById('printContainer');
/* eslint new-cap: ["error", { "newIsCap": false }]*/
let pdf = new jspdf('p', 'pt', 'letter');
let margins = { top: 50,
left: 60,
width: 612
};
pdf.fromHTML(
source,
margins.left,
margins.top,
{
width: margins.width
},
() => {
pdf.save('worksheet.pdf');
}
);
}
and I am getting warning Expected 'this' to be used by class method 'savePDF' class-me
this is being called an click like this onClick={this.savePDF} see below
render() {
<Link
name="save-to-pdf"
onClick={this.savePDF}
button="secondary">
Save to PDF</Link>
<div id="printContainer" className="cf-app-segment--alt cf-hearings-worksheet">...
There are two different answers to this question, depending on how you want to handle it.
First, the reason you get this error is because of the ESLint rule https://eslint.org/docs/rules/class-methods-use-this. Specifically, this is because if something is a class method, e.g. if you are calling this.foo() to call a function, the whole reason to make it a method is because there are properties on this that you need to use.
While in many languages with class, most functions are methods, that is not the case in JS. If you have a class like
class Example {
constructor(){
this.data = 42;
}
someMethod() {
this.someHelper(this.data);
}
someHelper(value){
console.log(value);
}
}
the someHelper function would trigger the same error you are getting, because it never uses this, so you can just as easily do
class Example {
constructor(){
this.data = 42;
}
someMethod() {
someHelper(this.data);
}
}
function someHelper(value){
console.log(value);
}
In your case, you can do this. Your whole savePDF function could be moved outside of the class object.
That said, it is important to ask yourself why something like this isn't using this. In most cases, you'd expect any function that works with HTML to absolutely use this, because how else, in React, is it supposed to access the element's that React has created.
So the real answer to your question would be to drop the
const source = document.getElementById('printContainer');
line. If you need access to the HTML element being created by React, you should be using React's APIs to do so. That would be done with something like
class SavePDFButton extends React.Component {
constructor(props) {
super(props);
this.printContainer = null;
this.savePDF = this.savePDF.bind(this);
this.handlePrintContainerRef = this.handlePrintContainerRef.bind(this);
}
render() {
return (
<div>
<Link
name="save-to-pdf"
onClick={this.savePDF}
button="secondary"
>
Save to PDF
</Link>
<div
id="printContainer"
className="cf-app-segment--alt cf-hearings-worksheet"
ref={this.handlePrintContainerRef}
/>
</div>
);
}
handlePrintContainerRef(el) {
// When React renders the div, the "ref={this.handlePrintContainerRef}" will
// make it call this function, which will store a reference.
this.printContainer = el;
}
savePDF() {
// OLD: const source = document.getElementById('printContainer');
const source = this.printContainer;
// ...
}
}
I believe that's caused by the class-methods-use-this ESLint rule.
It's just letting you know that your function doesn't use this, so you can probably make it a static function.
turn it into static function
static savePDF() { ... }
Its happening because this function isnt using this meaning it dosnt need to be dynamic
This is probably a stupid question, but is there way in Javascript (ES5 preferred) to "extend" a class function similar to how i can i extend a parent' function in PHP ?
Basicly, i have this class hierarchy from System -> Weapon -> Dual and i would like Dual to use the code from System.setState() and then do some more stuff.
Note i use pre ES6 syntax for my hierarchy.
function System(system){
this.setState = function(){
//do stuff
}
}
function Weapon(system){
System.call(this, system);
}
Weapon.prototype = Object.create(System.prototype);
function Dual(system){
Weapon.call(this, system);
this.setState = function(){ // this is the problem
System.prototype.setState(); // error - not defined
//Weapon.protoype.setState() doesnt work either
//do more stuff
}
}
Dual.prototype = Object.create(Weapon.prototype);
Because setState is an instance property of System it does not exist on System.proptotype so you can't call it using System.prototype.setState.call. If you want to call it in this case, just create an object from System like so
function Dual(system){
Weapon.call(this, system);
var parent = new System(system);
this.setState = function() {
parent.setState(); // done
}
}
Instance properties are duplicated on each individual object ( they don't share). Whereas, prototype properties will be shared among children( they are not duplicated on child classes). To make all System 's subclasses share setState function, add it to System 's prototype
function System (arg) { ... }
System.prototype.setState = function () {...}
Now in your child classes, you can do
function Dual(system){
Weapon.call(this, system);
this.setState = function() {
System.prototype.setState.call(this); // done
}
}
First, you should set your instance methods on the prototype:
System.prototype.setState = function() {
// your stuff
}
This will improve performance and allow you to inherit the method without constructing a System instance.
Then, you just need to call System's version of setState on the right object (the instance of Dual) instead of calling it on System.prototype:
Dual.prototype = Object.create(Weapon.prototype, {
'setState': { value: function(){
System.prototype.setState.call(this) // fixed
// other stuff
}}
})