This extensive answer on getting data out of an asynchronous call might be helpful, but the immediate problem is that your bati_loisirs
variable only exists within the scope of your process1
function. Once that function exits bati_loisirs
no longer exists.
So when you do map.hasLayer(bati_loisirs)
in your button click handler you're going to get an error. Here's a minimal demonstration of the problem. This doesn't work:
function a() {
let bati_loisirs = 'foo'; // only exists within function a
}
function b() {
console.log(bati_loisirs); // Uncaught ReferenceError: bati_loisirs is not defined
}
a();
b();
One remedy would be to move the bati_loisirs
variable declaration out to the global scope so it's available everywhere:
let bati_loisirs;
function a() {
bati_loisirs = 'foo'; // reassigning variable from outer scope
}
console.log(bati_loisirs); // 'undefined', hasn't been set yet.
a();
console.log(bati_loisirs); // 'foo', value changed by a() call
The primary problems with this approach are that it pollutes the global scope with your variables and has potential for variable name collisions. (If some other script also declares a bati_loisirs
variable, something's going to break.)
A better solution would be to move everything into the same scope, which in your specific case is pretty straightforward:
fetch('loisirs_all_repro', {
credentials: 'include'
})
.then(response => response.json())
.then(data => {
console.log(data);
// get the layer
const bati_loisirs = process1(data);
// add the click handler
$("#button3").click(function(event) {
event.preventDefault();
if (map.hasLayer(bati_loisirs)) {
$(this).removeClass('selected');
map.removeLayer(bati_loisirs);
} else {
map.addLayer(bati_loisirs);
$(this).addClass('selected');
}
});
});
function process1(donnees) {
// return the layer so the caller can use it
return L.geoJson(donnees, {
style: features => ({
weight: 12,
opacity: 0.1,
color: getColor(features.properties.Intensite),
dashArray: '1',
fillOpacity: 1
}),
});
}
This assumes you don't need the reference to bati_loisirs
anywhere else.
Finally, if you didn't want to do it all within the fetch, you could await the response:
async function setUp() {
const data = await fetch('loisirs_all_repro', {
credentials: 'include'
})
.then(response => response.json())
const bati_loisirs = process1(data);
// add the click handler
$("#button3").click(function(event) {
event.preventDefault();
if (map.hasLayer(bati_loisirs)) {
$(this).removeClass('selected');
map.removeLayer(bati_loisirs);
} else {
map.addLayer(bati_loisirs);
$(this).addClass('selected');
}
});
}
function process1(donnees) {
// return the layer so the caller can use it
return L.geoJson(donnees, {
style: features => ({
weight: 12,
opacity: 0.1,
color: getColor(features.properties.Intensite),
dashArray: '1',
fillOpacity: 1
}),
});
}
setUp();
Edit: Using Modules
Anything beyond the simplest toy apps can quickly become difficult to manage and maintain, which is why javascript has added support for modules, allowing you to break up your app into logical chunks, where each chunk (module) can focus on a specific thing.
I recommend you read the excellent MDN guide on modules, but here's a quick sketch of how you might apply it to your current task.
The basic idea is that you would put all of your layer management into one module, and only expose the parts that are useful outside the module itself.
So you might have a getLayer
function that your app could use to get a particular layer. All the fetching and parsing logic is abstracted away, hidden in the getLayer
implementation:
import { getLayer } from 'layers.js';
const bati_loisirs = getLayer('loisirs_all_repro');
// do stuff with bati_loisirs
You can set this up by creating two modules: 1) a "layers" module that exports "getLayers", and 2) an "app" module that uses the layers module. (Note that the names "layers" and "app" are arbitrary. You can call them whatever you want.)
In practice your layers.js
module might look something like this:
// layers.js
// A mapping of paths to layers. Not exported, so only visible within this module
const layers = {};
// exported. this is the only part of the module visible from the outside.
export async function getLayer(path) {
if (!layers[path]) {
// if we don't already have it do the fetch and add it to the mapping
layers[path] = await fetchLayer(path);
}
// return the layer for the given path from the mapping
return layers[path];
}
// "private" functions for fetching and processing data
async function fetchLayer(path) {
return fetch('loisirs_all_repro', { credentials: 'include' })
.then(response => response.json())
.then(data => process1(data))
}
function process1(donnees){
return L.geoJson(donnees,{
style: () => ({
weight:12,
opacity: 0.1,
color: getColor(features.properties.Intensite),
dashArray: '1',
fillOpacity: 1
}),
});
}
Then your app.js
module can import getLayers
and use it, as in the example above:
// app.js
import { getLayer } from './layers';
async function app () {
const bati_loisirs = getLayer('loisirs_all_repro');
// do stuff with bati_loisirs
}
app();
You can use this in the browser by including the main module in your html page and specifying type="module"
:
<script type="module" src="./app.js"></script>
I know this is a lot to take in. Hope this helps.