Vue Composables best practices - phpstorm

I need your help when writting good composables in Vue 3. Looking a the documentation I can see that composables should be a function. That's ok.
However I don't feel confortable with this because I lose IDE help and autocompletion.
For example, if I have a useUtils() composable like this:
// composables/useUtils.js
export default function useUtils() {
const isAdmin = () => true;
const isUser = () => false;
return {
isAdmin,
isUser,
}
Then, when writting code in PhpStorm/WebStorn, the IDE does not autocomplete (either auto import) the utilities functions described inside my useUtils() composable :(
For example, if I start to write:
const canCreate = isAdm //<-- Here I would like IDE to autocomplete and autoimport!
That doesn't work because IDE is not able to know what should autocomplete.
Workaround
If I define the composable as a bounch of exported functions however, it works correctly:
// composables/useUtils.js
export const isAdmin = () => true;
export const isUser = () => false;
Now, the IDE knows all available functions and does a good work autocompleting and autoimporting everything.
In addition, when using this approach, I also get's the ability to know what things of my composable are being used and what not, that's very cool. It doesn't happen when deffining a function. But I feel bad because Vue docs says that composables should be a function T_T
So, here is my question:
What do you do guys? Are there a way to configure the IDE for a better integration when writting composables? Is very bad to use a bunch of funtions?
Give me any tip please,
Thanks!

I usually do:
const useMyComposable = () => {
const myData = ref("something");
const myFunction = () => {
// ...
}
return {
myData,
myFunction
}
}
export { useMyComposable };
export default useMyComposable;
So now in a random component I get the autocompletion working.

Related

Markups Core enterEditMode() returning false

function newMarkupGUI(viewer, options) {
Autodesk.Viewing.Extension.call(this, viewer, options);
thisViewerId = options.id;
this.viewer.loadExtension("Autodesk.Viewing.MarkupsCore").then(() => {
let extension = this.viewer.getExtension("Autodesk.Viewing.MarkupsCore");
extension.enterEditMode();
console.log(extension.enterEditMode());
});
}
When I am inside my main js file where I initialize the viewer, I am able to access functions such as enterEditMode() like so:
var extension = viewer.getExtension("Autodesk.Viewing.MarkupsCore");
extension.enterEditMode();
This works. But inside my extension called newMarkupsGUI, it seems getExtension() does not work. I am confused about how this all works, as the documentation is pretty sparse. I would rather keep my extension separate and not hard code the functionality of markups where I am initializing the viewer. Any help would be appreciated, thank you.
I think your problem is related to the viewer reference. You don't need to use this.viewer if you have your viewer as function parameter.
When using viewer.loadExtension().then() the loaded extension is returned in the promise.
You can do something like that :
viewer.loadExtension("Autodesk.Viewing.MarkupsCore").then((markupExtension) =>
{
markupExtension.enterEditMode();
});

Avoid Repeating: Use Global Variables in Google Apps Script or Not?

As of 2021, with V8 engine, I'm wondering if it's a good idea to use Global Variables in Google Apps Script? And if it is, how to use them? Is my way a good way (described below)?
Now I checked, of course, all other similar questions. But there are still some details I couldn't find:
Basically what I tried to do is to not repeat the code: I have multiple functions, and I'm storing the active spreadsheet and the current sheet like so:
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();
which leads to
repeating (1)
wasting resources - instantiating spreadsheet and sheet (2)
Increasing variables names inconsistency (ss / spreadsheet / spreadSheet - when copy/pasting from a snippet on the web) (3)
Right?
So I thought of using the Global Variables (GV) whenever I have common local variables in multiple functions.
However, since they’re will be unnecessarily allocated on every function call (there are also other functions that don't need the GVs), I tried to define them only when needed (only when there's function call that uses them) - by not using a defining keyword (var, const, let):
According to this link, it seems to be a good approach (pattern 1).
Anyway, I'm wondering if there are any other considerations or downsides I'm not aware of? Is it really a good idea to go this road? Because so far, I didn’t see any snippet that implements this, and I saw a lot of GAS snippets.
One downside I'm aware of is the lack of autocompletion in the new GAS editor, for my GVs (since I didn't define them using 'var' or 'let' to set their scope Global on purpose).
Otherwise, I'm aware of PropertiesService and CacheService. However I'm willing to reuse my script (where I defined my GVs) as a library for my other scripts.
Plus, you can only store values as strings in PropertiesService and CacheService (and not SpreadsheetApp.getActiveSpreadsheet()) right? Not to mention that I don't need persistency after the script execution.
So I'm also hesitant to use them instead of GVs.
You can use the lazy loading technique in my answer
To make it dynamic and avoid repetition, You can use enclosing arrow functions(()=>{}) to avoid direct execution and use Object.defineProperty() to add a getter.
One of the significant advantage of this method is modular lazy loading. If a object isn't needed, it isn't loaded. If you have ss, sheet1, sheet2,rangeOfSheet1 and rangeOfSheet2 as global variables, if you access rangeOfSheet2, only it's dependencies are loaded, i.e, sheet1 and ss. The rest are untouched.
const g = {};//global object
const addGetter_ = (name, value, obj = g) => {
Object.defineProperty(obj, name, {
enumerable: true,
configurable: true,
get() {
delete this[name];
return (this[name] = value());
},
});
return obj;
};
//MY GLOBAL VARIABLES in g
[
['ss', () => SpreadsheetApp.getActive()],
['MasterSheet', () => g.ss.getSheetByName('Sheet1')],
['MasterRangeColA1_5', () => g.MasterSheet.getRange('A1:A5')],
['MasterRangeColAValues', () => g.MasterRangeColA1_5.getValues()],
].forEach(([n, v]) => addGetter_(n, v));
const test = () => {
console.info('start');
console.log({ g });
console.info('Accessing MasterSheet');
console.log(g.MasterSheet);
console.log({ g }); //note ss is loaded as well
console.info('Accessing MasterRangeColAValues');
console.log(g.MasterRangeColAValues);
console.log({ g }); //note MasterRangeColA1_5 is loaded as well
};
Instead of a global object g, we can also use the global this, in which case, all variables directly become members of a global object:
const addGetter_ = (name, value, obj = this) => {
Object.defineProperty(obj, name, {
enumerable: true,
configurable: true,
get() {
delete this[name];
return (this[name] = value());
},
});
return obj;
};
[
['ss', () => SpreadsheetApp.getActive()],
['MasterSheet', () => ss.getSheetByName('Sheet1')],
['MasterRangeColA1_5', () => MasterSheet.getRange('A1:A5')],
['MasterRangeColAValues', () => MasterRangeColA1_5.getValues()],
].forEach(([n, v]) => addGetter_(n, v));
const test = () => {
console.info('start');
console.log(this);
console.info('Accessing MasterSheet');
console.log(MasterSheet);
console.log(this); //note ss is loaded as well
console.info('Accessing MasterRangeColAValues');
console.log(MasterRangeColAValues);
console.log(this); //note MasterRangeColA1_5 is loaded as well
};
Advantage: You don't have to prefix variables with g. But, global space is polluted.

Geolocation.currentLocation() not working, giving null value using meteor(1.5.1) & mdg:geolocation(1.3.0)

I am working in a meteor project(1.5.1) and using mdg:geolocation#1.3.0, and i am trying to get the value of Geolocation.currentLocation() but it is giving me null value and the behaviour is not the same for all the time, sometime it is giving me null value and sometime giving me proper value.
I have research for the long time, but not got the solution yet, if possible please provide solution with meteor#1.5.1 and with use of mdg:geolocation package.
Thanks in advance...
MDG has implemented mdg:geolocation for continuous geolocation and functions in this Meteor package return a reactive var. You can use the package this way (for example):
// Continous geolocation with mdg:geolocation
// This code will run every time user location changes.
Tracker.autorun(() => {
const position = Geolocation.currentLocation();
Tracker.nonreactive(() => {
if (position) {
Meteor.call('user.update.position', position);
}
});
});
If you don't need continous geolocation, You can use javascript method instead of meteor package. See this answer for other options with windows.navigator.geolocation. In this case you may need to add cordova-plugin-geolocation in case you are building Android or iOS apps
// Navigation geolocation to get geolocation only once
let errorCallback;
let successCallback;
successCallback = position => Meteor.call('user.update.position', position);
errorCallback = err => console.log(err);
navigator.geolocation.getCurrentPosition(successCallback, errorCallback, {
maximumAge: 60000,
timeout: 20000
});

Can ES6's module loader also load assets (html/css/...)

ES6's modules are based on a flexible loader architecture (although the standard is not final, so ...).
Does this mean ES6's loader, based on system.js, can load all assets? I.e. CSS, HTML, Images, Text, .. files of any sort?
I ask because I'm starting to use WebComponents & Polymer which have their own HTML import, and implementing them with ES6, which has its own import/loader (system.js).
If you use SystemJS then you can load assets by using plugins:
// Will generate a <link> element for my/file.css
System.import('my/file.css!')
.then(() => console.log('CSS file loaded'));
Alternatively, you can use an import statement. This will make sure that the CSS file is loaded before the your script executes:
import 'my/file.css!';
Finally, you can retrieve the contents of the file using the text plugin:
import cssContent from 'my/file.css!text';
console.log('CSS file contents: ', cssContent);
Another option is to add the css as a dependency in JSPM config files. Basically adding the dependency in the specific package .json file and then running 'jspm install' which will add the override to package.js & jspm.config.js
I know you mentioned ES6 modules, but as that does not appear to support CSS natively, if you're looking for something standards-based to load resources dynamically and wish for something possibly somewhat less unpleasant than XMLHttpRequest, the new Fetch API might be used like this:
var myStylesheets = ['myStyles1.css', 'myStyles2.css'];
Promise.all(myStylesheets.map(url => fetch(url))).
then(arr => Promise.all(arr.map(url => url.text()))).
then(arr => {
var style = document.createElement('style');
style.textContent = arr.reduce(
(prev, fileContents) => prev + fileContents, ''
);
document.head.appendChild(style);
}).then(() => {
// Do whatever now
});
This is even cleaner with async functions:
var myStylesheets = ['myStyles1.css', 'myStyles2.css'];
async function loadStyles(stylesheets) {
let arr = await Promise.all(stylesheets.map(url => fetch(url)))
arr = await Promise.all(arr.map(url => url.text()))
const style = document.createElement('style')
style.textContent = arr.reduce(
(prev, fileContents) => prev + fileContents, ''
)
document.head.appendChild(style);
// Do whatever now
}
loadStyles(myStylesheets)
For other resource types, you can use the blob() method for images, and pending ES6 modules support, eval() for JavaScript, etc.
I faced that question 8 years later :D
In my option, I think is interesting we can use the import.meta.url to be able to load assets relatively in a web scenario. That makes it more similar that esmodules import way.
In addition to the Zamir answer, if the assets/styles are public and we do not have to handle their content, there is no need to use fetch or any ajax at all. Just use HTML tags: link, img, scripts ...
eg: Loading a sibling css file relatively
mywebsite.com
assets
a-component.css
a-component.js
function loadStyle(styleUrl) {
const linkEl = document.createElement("link");
linkEl.setAttribute("rel", "stylesheet");
linkEl.setAttribute("href", styleUrl);
document.head.append(linkEl);
}
function load(currentPath, relativePath) {
// [https:,,,mywebsite.com,assets,a-component.js]
const parts = currentPath.split('/');
// [https:,,,mywebsite.com,assets]
parts.pop();
// [https:,,,mywebsite.com,assets,./a-component.css]
parts.push(relativePath);
// https://mywebsite.com/assets/a-component.css
const absoluteUrl = parts.join('/').replace('/./', '/');
// css
const extension = relativePath.split('.').pop();
switch(extension) {
case 'css':
loadStyle(absoluteUrl);
break;
// others types
}
}
// usage in a-component.js
// https://mywebsite.com/assets/a-component.js
load(import.meta.url, "./a-component.css");
That did help me out in a pure JS Legacy personal project without SystemJS.

Node.JS export function without object wrapper

I am looking at Node.JS request and notice that you can use
var request = require('request');
request(...)
But when I try to do something similar like in the module I try
exports = function() {}
it does not work. The only way I know to use is
var request = require('request').request;
request(...)
and
exports.request = function() {}
How can I set the whole export to a function instead of adding a function to the export object?
A hint might be available in the request source code but I am finding it hard to figure out what is going on. Can you help?
You need to overwrite it as
module.exports = function() {}
Merely writing exports = function() {} creates a new local variables called exports and hides the exports variable living in module.exports