I am trying to use the region variable inside the callback however there is no way I can get the value of it.
As you might be already aware I am instantiating a JS objects using TS and the callback is part of the definition of this JS object.
I am already being able to use JS in my Angular components using () => { in those methods where I need to use any reference to 'this' inside JS callbacks but for this particular case it does not work at all.
showRegion = (region: string) => {
var service = new JS.service();
var prov = new JS.provider(service, {
callback: (data) => {
// cannot read region variable
}
})
Thanks,
Related
So, this is my current problem:
I have locations.js file that has several functions inside with different town/area names and they all include few switches. This is example:
let Locations = {
town1: function (eventType) {
switch (eventType) {
case "load":
// Stuff related to main starting point of this location
break;
}}
They have been all nice for everything else, but now I'm trying to create a "main access" when starting the program. Let's call main file as manager.js
let Manager = {
setStart: function () {
currentLocation = user.currentLoc;
// Call load function based on current location to start program from right spot
Locations.currentLocation("load");
}
Result:
TypeError: Locations is not a function
As you see, I want to call function with user.currentLoc information (town names), which are saved to database. But it seems like I cant add it to variable and just use it. What am I missing here? Do I type it wrong when calling a function? I'm quite sure that there's some easy solution to this, but even after several hours I still fail to do this right.
You are trying to access to a property field of the Locations object. This field is a function, however, if you are trying to call this function by the name of a variable, the Javascript interpreter will take it as a direct call of the function.
let manager = {
setStart: () => {
const currentLocation = user.currentLoc;
// Access the Locations property
const fn = Locations[currentLocation];
// Invoke the function
fn(`load`);
}
};
Hope it helps.
Perhaps Locations[currentLocation]('load') is what you mean to call, but all the same - it looks like Locations isn't in scope in your Manager file, you need to import it somehow. That could be a missing require or import depending on your project
let manager = {
setStart: () => {
const currentLocation = user.currentLoc;
// Access the Locations property
const fn = Locations[currentLocation];
// Invoke the function
fn(`load`);
}
};
This did it! Thanks VRoxa. I had no idea that Javascript works like this.
I've run into an issue where I'm unable to get the value I need from inside a callback that's passed to an instantiation of a JavaScript class.
let value;
const mutationObserver = new MutationObserver((mutations) => {
return values.map((mutation) => {
value = mutation.target.childElementCount;
return value;
})
})
console.log("value", value) <= undefined
const element = document.getElementsByClassName('pac-container')[0];
if (element) {
mutationObserver.observe(element, {
childList: true,
subtree: true
});
}
I'm using the MutationObserver because I'd like a way to know, in real-time, how the DOM is being mutated by the results from the googlePlaces API. Since I don't have control over that markup - this seems like the next best thing.
I have explored using the AutoCompleteService - but that would require us to re-implement the existing googlePlaces using the AutoCompleteService call, instead of the AutoComplete API endpoint.
The approach I'm using here is in perfect sync with the results.
I don't think I've run into before - and the difference here is that the value is set inside this instance of a class.
How do you allow the value retrieved inside the class to be defined outside the scope of the function inside the class where it's handled?
We're making the call to the googlePlaces API like this:
const autoComplete = new this.googleApi.places.Autocomplete(
this.props.inputRef,
INIT_OPTIONS
);
An object is returned after the call - and I can see the request being made - and there is new data after each search - but there is no clean JSON response - and the data is embedded in some optimized way deep in the object.
I am currently looking into utilizing mostly pure functions in my code. I realize the benefits of this. However, I'm wondering what the best practice is when updating a state variable, calling methods defined within a module, and how to handle window functions.
Using the example below, let's say I have a module, MyModule.
The MyModule.calculate function is pure, so no worries there.
The MyModule.trackSomething function however is updating the MyModule.count to keep track of it for use in the event handler, used in MyModule.assignEventHandlers function. This is not pure because it is using an outside reference to a variable. Even if I refactor to make count an object and pass it in as a parameter to MyModule.trackSomething, I'd still be altering the value through reference, making it impure.
In the MyModule.assignEventHandlers function, I'm using global window object functions. This makes it impure because I'm referencing outside functions. Should the window object be passed by reference when calling functions it has?
In the MyModule.init function, I'm calling the MyModule.assignEventHandlers function. I guess this makes it impure as well since MyModule.assignEventHandlers is an outside reference.
So my question is: What is the best way to handle the last 3 bullet points?
Any advice is much appreciated. Thanks!
const MyModule = {
calculate(a, b) {
return a + b;
},
count: 0,
trackSomething() {
MyModule.count += 1;
},
assignEventHandlers() {
// assign event handlers to some DOM elements
window.document.getElementById('my-ele').addEventListener('click', () => {
window.alert(MyModule.count);
MyModule.trackSomething(MyModule.count);
});
},
init() {
MyModule.assignEventHandlers();
}
};
I think when we came out with a solution, instead of just focus on pure and impure function, it's better to focus on practicability as well. It's okay to have impure function.
Here is another version of the implementation using RxJs - a reactive js lib. Also using ES6 class instead of module. Take a look at action and action2. Both produce same result in different ways.
In my opinion, action is good enough. Don't need to make everything super pure.
class MyClass {
constructor(elementId) {
this.ele = document.getElementById(elementId);
this.click$ = Rx.Observable.fromEvent(this.ele, 'click');
this.count = new Rx.BehaviorSubject(0);
}
calculate(a, b) {
return a + b;
}
action() {
window.alert(this.count.getValue());
const nextVal = this.calculate(this.count.getValue(), 1);
this.count.next(nextVal);
}
action2(log, subject, method) {
log(subject.getValue());
const nextVal = method(subject.getValue(), 1);
subject.next(nextVal);
}
}
const a = new MyClass('my-ele1');
a.click$.subscribe(() => a.action2(window.alert, a.count, a.calculate));
const b = new MyClass('my-ele2');
b.click$.subscribe(() => b.action());
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
<button type="button" id="my-ele1">me1</button>
<br>
<button type="button" id="my-ele2">me2</button>
I am working with dojo in the ESRI Web App Builder and have come across a situation where I need to run an AJAX call and still access a variable from the base class. Below is my code with comments in it to explain exactly where it is successful and exactly where it is failing:
define(['dojo/_base/declare', 'jimu/BaseWidget', 'dojo/request', "esri/layers/WMSLayer", "esri/config", "dojo/domReady!"], function (declare, BaseWidget, request, WMSLayer, esriConfig) {
return declare([BaseWidget], {
baseClass: 'jimu-widget-mywidget',
// This function is called by a button press (Normally the WMSLayer variable would be set by user input)
addWms: function () {
var wmsLayer = new WMSLayer("http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer", {
format: "png",
visibleLayers: [2]
});
this.map.addLayer(wmsLayer); // this.map is inherited from BaseWidget as far as I can see. This adds a wms to my map without error
request("request.html").then(function(data){
var wmsLayer = new WMSLayer("http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer", {
format: "png",
visibleLayers: [2]
});
this.map.addLayer(wmsLayer); // This is now in another context....I get the error HERE.
// At this point map is not defined because this anonymous function is running
// in a different context. ( at least I think that's what is happening )
}, function(err){
// Hopefully there are no typos in my example XD
});
}
});
});
My question is --> How do I access the "map" variable from withing the callback function of "request"?
I want to be able to run this.map.addLayers from my call to the GetCapabilities of a WMS service. The request would normally call it and I get all the way to the end of my code until I can't access the "map" variable anymore as I know it.
Dojo type answer is preferred but plain old javaScript is also fine. Please avoid JQuery answers.
Resources are:
ESRI JavaScript library
Dojo
ESRI Web App Builder
The problem you're running into is the classic problem of execution context being lost when an asynchronous callback is invoked (and thus this no longer means what you want). There are generally two ways to resolve this.
One way is to create a variable in the outer scope that references what this is, so that the inner function can access it:
var self = this;
request('request.html').then(function (data) {
// ...
self.map.addLayer(wmsLayer);
});
The other way is to use context binding, either via Function#bind (ES5) or dojo/_base/lang.hitch:
// Function#bind:
request('request.html').then(function (data) {
// ...
this.map.addLayer(wmsLayer);
}.bind(this));
// lang.hitch:
request('request.html').then(lang.hitch(this, function (data) {
// ...
this.map.addLayer(wmsLayer);
}));
It's also common with the binding approach to break out the asynchronous handler function to another internal instance method:
_addRequestedLayer: function () {
// ...
this.map.addLayer(wmsLayer);
},
addWms: function () {
// ...
// ES5:
request('request.html').then(this._addRequestedLayer.bind(this));
// ...or lang.hitch, with late binding:
request('request.html').then(lang.hitch(this, '_addRequestedLayer'));
There is also a tutorial on lang.hitch.
In your request callback, this does not refer to your class anymore. A simple way to have access to the map in the callback is assigning the reference of the map in a variable on addWms function scope and then using it in your callback:
addWms: function() {
var map = this.map;
// Your code here
request('request.html').then(function (data) {
// Your code here
map.addLayer(wmsLayer); // Note that you're using map instead of this.map
}
}
You can also use Dojo hitch function, where you can pass a function and the context that it should be applied. I suggest that you take a look at this link http://dojotoolkit.org/reference-guide/1.10/dojo/_base/lang.html#hitch.
EDIT:
Everything is working as I expected. It was just an error calling the template method. I mistyped a () so I was trying template.method instead of template().method;
Anyway, if somebody would like to explain me if this is a valid design pattern or if I should go in a different way I will be definitively very grateful.
I read about the module pattern and I'm trying to implement it in some of my projects. The problem is that, in my opinion, I'm twisting it too much.
I'm inspired by the google apps script style where many objects returns other objects with methods and so on and they pass arguments.
something like
object.method(var).otherMethod();
What I want to achieve is a method that receives a parameter, sets an internal variable to that parameter and then returns an object with methods that uses that variable. Here is a minified version of the code that does not work:
var H_UI =(function (window) {
var selectedTemplate,
compileTemplate = function(){},
parseTemplateFields = function(){};
//template subModule. Collect: collects the template fields and returns a JSON representation.
var template = function(templateString){
if(templateString) selectedTemplate = templateString;
return {
getHtml:function(){ return compileTemplate( parseTemplateFields( selectedTemplate ) ) } ,
collect:function(){
.. operating over selectedTemplate ...
return JSON.stringify(result)}
} };
return {
template:template
};
})(window);
If I remove the line :
if(templateString) selectedTemplate = templateString;
and replace selectedTemplate with the parameter templateString in the methods of the returned object it works as expected. I know that I cant create a set() method in the returned object and use it like this
H_UI.template().set(var)
But I find it ugly. Anyway I think that I'm messing things up.
What is the best way to construct this?
If you want H_UI.template() creates a new object every time you call template() on it, your solution does not work. Because the variable selectedTemplate is created only once when the immediate function is called.
However if your intent is this your solution works fine. (variable selectedTemplate is shared for all calls to template()).
But if you want to every call to template creates a new object. Please tell me to write my idea
Is this a valid design pattern or if I should go in a different way
Yes, enabling chaining is definitely a valid design pattern.
However, if your template() method returns a new object, that object and its methods should only depend on itself (including the local variables and parameters of the template call), but not on anything else like the parent object that template was called on.
So either remove that "global" selectedTemplate thing:
var H_UI = (function () {
function compileTemplate(){}
function parseTemplateFields(){}
// make a template
function template(templateString) {
return {
getHtml: function(){
return compileTemplate(parseTemplateFields(templateString));
},
collect: function(){
// .. operating over templateString ...
return JSON.stringify(result)
}
}
}
return {template:template};
})();
or make only one module with with a global selectedTemplate, a setter for it, and global methods:
var H_UI = (function () {
var selectedTemplate;
function compileTemplate(){}
function parseTemplateFields(){}
return {
template: function(templateString){
if (templateString)
selectedTemplate = templateString;
return this; // for chaining
},
getHtml: function(){
return compileTemplate(parseTemplateFields(selectedTemplate));
},
collect: function(){
// .. operating over selectedTemplate ...
return JSON.stringify(result)}
}
};
})();
The difference is striking when we make two templates with that method:
var templ1 = H_UI.template("a"),
templ2 = H_UI.template("b");
What would you expect them to do? In a functional design, templ1 must not use "b". With the first snippet we have this, and templ1 != templ2. However, if .template() is a mere setter, and every call affects the whole instance (like in the second snippet), we have templ1 == H_UI and templ2 == H_UI.