I create a class in js which create a MySQL model like this :
class Model {
constructor(options = {}, table) {
this.options = options;
this.table = table;
this.create();
}
create() {
let queryString = `INSERT INTO ${this.table}`;
let fieldsString = ``;
let valuesString = ``;
for (let prop in this.options) {
fieldsString += `${prop},`;
valuesString += `${this.options[prop]},`;
//console.log(prop, this.options[prop]);
}
fieldsString = fieldsString.slice(0, -1);
valuesString = valuesString.slice(0, -1);
queryString = `${queryString} (${fieldsString}) VALUES (${valuesString})`;
console.log(queryString);
}
}
class UsersModel extends Model {
constructor(options = {}, table) {
super(options, table);
this.table = "users";
}
}
const u1 = new UsersModel({
username: "test",
mail: "darya",
});
when I run the constructor variable queryString looks like this: INSERT INTO undefined (username, mail) VALUES (test, Darya) why this.table is undefined? what I missed?
I will appreciate any help!
Because you first call is create (in super) and only after you set this.table.
class Model {
constructor(table) {
// table is undefined, call create..
this.table = table;
this.create();
}
create() {
let queryString = `INSERT INTO ${this.table}`;
console.log(queryString);
}
}
class UsersModel extends Model {
constructor(table) {
// table is undefined, call super..
super(table);
this.table = "users";
// here you already have table name, so create works with it
this.create();
}
}
new UsersModel();
I would remove the table parameter from the UsersModel constructor, because you aren't passing it in. It also shouldn't change, a table name will usually not be dynamic. Here's what I would change it to:
class UsersModel extends Model {
constructor(options) {
super(options, "users")
}
}
I also chose not to make the options parameter optional (pun not intended). I would even recommend taking the username and mail as separate parameters, then passing them down as an object to the base Model constructor.
Related
JS newbie here.
Hi, I am trying to add an instance as a Node to a div element I created,
but it gives this error:
TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
Should I not do it this way? is this wrong?
I want to create div element for every GitHub user I am searching for, adding 3 tags for each under a div.
I thought doing so by building a class with methods will help with DRY.
class AddElement {
constructor(name, tag) {
this.name = name;
this.tag = tag;
}
elem() {
this.name = document.createElement(this.tag);
document.body.append(this.name);
}
elemProp(property) {
this.elem();
this.name.innerHTML = property;
}
elemError() {
this.elem();
this.name.innerHTML = this.name + ' does not exist.';
this.name.style.color = 'red';
}
elemImg(property) {
this.elem();
this.name.src = property;
this.name.height = 150;
}
}
async function getJson(name) {
let response = await fetch(`https://api.github.com/users/${name}`);
if (response.status === 404) {
new AddElement('notFound', 'h3').elemError();
throw new Error('User not found!');
} else {
let data = await response.json();
return data;
}
}
async function gitUserInfo(name) {
let jsonData = await getJson(name);
let img = jsonData.avatar_url;
let login = jsonData.login;
let bio = jsonData.bio;
let arr = [
new AddElement('userimg', 'img').elemImg(img),
new AddElement('login', 'h2').elemProp(login),
new AddElement('bio', 'h3').elemProp(bio),
]
let div = document.createElement('div');
document.body.append(div);
arr.forEach(item => div.appendChild(item))
}
let search = document.getElementById('searchUser');
search.addEventListener('keyup', function(e) {
if (e.key === 'Enter') {
gitUserInfo(search.value)
} else if (search.value === '') {
removeTag('h2');
removeTag('h3');
removeTag('img');
}
});
function removeTag(tag) {
for (let i = 0; i < document.getElementsByTagName.length; i++) {
document.getElementsByTagName(tag)[i].remove();
}
}
You are not creating an array of htmlElement's but an array of AddElements.
However you were getting the exception as you were creating an array of undefined objects because you were calling the ctor and then some properties that had no return statement so arr looked like this:
arr[ undefined, undefined, undefined ]
It is bad practice to initialize an array in this manner. Initialize your objects first, apply the jsonData, THEN add them to the array. OR simply bypass the array and just modify the dom as you go.
Also, inside elem() you are modifying the dom by adding a newly created element to <body> before you've processed the data. If it were to work as you have it written you would be appending these objects twice.
In the code below I've stripped out the async function and just created a hard-coded json object for brevity.
Also, elements created by document.createElement() contain all the properties you need so there is no reason to create your own.
class AddElement {
constructor(name, tag) {
this.obj = document.createElement(tag);
this.obj.name = name;
}
elemProp(txt) {
this.obj.innerHTML = txt;
return this.obj;
}
elemImg(imgUrl) {
this.obj.src = imgUrl;
this.obj.height = 150;
return this.obj;
}
}
let jsonData = {
avatar_url: "https://picsum.photos/200",
login: "somelogin",
bio: "Lorem ipsum dolor sit amet"
}
function gitUserInfo(data) {
let arr = [
new AddElement('userimg', 'img').elemImg(data.avatar_url),
new AddElement('login', 'h2').elemProp(data.login),
new AddElement('bio', 'h3').elemProp(data.bio),
]
let div = document.createElement('div');
arr.forEach(item => div.appendChild(item));
document.body.append(div);
}
gitUserInfo(jsonData)
I am trying to set an react (v15) component its props callback function dynamically somehow. Something like below, bud it isn't working as I wanted it to be.
The whole idea behind this is that the popup needs to return specific data for an grid item that is pressed specifically in the grid (html table).
Any suggestions how to archive this dynamic setting of a component and its props?
The code below gives this error:
TypeError: can't define property "dynamicCallback": Object is not extensible
(I guess the element props is set with Object.preventExtensions)
getGridColumnData() {
var notificationsColumnsObj = columns.GridData; // from json file, Array With columns
for (let iColumn = 0; iColumn < notificationsColumnsObj.length; iColumn++) {
if (notificationsColumnsObj[iColumn].field === 'Landlord_Name') {
notificationsColumnsObj[iColumn]['editor'] = function (container, options) {
that.refs.searchCompanyPopup.props.dynamicCallback = function (data) {
var landlordName = null;
var landlordCode = null;
if (data) {
landlordCode = data.Code;
landlordName = data.Name;
}
options.model.set('Landlord_Code', landlordCode);
options.model.set('Landlord_Name', landlordCode);
};
};
}
}
return notificationsColumnsObj;
}
<SearchPopup ref="searchPopup" data={this.state.data} />
-
Update
How I managed to tet it working in the end. I used the state to set the function used for the callback by the popup. When you click an item in the grid: notificationsColumnsObj[iColumn]['editor'] is called, then the state is set for the popup callback when it finishes to call the function.
var that;
class TheComponent extends React.Component {
constructor(props,context) {
super(props,context);
this.state={
data: {},
landlordSelectedCallback: function (data) {},
}
that = this;
}
getGridColumnData() {
var notificationsColumnsObj = columns.GridData; // from json file, Array With columns
for (let iColumn = 0; iColumn < notificationsColumnsObj.length; iColumn++) {
//only one item will match this, not multiple
if (notificationsColumnsObj[iColumn].field === 'Landlord_Name') {
notificationsColumnsObj[iColumn]['editor'] = function (container, options) {
that.setState({
landlordSelectedCallback: function (data) {
var landlordName = null;
var landlordCode = null;
if (data) {
landlordCode = data.Code;
landlordName = data.Name;
}
options.model.set('Landlord_Code', landlordCode);
options.model.set('Landlord_Name', landlordCode);
}
}, () => { //callback function, after the state is set
$(ReactDOM.findDOMNode(that.refs.searchPopup)).modal(); //shows the <SearchPopup modal
$(ReactDOM.findDOMNode(that.refs.searchPopup)).off('hide.bs.modal');
$(ReactDOM.findDOMNode(that.refs.searchPopup)).on('hide.bs.modal', function (e) {
$(ReactDOM.findDOMNode(that.refs.searchPopup)).off('hide.bs.modal');
that.closeGridCellFromEditing(); //closes the grid cell edit mode
});
});
};
}
}
return notificationsColumnsObj;
}
render() {
return (<div>[other nodes]
<SearchPopup ref="searchPopup" data={this.state.data} onModalFinished={this.state.landlordSelectedCallback} />
</div>);
}
}
It's not working for two reasons:
Because your ref is called searchPopup, not props. Per the documentation for legacy string refs, you would access that via this.refs.searchProps.
Because props are read-only.
I'm a bit surprised that the second rule is actively enforced, but that's a good thing. :-)
If you want to change the props of a child component, you do so by changing your state such that you re-render the child with the new props. This is part of React's Lifting State Up / Data Flows Down philosophy(ies).
Rather than feeding a new callback function, just keep one function but feed data to it.
getGridColumnData() {
var notificationsColumnsObj = columns.GridData; //from json file, Array With columns
for (let iColumn = 0; iColumn < notificationsColumnsObj.length; iColumn++) {
if (notificationsColumnsObj[iColumn].field === 'Landlord_Name') {
notificationsColumnsObj[iColumn]['editor'] = (function (container, options) {
this.options = options
}).bind(this);
}
}
return notificationsColumnsObj;
}
dynamicCallback = function (data) {
var landlordName = null;
var landlordCode = null;
if (data) {
landlordCode = data.Code;
landlordName = data.Name;
}
this.options.model.set('Landlord_Code', landlordCode);
this.options.model.set('Landlord_Name', landlordCode);
}
render() {
return <SearchPopup ref="searchPopup" data={this.state.data} dynamicCallback = {this.dynamicCallback.bind(this)}/>
}
I'm trying to pass a pointer to a function through the parameters, so the function can update the parameters of the object. The object is bound to the view using {{tasks}} in the tasks.component.html file. Somehow, it looks like the mutations that I do in the provideAndDisplayTasks() function don't change the tasks property in tasks.component.ts at all. I've got similar code that does the same. There it correctly updates the object. I don't understand why it doesn't work in this situation.
How do I update the array, without cloning it?
tasks.component.ts:
private provideAndDisplayTasks() {
const self = this;
self.tasks.push(new Task());
console.log('self.tasks', self.tasks);
this.tasksRepository.provideTasks(self.tasks);
}
tasks-repository.ts:
public provideTasks(tasks: Task[]) {
console.log('parameter', tasks);
this.get('truelime.task/all').subscribe(
data => this.addTasksToTaskArray(tasks, data),
error => {
if (error.status === 401) {
this.router.navigateByUrl('login');
}
Logger.getInstance().logError(error);
}
);
}
private addTasksToTaskArray(tasks: Task[], tasksDto) {
const taskArray = tasksDto.truelime_tasks[0].TrueLime_Task;
for (let i = 0; i < taskArray.length; i++) {
const taskDto = taskArray[i];
const task = new Task();
task.id = taskDto.TaskID;
task.title = taskDto.TaskTitle;
task.description = taskDto.TaskDescription;
task.status = taskDto.TaskStatus;
tasks.push(task);
console.log('tasks', tasks);
}
}
Console logs:
I may be missing something basic as why is it happening.
GET: example.com/users
//gives all data
GET: example.com/users?status=1
//gives data with status = 1
GET: example.com/users // this does not work
gives same data as pervious API condition with status=1
On third hit, self.whereObj is not initialising to default empty object instead it takes previous value of {'status' = '1'}, however self.page and self.limit is taking default value if no query parameter is provided in query string.
example.com/users?limit=3, // takes override to 3 form default value of 5
example.com/users // self.limit takes default 5 and this works fine
So my question is why the self.limit (simple string variable) is initialising however self.whereObj is not ?
var Bookshelf = require('../../dbconfig').bookshelf;
Bookshelf.Collection = Bookshelf.Collection.extend({
limit: 5,
page: 1,
whereObj: {}
myFetch: function (query_params,expectedWhereFields) {
var self = this;
var whereObj = self.whereObj ; // this is not initializing
// var whereObj = {}; this is initialising
var page = self.page;
var limit = self.limit; //this is not showing nay initialisation error
for (var x in query_params) {
if (expectedWhereFields.includes(x)) {
whereObj[x] = query_params[x];
}
if (x === 'page') {
page = query_params[x];
}
if (x === 'limit') {
limit = query_params[x];
}
}
var offset = (page - 1) * limit;
function fetch() {
return self.constructor.forge()
.query({where: whereObj})
.query(function (qb) {
qb.offset(offset).limit(limit);
})
.then(function (collection) {
return collection;
})
.catch(function (err) {
return err
});
}
return new fetch();
}
});
module.exports = Bookshelf;
UPDATED
service.js
var Model = require('./../models/Users');
var express = require('express');
var listUsers = function (query_params, callback) {
var expectedWhereFields = ["type", "status", "name"];
Model.Users
.forge()
.myFetch(query_params, expectedWhereFields)
.then(function (collection) {
return callback(null, collection);
})
.catch(function (err) {
return callback(err, null);
});
};
module.exports = {
listUsers: listUsers
};
model/Users.js
var Bookshelf = require('../../dbconfig').bookshelf;
var Base = require('./base');
// Users model
var User = Bookshelf.Model.extend({
tableName: 'user_table'
});
var Users = Bookshelf.Collection.extend({
model: User
});
module.exports = {
User: User,
Users: Users
};
So my question is why the self.limit (simple string variable) is initialising however self.whereObj is not?
Because objects are reference values. When you set var whereObj = self.whereObj;, both refer to the same object, and when you copy the query parameters into the object properties you are effectively writing into your defaults instance. This does not happen with primitive values such as strings - they don't have mutable properties.
I am new to Parse and Cloud Code, but I have managed to write a few AfterSave Cloud Code functions that work fine. However, I am having a lot of trouble with this one, and I cannot figure out why. Please help...
I have
Two PFObject classes: Message and MessageThread
Message contains chat messages that are associated with a MessageThread
MessageThread contains an array of members (which are all PFUsers)
Upon insert to Message, I want to look up all the members of the related MessageThread and Push notifications to them
class MessageThread: PFObject {
#NSManaged var members: [PFUser]
#NSManaged var lastMessageDate: NSDate?
#NSManaged var messageCount: NSNumber?
override class func query() -> PFQuery? {
let query = PFQuery(className: MessageThread.parseClassName())
query.includeKey("members")
return query
}
init(members: [PFUser], lastMessageDate: NSDate?, messageCount: NSNumber?) {
super.init()
self.members = members
self.lastMessageDate = lastMessageDate
self.messageCount = messageCount
}
override init() {
super.init()
}
}
extension MessageThread: PFSubclassing {
class func parseClassName() -> String {
return "MessageThread"
}
override class func initialize() {
var onceToken: dispatch_once_t = 0
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
}
class Message: PFObject {
#NSManaged var messageThreadParent: MessageThread
#NSManaged var from: PFUser
#NSManaged var message: String
#NSManaged var image: PFFile?
override class func query() -> PFQuery? {
let query = PFQuery(className: Message.parseClassName())
query.includeKey("messageThreadParent")
return query
}
init( messageThreadParent: MessageThread, from: PFUser, message: String, image: PFFile?) {
super.init()
self.messageThreadParent = messageThreadParent
self.from = from
self.message = message
self.image = image
}
override init() {
super.init()
}
}
extension Message: PFSubclassing {
class func parseClassName() -> String {
return "Message"
}
override class func initialize() {
var onceToken: dispatch_once_t = 0
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
}
Approach
From the request object (a Message), get its messageThreadParent
Lookup the members of the parent MessageThread, loop through them, etc.
The problem
When I try to retrieve the MessageThread object, I attempt to query on Id == threadParent.objectId. However, this query always returns all 8 of my current MessageThreads, rather than the single one I need.
Parse.Cloud.afterSave(Parse.Object.extend("Message"), function(request) {
Parse.Cloud.useMasterKey();
var theMsg = request.object;
var threadParent;
var currUsername = request.user.get("username");
var threadUsers;
var usernameArray;
threadParent = request.object.get("messageThreadParent");
// promise
queryM = new Parse.Query(Parse.Object.extend("MessageThread"));
queryM.include("members");
queryM.equalTo("id", threadParent.objectId);
queryM.find().then(function (threadParam) {
console.log(" threads: ");
console.log(threadParam.length); //this returns 8, which is the number of threads I have. I would expect just 1, matching threadParent.objectId...
console.log("thread is: ");
//... additional code follows, which seems to work...
After grappling with a separate problem all day I finally figured out that in Parse's Javascript SDK there is a difference between "id" and "objectId".
Changing this
queryM.equalTo("id", threadParent.objectId); // doesn't work
to
queryM.equalTo("objectId", threadParent.id); // works!
fixed my problem.