5m1Ly
1
I ran into this problem. but instead i figured out a way to dynamically load modules in the user interface. Which you are able to interact within 1 or more files.
fxmanifest.lua
-
minimum contents to follow
fx_version 'cerulean'
game 'gta5'
author 'Sm1Ly ~ 5 Pixel'
description 'dynamically load modules'
version '1.0.0'
ui_page 'view/index.html'
files {
'view/index.html',
'view/modules/**/*.html',
'view/modules/**/*.css',
'view/modules/**/*.js'
}
server_scripts {
'server/config.lua', -- (optional to store paths)
'server/main.lua'
}
client_script 'client/main.lua'
Loading js/css content dynamically
-
scandir function (in lua) that is run serverside
function scandir(directory)
-- search a givin path
local i, t, popen = 0, {}, io.popen
-- on a windows server
local pfile = popen('dir "'..directory..'" /b /ad')
-- on a linux server
local pfile = popen('ls -a "'..directory..'"')
-- loop trough the returned files
for filename in pfile:lines() do
-- the if statement isn't nessasery but usefull if you got a template module like me.
if filename ~= 'template' then
i = i + 1
t[i] = filename
end
end
pfile:close()
-- return the table with the names of the modules
return t
end
-
Trigger this function underneath and asing to a local variable
- specify a path with escaped backslashed otherwise it wont work, so do not use normal slashes!!!
local path = 'C:\\*server_folder*\\resources\\*resource_name*\\view\\modules'
local modules = scandir(path)
-
Trigger this function whitin an event that is called by the client
- return the table with module names to the client
RegisterServerEvent('module-loader:server:getModules')
AddEventHandler('module-loader:server:getModules', function()
local src = source
TriggerClientEvent('module-loader:client:sendModules', src, modules)
end)
-
send te information form the client to the user interface
RegisterNetEvent('module-loader:client:sendModules')
AddEventHandler('module-loader:client:sendModules', function(modules)
-- setup an 'SendNUIMessage' function to, send the names to the client their webpage
SendNUIMessage{ action = 'setModules', modules = modules }
end)
-
Place a script tag the head tag after your prefferd librarys are loaded do not load any other script you want to use
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- jQuery -->
<script src="nui://game/ui/jquery.js" type="text/javascript"></script>
<script>
// write the upcomming code here
</script>
<title>Dynamic loading</title>
</head>
<body>
</body>
</html>
-
Add a function to the script tag which loads the contents of the modules
let loadModule = ({ js, css }) => {
// create script element for the js file
let script = document.createElement("script");
// set script type for older browsers (not required)
script.type = "text/javascript";
// add the given path to the src attribute
script.src = js;
// add element to head
document.getElementsByTagName("head")[0].append(script);
// do the same for the css file but with a link element
// within conditional statement in case you just want to load a js file
if (css != null) {
let link = document.createElement("link");
// rel is required within a link element when you load css files,
// unlike type within a script element
link.rel = "stylesheet";
// add the given path to the href attribute
link.href = css;
// add element to head
document.getElementsByTagName("head")[0].append(link);
}
};
-
Add a event listener to catch the message
- i’m using a switch case statement for keybinding
window.addEventListener('message', (ev) => {
switch (ev.data.action) {
case 'setModules'; // switch on the action send
const modules = ev.data.modules;
// loop through the recieved names
for (let i = 0; i < modules.length; i++) {
const name = modules[i];
/* we can send the paths within any table because,
the fuction uses the table names as parameters */
const path = {
js: `./modules/${name}/js/app.js`,
css: `./modules/${name}/css/app.css`
}
// call the loadModule function and pass the path table
loadModule(path);
}
break;
}
});
Loading HTML5 dynamically
if you want switch content and do this in a smooth way its recommended to animate te moduleContainer div out and unload the content, then load the new content and animate it back this can eigther be acomplished by a fade or slide animation.
It would be better to use 2 containers to load modules, if there isnt any form main content for the moduleContainer to overlap.
-
Add the following events to the server file
local tokens = tokens or {}
RegisterServerEvent('module-loader:server:generateToken')
AddEventHandler('module-loader:server:generateToken', function(callback)
local src = source
local newToken = 'T'..tostring(math.random(1111111, 9999999))
tokens[src] = newToken
callback(newToken)
end)
-
Add the following thread to the client file
local sleep = 0
Citizen.CreateThread(function()
repeat
if IsControlJustReleased(0, * control key number) then
sleep = (1000 * 60) * 0.5 -- 30 seconds
TriggerServerEvent('module-loader:server:generateToken', function(token)
SendNUIMessage {
action = 'loadModuleContent',
moduleName = '*your_module_name*',
token = token
}
end)
end
Citizen.Wait(sleep)
until false
end)
* control key number
-
Create a container to load the content
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- jQuery -->
<script src="nui://game/ui/jquery.js" type="text/javascript"></script>
<script>
// code written in 'Loading js/css content dynamically'
</script>
<title>Dynamic loading</title>
</head>
<body>
<div id="moduleContainer"></div>
</body>
</html>
-
Add the following variable above te event listener
let container = document.querySelector('.module');
let token;
-
Add the followning case in message event listener
case 'loadModuleContent';
token = ev.data.token;
const name = ev.data.name;
const path = `./modules/${name}/css/app.css`;
container.classList.add(`module-${name}`);
fetch(path)
.then(data => data.text())
.then(html => container.innerHTML = html);
break;
-
Add a key down, up or press event listener to unload the content
$(document).on('keydown', (e) => {
switch (e.keyCode) {
case 27: // esc
container.innerHTML = '';
container.classList.remove(`module-${name}`);
$.post('https://module-loader/close', token);
break;
}
});
-
Add a NUI callback to the client file under neath the thread which sets the sleeper back to 0
RegisterNUICallback('close', function(token)
TriggerServerEvent('module-loader:server:checkToken', token)
sleep = 0
end)
-
Add event to the server file to check the token given back by the client
RegisterServerEvent('module-loader:server:checkToken')
AddEventHandler('module-loader:server:checkToken', function(token)
local src = source
local newToken = 'T'..tostring(math.random(1111111, 9999999))
if token ~= tokens[src] then
-- yea 99.9999% sure that hes fucking with the devtools
tokens[src] = newToken
else
tokens[src] = newToken
end
end)
system
Closed
2
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.