Why do I continue to get the Tasks is not defined? - javascript

I am trying to follow the Meteor documentation here:
https://www.meteor.com/tutorials/blaze/collections
in adding a collection and being able to get an empty array back by doing this in the console:
Tasks.find().fetch()
but instead I get this:
Uncaught ReferenceError: Tasks is not defined
at <anonymous>:1:1
I am not sure where I have gone wrong since I am following their documentation, I believe the tree structure for the imports folder which I created according to the documentation is correct and the code I have implemented so far is also as suggested from their docs.
This is the client/main.js:
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import {Tasks} from '../imports/api/tasks';
import './main.html';
Template.hello.onCreated(function helloOnCreated() {
// counter starts at 0
this.counter = new ReactiveVar(0);
});
// templates can have helpers which are just functions and events and this
// particular event is a click event
Template.todos.helpers({
tasks() {
return Tasks.find({});
},
});
Template.todos.events({
});
This is the imports/api/tasks.js:
import {Mongo} from 'meteor/mongo';
export const Tasks = new Mongo.Collection('tasks');
This is the server/main.js:
import { Meteor } from 'meteor/meteor';
import {Tasks} from '../imports/api/tasks';
Meteor.startup(() => {
// code to run on server at startup
});
This is the client/main.html:
<head>
<title>tasklist</title>
</head>
<body>
<h1>Welcome to Meteor!</h1>
{{> todos}}
{{> info}}
</body>
<template name="todos">
</template>
<template name="info">
<h2>Learn Meteor!</h2>
<ul>
<li>Do the Tutorial</li>
<li>Follow the Guide</li>
<li>Read the Docs</li>
<li>Discussions</li>
</ul>
</template>

Tasks is defined, but only in the scope of where it is imported. Since it was never declared as global, you won't be able to access it within your browser's console.
If you want to see the tasks (the ones you are subscribing to) in the console, just update your helper function:
Template.todos.helpers({
tasks() {
let tasks = Tasks.find({});
console.log(tasks.fetch());
return tasks;
}
});
Or, you can check your database directly:
> meteor mongo
> db.tasks.find()

Related

Vue <script setup> Top level await causing template not to render

I'm using the new syntax in Vue 3 and I really like the idea of it, but once I tried to use a top level await I started to run in some problems.
This is my code:
<template>
<div class="inventory">
<a class="btn btn-primary">Test button</a>
<table class="table">
<thead>
<tr>Name</tr>
<tr>Description</tr>
</thead>
<tbody>
<tr v-for="(item, key) in inventory" :key="key">
<td>{{ item.name }}</td>
<td>{{ item.description }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup lang="ts">
import { apiGetInventory, GetInventoryResponse } from '#/module/inventory/common/api/getInventory'
const inventory: GetInventoryResponse = await apiGetInventory()
</script>
As you can see it's not that complicated, the apiGetInventory is just an axios call so I won't bother going into that.
The problem is, that if I have this top level await, my template doesn't render anymore, it's just a blank page in my browser. If I remove the two lines of code, it works fine.
Also the promise seems to revolve just fine, if I place a
console.log(inventory) underneath it I get an array with objects all fine and dandy.
Anyone have a clue what's going wrong here?
Top-level await must be used in combination with Suspense (which is experimental).
You should be able to just do it in onBeforeMount. Not as elegant; but a solid solution. Something like this:
<script setup lang="ts">
import { apiGetInventory, GetInventoryResponse } from '#/module/inventory/common/api/getInventory';
import {ref, onBeforeMount} from 'vue';
const inventory = ref<GetInventoryResponse>()
onBeforeMount( async () => {
inventory.value = await apiGetInventory()
})
</script>
Using onBeforeMount is good, but there are a couple of other options.
#skirtle suggested in Vue Discord chat to do the initialization inside an async lambda or function (possibly as an IIFE):
<script setup lang="ts">
let inventory: GetInventoryResponse
const loadData = async () => inventory = apiGetInventory()
loadData()
</script>
#wenfang-du suggested in How can I use async/await in the Vue 3.0 setup() function using Typescript to use promise chaining:
<script setup lang="ts">
let inventory: GetInventoryResponse
apiGetInventory().then(d: GetInventoryResponse => inventory = d)
</script>
The benefit of doing so is that the code is run before the beforeMount lifecycle hook.
You additionally need to take care of error handling as appropriate in both cases.
if you need for specific template(routes).
You can use router beforeResolve:
import { apiGetInventory, GetInventoryResponse } from '#/module/inventory/common/api/getInventory'
let inventory = false
router.beforeResolve(async to => {
// Skip if loaded or for specific vue file
if (inventory || to.meta?.layout === 404 || to.meta?.layout === 'blank') {
return
}
inventory = await apiGetInventory()
})
The Vue3 documentation says
Top-level await can be used inside script setup. The resulting code will be compiled as async setup()
In addition, the awaited expression will be automatically compiled in a format that preserves the current component instance context after the await.
For example:
<script setup lang="ts">
import { apiGetInventory, GetInventoryResponse } from '#/module/inventory/common/api/getInventory'
const inventory = ref(await apiGetInventory())
</script>
This only works if you use the Suspense compenent in the parent component, for instance:
<Suspense>
<RouterView />
<template #fallback>
Loading...
</template>
</Suspense>

How can I embed VaniilaJS into React?

I have open source library that I want to use. the library wrote in clean vanilla js:
follow their docs, if I want to use the library:
<html>
<head>
<script src="./jquery-2.0.3.min.js"></script>
<script src="./kinetic-v5.1.0.min.js"></script>
<script src="./inchlib-1.2.0.js"></script>
<script>
$(document).ready(function() { //run when the whole page is loaded
var inchlib = new InCHlib({"target": "inchlib",
"width": 800,
"height": 1200,
"column_metadata_colors": "RdLrBu",
"heatmap_colors": "RdBkGr",
"max_percentile": 90,
"middle_percentile": 60,
"min_percentile": 10,
"heatmap_font_color": "white",
text: 'biojs'});
inchlib.read_data_from_file("/microarrays.json");
inchlib.draw();
inchlib.onAll(function(name){
console.log(name + " event triggered");
});
});
</script>
</head>
<body>
<div class="heatmaps" style="margin:auto; align-items: center; margin-left:25%;">
<div id="inchlib"></div>
</div>
<div ></div>
</body>
</html>
The file inchlib-1.2.0.js contains the main logic and js code. I want to build react project and use this library there. How can I achieve this goal?
import React, { Component } from 'react';
import './App.css';
export default class App extends Component {
render () {
return (
<div>
<div>
</div>
</div>
)
}
}
You can create custom hook with useEffect. In useEffect you should paste your code. You can insert html elements, add event listeners and so on.
useLibrary.js
import { useEffect } from "react";
const useLibrary = () => {
useEffect(() => {
$.getScript("inchlib-1.2.0.js", function(){
var inchlib = new InCHlib({"target": "inchlib",
"width": 800,
"height": 1200,
"column_metadata_colors": "RdLrBu",
"heatmap_colors": "RdBkGr",
"max_percentile": 90,
"middle_percentile": 60,
"min_percentile": 10,
"heatmap_font_color": "white",
text: 'biojs'});
inchlib.read_data_from_file("/microarrays.json");
inchlib.draw();
inchlib.onAll(function(name){
console.log(name + " event triggered");
});
});
}, []);
};
export default useLibrary;
App.js
import useLibrary from ".useLibrary";
export default class App extends Component {
useLibrary();
render () {
return (
<div>
<div class="heatmaps" style="margin:auto; align-items: center; margin-left:25%;">
<div id="inchlib"></div>
</div>
</div>
)
}
}
But I warn you that this is a big crutch.
Depends on what you're gonna do with the library you want to integrate with. Checkout this as a base reference: Integrating with other libraries.
If you're gonna manipulate DOM elements you'll gonna need a reference to them. In this case checkout this: Refs and the DOM.
If the library provides some general logic, you have no problem using it anywhere throughout your code or more specifically in effects.
As inchlib is a visual element library, you'll need to go the first route and get a reference to a specific DOM element. As already noted, checkout Refs from react docs.
Alternative solution is to wrap the whole library usage in your own react component.
Well If I were to do the same thing then I would paste the script tags as you've done in your html file
<head>
<script src="./jquery-2.0.3.min.js"></script>
<script src="./kinetic-v5.1.0.min.js"></script>
<script src="./inchlib-1.2.0.js"></script>
<script>
</head>
For accessing an object into react app, Create a file named Inchlib.js in same directory as is your app.js
Contents of Inchlib.js should be
export default window.InCHlib;
Import the default export into your app.js
import InCHlib from "./inchlib";
function App() {
console.log(InCHlib); // prints the InCHlib object
return "hello";
}
Note: Although this should work, there might be a better way to do this. Also using global objects in react code is not usually a preferred option.
Hopefully this would help.
Just add the Libraries and Scripts you want in the public/index.html file in your react project.
create loadScript function:
function loadScript(src, position, id) {
if (!position) {
return;
}
const script = document.createElement('script');
script.setAttribute('async', '');
script.setAttribute('id', id);
script.src = src;
position.appendChild(script);
}
in Component:
export default function GoogleMaps() {
const loaded = React.useRef(false);
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
'https://maps.googleapis.com/maps/api/js?key=AIzaSyBwRp1e12ec1vOTtGiA4fcCt2sCUS78UYc&libraries=places',
document.querySelector('head'),
'google-maps',
);
}
loaded.current = true;
}
}
now you can access window.google
here is a example

How do I create a function library which I can used across all my Vuejs components?

I currently writing a financial application using Vue.js and Vuetify. I have a few component files and javascript files like
Dashboard.vue
Cashflow.vue
NetWorth.vue
stores.js <- Vue Vuex
I have some functions which I need to use across all the Vue.js and javascript files. Would it be possible for me to perhaps write a function library which can be used across all
the component and js files.
function moneyFormat(num)
function IRRCalc(Cashflow)
function TimeValueMoneyCalc(I,N,PV,FV,PMT)
function PerpetualAnnuityCalc(I,PV)
function CarLoanInstallment(V,N)
function HouseLoanInstallment(V,N)
I know in C it is very simple just #include<financial.h> was wondering is there something similar in javascript.
Thanks.
There are 3 ways to do this:
1/You can create a helper.js file and import it to .vue files
// helper.js
export default {
function moneyFormat(num) { // some logic}
}
// Dashboard.vue
<script>
import helper from "helper.js" //the path may change depends on where you put the js file
methods: {
useHelper(value) {
helper.moneyFormat(value)
}
}
</script>
2/Another way is bind the function to Vue prototype
in main.js
Vue.prototype.$moneyFormat= function moneyFormat(num) {}
then in Dashboard.vue just call this.$moneyFormat(num). No need to import anything
3/ Use mixins. You can search online on how to use this https://v2.vuejs.org/v2/guide/mixins.html
You can create a single JS file that holds all the helper/util methods, and then export them individually:
export function moneyFormat(num) { ... }
export function IRRCalc(Cashflow) { ... }
export function TimeValueMoneyCalc(I,N,PV,FV,PMT) { ... }
export function PerpetualAnnuityCalc(I,PV) { ... }
export function CarLoanInstallment(V,N) { ... }
export function HouseLoanInstallment(V,N) { ... }
Then, you can simply import individual methods as of when needed, i.e.:
import { CarLoanInstallment, HouseLoanInstallment } from '/path/to/helper/file';
This can be quite usefuly for tree-shaking when you're bundling with webpack, for example, so that you don't bundle unnecessary functions that are never used in your project.
You can use Mixin
In your main.js, add Vue.mixin:
import Vue from "vue";
import App from "./App.vue";
Vue.mixin({
methods: {
helloWorld() {
alert("Hello world");
}
}
});
new Vue({
render: h => h(App)
}).$mount("#app");
and then you can call helloWorld() method from your component script with this.helloWorld() or just helloWorld() from the template.
You also can use filters if the method is to apply common text formatting
In your main.js, add Vue.filter:
import Vue from "vue";
import App from "./App.vue";
Vue.filter("capitalize", function(value) {
if (!value) return "";
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
});
new Vue({
render: h => h(App)
}).$mount("#app");
and then you can do {{ "some text" | capitalize }} to apply capitalize filter on "some text"
Example here: https://codesandbox.io/s/heuristic-dirac-esb45?file=/src/main.js:0-226

How to import a external function in a Vue component?

I'm a newbie in javascript and vue.js and I'm facing somme issue when trying to add a new function in an existing programme.
I have put my new function (with others) in a separate file:
export const MyFunctions = {
MyFunction: function(param) {
// Doing stuff
}
}
Then I import the file in the component file and calling my function :
<script>
import {MyFunctions} from "#/components/MyFunctions.js";
export default {
name:"Miniature",
computed: {
useMyFunction() {
MyFunction("Please do some stuff !");
}
}
}
</script>
When the component is used, I get an error message
[Vue warn]: Property or method "MyFunction" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I've read a lot of documentation and fail to understand why it doesn't work. Can anyone help me with this ??
You're exporting an object then in order to use the MyFunction you need to access to that function using dot notation, like this: MyFunctions.MyFunction("Please do some stuff !")
I made a working example for this use-case: https://codesandbox.io/s/62l1j19rvw
MyFunctions.js
export const MyFunctions = {
MyFunction: function(param) {
alert(param);
}
};
Component
<template>
<div class="hello">
{{msg}}
<button #click="handleClick">Click me</button>
</div>
</template>
<script>
import {MyFunctions} from "../MyFunctions.js";
export default {
name: "HelloWorld",
data() {
return {
msg: "Welcome to Your Vue.js App"
};
},
methods:{
handleClick: function(){
MyFunctions.MyFunction("Please do some stuff !");
}
}
};
</script>
You can just import your javascript files into .vue files as long as they are inside <script> tags. Since Vue.js is after all javascript, the first part where you should look at while debugging is if you have some kind of mistake in your syntax. From what I see, there is some confusion with the import and export statements, which could be quite complex at first!
Check MDN's Documentation specially under named exports:
In the module, we could use the following
// module "my-module.js"
function cube(x) {
return x * x * x;
}
const foo = Math.PI + Math.SQRT2;
var graph = { /* nice big object */ }
export { cube, foo, graph };
This way, in another script, we could have:
import { cube, foo, graph } from 'my-module';
// Use your functions wisely
what you export is an object, and what you use is a field/method inside this object, so you need to use your function this way:
MyFunctions.MyFunction("Please do some stuff !");

How to import functions from different js file in a Vue+webpack+vue-loader project

I have App.vue which has a template:
<template>
<div id="app">
<login v-if="isTokenAvailable()"></login>
</div>
</template>
I've declared the isTokenAvailable method in the normal way for Vue inside methods. It uses a function that I wrote in a separate js file:
<script>
import * as mylib from './mylib';
export default {
....
methods:{
isTokenAvailable: () => {
return mylib.myfunc();
}
}
}
</script>
mylib starts like this:
import models from './model/models'
import axois from 'axios'
export default function() {
// functions and constants
}
When I run the project, I get this below warning:
export 'myfunc' (imported as 'mylib') was not found in './mylib'
I gather I'm not importing or declaring a javascript module correctly... but there seem to be so many ways to do it, added with the complexity of the scoping in Vue, I'm not sure what is the right way to do it?
Why this isn't a dupe of: How do I include a JavaScript file in another JavaScript file?
That one doesn't seem to fix the problem, specifically in the context of vuejs.
I have tried this:
<script>
const mylib = require('./mylib');
...
</script>
With the function modified to: exports.myfunc = function()
Should I have some other dependency for this to work? Because I get a different error:
[Vue warn]: Error in render function:
TypeError: mylib.myfunc is not a function
Say I want to import data into a component from src/mylib.js:
var test = {
foo () { console.log('foo') },
bar () { console.log('bar') },
baz () { console.log('baz') }
}
export default test
In my .Vue file I simply imported test from src/mylib.js:
<script>
import test from '#/mylib'
console.log(test.foo())
...
</script>
After a few hours of messing around I eventually got something that works, partially answered in a similar issue here: How do I include a JavaScript file in another JavaScript file?
BUT there was an import that was screwing the rest of it up:
Use require in .vue files
<script>
var mylib = require('./mylib');
export default {
....
Exports in mylib
exports.myfunc = () => {....}
Avoid import
The actual issue in my case (which I didn't think was relevant!) was that mylib.js was itself using other dependencies. The resulting error seems to have nothing to do with this, and there was no transpiling error from webpack but anyway I had:
import models from './model/models'
import axios from 'axios'
This works so long as I'm not using mylib in a .vue component. However as soon as I use mylib there, the error described in this issue arises.
I changed to:
let models = require('./model/models');
let axios = require('axios');
And all works as expected.
I like the answer of Anacrust, though, by the fact "console.log" is executed twice, I would like to do a small update for src/mylib.js:
let test = {
foo () { return 'foo' },
bar () { return 'bar' },
baz () { return 'baz' }
}
export default test
All other code remains the same...
I was trying to organize my vue app code, and came across this question , since I have a lot of logic in my component and can not use other sub-coponents , it makes sense to use many functions in a separate js file and call them in the vue file, so here is my attempt
1)The Component (.vue file)
//MyComponent.vue file
<template>
<div>
<div>Hello {{name}}</div>
<button #click="function_A">Read Name</button>
<button #click="function_B">Write Name</button>
<button #click="function_C">Reset</button>
<div>{{message}}</div>
</div>
</template>
<script>
import Mylib from "./Mylib"; // <-- import
export default {
name: "MyComponent",
data() {
return {
name: "Bob",
message: "click on the buttons"
};
},
methods: {
function_A() {
Mylib.myfuncA(this); // <---read data
},
function_B() {
Mylib.myfuncB(this); // <---write data
},
function_C() {
Mylib.myfuncC(this); // <---write data
}
}
};
</script>
2)The External js file
//Mylib.js
let exports = {};
// this (vue instance) is passed as that , so we
// can read and write data from and to it as we please :)
exports.myfuncA = (that) => {
that.message =
"you hit ''myfuncA'' function that is located in Mylib.js and data.name = " +
that.name;
};
exports.myfuncB = (that) => {
that.message =
"you hit ''myfuncB'' function that is located in Mylib.js and now I will change the name to Nassim";
that.name = "Nassim"; // <-- change name to Nassim
};
exports.myfuncC = (that) => {
that.message =
"you hit ''myfuncC'' function that is located in Mylib.js and now I will change the name back to Bob";
that.name = "Bob"; // <-- change name to Bob
};
export default exports;
3)see it in action :
https://codesandbox.io/s/distracted-pare-vuw7i?file=/src/components/MyComponent.vue
edit
after getting more experience with Vue , I found out that you could use mixins too to split your code into different files and make it easier to code and maintain see https://v2.vuejs.org/v2/guide/mixins.html

Categories