Call SQL linter's API from Codemirror with Typescript - sql

I am trying to call an API to lint a SQL query written in Codemirror (actually I use Angular and the wrapper ngx-codemirror)
Unfortunately, I could not call the API because this is considered undefined:
data-analyzer.component.html:81 ERROR TypeError: Cannot read property 'analyzerService' of undefined
at testA (data-analyzer.component.ts:624)
at lintAsync (lint.js:134)
at startLinting (lint.js:152)
at Object.lint (lint.js:248)
at new CodeMirror (codemirror.js:7885)
at CodeMirror (codemirror.js:7831)
at Function.fromTextArea (codemirror.js:9682)
at ctrl-ngx-codemirror.js:64
at ZoneDelegate.invoke (zone-evergreen.js:365)
at Zone.run (zone-evergreen.js:124)
My code is as follow:
<ngx-codemirror
#ref
name="query"
[options]="config"
[(ngModel)]="item.query"
(keypress)="CMonKeyPress($event)"
>
</ngx-codemirror>
config = {
mode: 'text/x-mysql',
showHint: true,
lint: {
lintOnChange: true,
getAnnotations: this.lintSQL
},
gutters: [
'CodeMirror-linenumbers',
'CodeMirror-lint-markers'
]
};
constructor(
private analyzerService: DataAnalyzerService
) {}
lintSQL(a: string, b: LintStateOptions, cm: Editor) {
const found: Annotation[] = [];
// The error occurs here
this.analyzerService.lint(this.item.query).subscribe(
(r: any) => {
console.log(r.data);
},
(err) => {
console.log(err);
}
);
// So far I return an empty array, the focus is to get the results from the service
return found;
}
I would like to know how could I access to the service in the linting function.

Found from this question, the solution is to bind (this) as follow:
getAnnotations: this.lintSQL.bind(this)

Related

How to reference to element from state in foreach (Vuex)?

I am using vuex in my vue application. In store is a declared object:
state: {
list: {
a: false,
b: false,
c: false
}
}
In mutations is mutation that receives arrays in parameters, for example: el: ['a', 'b'].
Those elements in the el array must be set to true in list object in state. I'm using a foreach loop for this:
mutations: {
SET_LIST(state, el) {
el.forEach(element => {
if (state.list.element) {
state.list.element = true;
}
});
}
}
But I am getting an error: error 'element' is defined but never used.
Because element is not used and I don't know how to refer to it correctly.
I searched on the internet and found this solution: state.list[element]. I don't get an error then, but it doesn't work.
Use the bracket notation [] to get the property dynamically :
mutations: {
SET_LIST(state, el) {
el.forEach(element => {
if (Object.keys(state.list).includes(element)) {
state.list[element] = true;
}
});
}
}

Sending Vuex State To Backend

I am trying to send a state in Vuex that is an array of objects to my backend. However, I keep getting this error:
Uncaught (in promise) TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property '$options' -> object with constructor 'Object'
| property 'router' -> object with constructor 'Object'
--- property 'app' closes the circle
at JSON.stringify (<anonymous>)
at transformRequest (defaults.js?6d1b:52)
at transform (transformData.js?e0e9:16)
at Object.forEach (utils.js?a2f8:232)
at transformData (transformData.js?e0e9:15)
at dispatchRequest (dispatchRequest.js?82e5:30)
I am using axios to post to my backend, and here is my axios post method call:
SET_ITEMS: (state) => {
const items = state.items;
axios.post('http://localhost:5000/api/boards/update-list', { list: items });
}
I have tried multiple things one being the uses flatted package (Flatted), however, to no avail. How can I fix this error? Thanks in advance.
EDIT
In my case, I have manually given value to the state.items.
state: {
items: [
{
x: 100,
y: 200,
},
{
x: 50,
y: 400,
},
{
x: 100,
y: 100,
},
{
x: 350,
y: 300,
}
]
}
SECOND EDIT
Moved async call into a action:
setItems: ({state}) => {
axios.post('http://localhost:5000/api/boards/update-list', { hey: state.items})
}
I didn't mention this in the question, but I have a value in state.items where the element (HTMLElement) is being stored. This has been causing the error mentioned in the question. How I solved this was by completing leaving out the element value from JSON.stringify. You can accomplish this by creating a replacer function and here is mine in my case:
function replacer(key, value) {
if (key === 'element') return undefined;
return value;
}
Then you can put this function in the JSON.stringify as shown here:
JSON.stringify(state.items, replacer(key, value))
And now the element value is not included after the JSON.stringify method call!
I hope someone finds this helpful.

Vue.js 2: action upon state variable change

I am using a simple state manager (NOT vuex) as detailed in the official docs. Simplified, it looks like this:
export const stateholder = {
state: {
teams: [{id: 1, name:'Dallas Cowboys'}, {id: 2, name:'Chicago Bears'}, {id: 3, name:'Philadelphia Eagles'}, {id:4, name:'L.A. Rams'}],
selectedTeam: 2,
players: []
}
getPlayerList: async function() {
await axios.get(`http://www.someapi.com/api/teams/${selectedTeam}/players`)
.then((response) => {
this.state.players = response.data;
})
}
}
How can I (reactively, not via the onChange event of an HTML element) ensure players gets updated (via getPlayerList) every time the selectedTeam changes?
Any examples of simple state that goes a little further than the official docs? Thank you.
Internally, Vue uses Object.defineProperty to convert properties to getter/setter pairs to make them reactive. This is mentioned in the docs at https://v2.vuejs.org/v2/guide/reactivity.html#How-Changes-Are-Tracked:
When you pass a plain JavaScript object to a Vue instance as its data
option, Vue will walk through all of its properties and convert them
to getter/setters using Object.defineProperty.
You can see how this is set up in the Vue source code here: https://github.com/vuejs/vue/blob/79cabadeace0e01fb63aa9f220f41193c0ca93af/src/core/observer/index.js#L134.
You could do the same to trigger getPlayerList when selectedTeam changes:
function defineReactive(obj, key) {
let val = obj[key]
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
return val;
},
set: function reactiveSetter(newVal) {
val = newVal;
stateholder.getPlayerList();
}
})
}
defineReactive(stateholder.state, 'selectedTeam');
Or you could set it up implicitly using an internal property:
const stateholder = {
state: {
teams: [/* ... */],
_selectedTeam: 2,
get selectedTeam() {
return this._selectedTeam;
},
set selectedTeam(val) {
this._selectedTeam = val;
stateholder.getPlayerList();
},
players: []
},
getPlayerList: async function() {
/* ... */
},
};
Your question is also similar to Call a function when a property gets set on an object, and you may find some more information there.
You could use v-on:change or #change for short to trigger getPlayerList.
Here a fiddle, simulating the request with setTimeout.

Whatsapp Web - how to access data now?

It used to be possible to access http://web.whatsapp.com/ with the Store object in JavaScript. A few hours ago, this stopped working. How does it update chat data now? It must save the data somewhere.
I'm using this to get the Store again:
setTimeout(function() {
// Returns promise that resolves to all installed modules
function getAllModules() {
return new Promise((resolve) => {
const id = _.uniqueId("fakeModule_");
window["webpackJsonp"](
[],
{
[id]: function(module, exports, __webpack_require__) {
resolve(__webpack_require__.c);
}
},
[id]
);
});
}
var modules = getAllModules()._value;
// Automatically locate modules
for (var key in modules) {
if (modules[key].exports) {
if (modules[key].exports.default) {
if (modules[key].exports.default.Wap) {
store_id = modules[key].id.replace(/"/g, '"');
}
}
}
}
}, 5000);
function _requireById(id) {
return webpackJsonp([], null, [id]);
}
// Module IDs
var store_id = 0;
var Store = {};
function init() {
Store = _requireById(store_id).default;
console.log("Store is ready" + Store);
}
setTimeout(function() {
init();
}, 7000);
Just copy&paste on the console and wait for the message "Store is ready".
Enjoy!
To explain Pablo's answer in detail, initially we load all the Webpack modules using code based on this How do I require() from the console using webpack?.
Essentially, the getAllModules() returns a promise with all the installed modules in Webpack. Each module can be required by ID using the _requireById(id) which uses the webpackJsonp(...) function that is exposed by Webpack.
Once the modules are loaded, we need to identify which id corresponds to the Store. We search for a module containing exports.default.Wap and assign it's id as the Store ID.
You can find more details on my github wiki here
A faster method:
I grab the source of the "app" and find the store object then
I save it in ZStore global variable. :D
!function(){for(var t of document.getElementsByTagName("script"))t.src.indexOf("/app.")>0&&fetch(t.src,{method:"get"}).then(function(t){return t.text().then(function(t){var e=t.indexOf('var a={};t["default"]')-89;window.ZStore=window.webpackJsonp([],null,JSON.stringify(t.substr(e,10))).default})})}();
window.ZStore will contain the object.
Non minified version:
(function() {
function getStore(url) {
fetch(url, {
"method": 'get'
}).then(function(response) {
return response.text().then(function(data) {
var offset = data.indexOf('var a={};t["default"]') - 89;
window.ZStore = window.webpackJsonp([], null, JSON.stringify(data.substr(offset, 10))).default
});
});
}
for (var e of document.getElementsByTagName("script")) {
if (e.src.indexOf("/app.") > 0) getStore(e.src);
}
})();

How do I operate the m.withAttr tutorials code?

A contrived example of bi-directional data binding
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
return {user: new user.model("John Doe")};
},
view: function(controller) {
m.render("body", [
m("input", {onchange: m.withAttr("value", controller.user.name), value: controller.user.name()})
]);
}
};
https://lhorie.github.io/mithril/mithril.withAttr.html
I tried the above code does not work nothing.
It was the first to try to append the following.
m.mount(document.body, user);
Uncaught SyntaxError: Unexpected token n
Then I tried to append the following.
var users = m.prop([]);
var error = m.prop("");
m.request({method: "GET", url: "/users/index.php"})
.then(users, error);
▼/users/index.php
<?php
echo '[{name: "John"}, {name: "Mary"}]';
Uncaught SyntaxError: Unexpected token n
How do I operate the m.withAttr tutorials code?
Try returning m('body', [...]) from your controller.
view: function (ctrl) {
return m("body", [
...
]);
}
render should not be used inside of Mithril components (render is only used to mount Mithril components on existing DOM nodes).
The example is difficult to operate because it's contrived, it's not meant to be working out-of-the-box. Here's a slightly modified, working version:
http://jsfiddle.net/ciscoheat/8dwenn02/2/
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
return {user: new user.model("John Doe")};
},
view: function(controller) {
return [
m("input", {
oninput: m.withAttr("value", controller.user.name),
value: controller.user.name()
}),
m("h1", controller.user.name())
];
}
};
m.mount(document.body, user);
Changes made:
m.mount injects html inside the element specified as first parameter, so rendering a body element in view will make a body inside a body.
Changed the input field event to oninput for instant feedback, and added a h1 to display the model, so you can see it changing when the input field changes.
Using m.request
Another example how to make an ajax request that displays the retrieved data, as per your modifications:
http://jsfiddle.net/ciscoheat/3senfh9c/
var userList = {
controller: function() {
var users = m.prop([]);
var error = m.prop("");
m.request({
method: "GET",
url: "http://jsonplaceholder.typicode.com/users",
}).then(users, error);
return { users: users, error: error };
},
view: function(controller) {
return [
controller.users().map(function(u) {
return m("div", u.name)
}),
controller.error() ? m(".error", {style: "color:red"}, "Error: " + controller.error()) : null
];
}
};
m.mount(document.body, userList);
The Unexpected token n error can happen if the requested url doesn't return valid JSON, so you need to fix the JSON data in /users/index.php to make it work with your own code. There are no quotes around the name field.