VueFormGenerator, Custom Fieds didn't update value in model - forms

I try to create my custom field.
so i do this :
import Vue from 'vue';
import Datepicker from 'vuejs-datepicker';
import VueFormGenerator from 'vue-form-generator';
Vue.component('fieldDatePicker', {
template: '<datepicker :placeholder="schema.placeholder" v-model="datePicker_model"></datepicker>',
components: {
Datepicker,
},
data: function() {
return {
datePicker_model: '',
};
},
mixins: [VueFormGenerator.abstractField],
mounted() {
this.datePicker_model = this.schema.default;
},
computed: {
value: {
get: function() {
return this.$moment(this.datePicker_model).format('DD-MM-YY')
},
set: function(newValue) {
throw 'TODO : ' + newValue;
// TODO converte and set value ! this.datePucker_model = newValue;
},
},
},
});
But my component didn't update the model of VueFormGenerator ...
What i forget ???

i add this to my component and it work ;)
watch: {
datePicker_model: function(newValue, OldValue) {
if(this.format(newValue) !== this.format(OldValue)) {
this.setModelValueByPath(this.schema.model, this.format(newValue));
}
},
},

Related

How to properly define a CodeMirror language?

I'm trying to provide a basic autocompletion for something like this:
db.collection("Items").where("name", "==", "temp").limit(1).get();
Here's the code I have so far, using StreamLanguage of CodeMirror 6:
import {
IndentContext,
LanguageSupport,
StreamLanguage,
StringStream
} from "#codemirror/language";
import { tags as t } from "#lezer/highlight";
export const FireStoreLanguage = StreamLanguage.define({
name: "firestore",
startState: (indentUnit: number) => {
return {};
},
token: (stream: StringStream, state: any = {}): string | null => {
console.log(stream);
if (stream.match("db")) {
state.db = true;
return "keyword";
}
if (stream.match(".")) {
if (state.db) {
state.db = false;
state.collection = true;
return "keyword";
} else if (state.collection) {
state.collection = false;
state.where = true;
return "keyword";
} else if (state.where) {
state.where = false;
state.limit = true;
return "keyword";
} else if (state.limit) {
state.limit = false;
return "keyword";
}
}
if (stream.match("collection")) {
if (state.db) {
state.collection = true;
return "keyword";
}
}
if (stream.match("where")) {
if (state.collection) {
state.where = true;
return "keyword";
}
}
if (stream.match("limit")) {
if (state.where) {
state.limit = true;
return "keyword";
}
}
if (stream.match("get")) {
if (state.limit) {
state.limit = false;
return "keyword";
}
}
if (stream.match(/"(?:[^\\"]|\\.)*"/)) {
if (state.collection) {
return "string";
}
if (state.where) {
state.where = false;
state.whereValue = true;
return "string";
}
if (state.whereValue) {
state.whereValue = false;
return "string";
}
if (stream.match("==")) {
if (state.whereValue) {
state.whereValue = false;
state.whereOperator = true;
return "operator";
}
}
if (stream.match(/[0-9]+/)) {
if (state.limit) {
return "number";
}
}
}
stream.next();
return null;
},
blankLine: (state: {}, indentUnit: number): void => {},
copyState: (state: {}) => {},
indent: (
state: {},
textAfter: string,
context: IndentContext
): number | null => {
return 1;
},
languageData: {
commentTokens: { line: ";" },
},
tokenTable: {
db: t.keyword,
dot: t.punctuation,
collection: t.keyword,
get: t.keyword,
lParen: t.punctuation,
rParen: t.punctuation,
string: t.string,
},
});
export function firestore() {
return new LanguageSupport(FireStoreLanguage);
}
In React, here's how I use it(after building it):
import CodeMirror from "#uiw/react-codemirror";
import React from "react";
import { firestore } from "./firestore";
function App() {
const onChange = React.useCallback((value, viewUpdate) => {
console.log("value:", value);
}, []);
return (
<CodeMirror
value={``}
height="100vh"
extensions={[firestore()]}
onChange={onChange}
/>
);
}
export default App;
The editor loads okay, but no autocompletion is provided while I type!
What am I doing wrong or missing in the code above?
I was missing these parts:
export const FireStoreCompletion = FireStoreLanguage.data.of({
autocomplete: completeFromList([
{ label: "db", type: "namespace" },
{ label: "collection", type: "function" },
{ label: "where", type: "function" },
{ label: "limit", type: "function" },
{ label: "get", type: "function" },
]),
});
export function firestore() {
return new LanguageSupport(FireStoreLanguage, [FireStoreCompletion]);
}

Change field in object in array of object

I have a field achivment with an array of objects. I need to update the field currentPoints in one object that I will find by field name in the array.
Code model of mongoose:
const achive = new Schema(
{
achiveId: ObjectId,
name: { type: String, required: true },
finishedPoints: { type: Number, required: true },
currentPoints: {
type: Number,
default: 0,
set: function (v) {
if (v >= this.finishedPoints) this.isFinished = true;
return v;
}
},
isFinished: { type: Boolean, default: false }
},
{ _id: false }
);
const achivesSchema = new Schema({
userId: ObjectId,
achivement: [achive]
});
Code query:
export async function progressAchive(req, res) {
const value = 3;
try {
const test = await Achives.updateOne(
{
userId: req.user._id,
achivement: { $elemMatch: { name: req.params.nameAchive } }
},
{ $set: { achivement: { currentPoints: value } } },
{ new: true }
);
res.json(test);
} catch (e) {
console.log(e);
}
}
Instead of updating, it removes all objects from the array and leaves them one object with the currentPoint field. How can I update this like I want?
You should use the following for update
const test = await Achives.updateOne(
{
userId: req.user._id,
},
{
$set:{"achivement.$[el].currentPoints": value}
},
{
arrayFilters:[{
"el.name": req.params.nameAchive
}],
new: true
}
);

set empty values in MongoDB

I'm trying to set some values for the devices that don't have that parameters but for dataPersist and each of the timestamps is not working. I don't know if it'a a problem of the if structure I'm using, but for dataContainer and dataImageList is working.
public getDeviceById = (deviceId: string): Promise<IDevice> => {
return new Promise((resolve, reject) => {
Device.findOne({ _id: mongoose.Types.ObjectId(deviceId) }).then((doc: any) => {
if (doc) {
if (!doc.dataPersist) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataPersist: { persistSize: "", timestamp: new Date()} }})
}
if (!doc.dataContainer) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataContainer: { containerInfo:[], dockerInfo: [], timestamp: new Date()} }})
}
if(!doc.dataImageList) {
Device.updateOne({_id: mongoose.Types.ObjectId(deviceId) }, { $set: { dataImageList: { imageList:[], timestamp: new Date()} }})
}
resolve(doc);
} else {
reject("fieldNotFoundError,device,getDeviceById");
}
}).catch((err: any) => {
console.error(err);
reject("databaseError,043");
});
});
}
Why is that?
This is my model:
import mongoose from "mongoose";
import { IDevice } from "./iDevice";
interface IDeviceModel extends IDevice, mongoose.Document {
}
const deviceSchema = new mongoose.Schema({
dataPersist: {
persistSize: String,
timestamp: Date
},
dataImageList: {
imageList: Array,
timestamp: Date
},
dataContainer: {
containerInfo: Array,
dockerInfo: Array,
timestamp: Date
}
});
const Device = mongoose.model<IDeviceModel>("device", deviceSchema);
export = Device;
Thank in advance for your help.

Extend control with HBox container and inherited but customized event

The idea is to have a HBox container under the MultiComboBox control to which selected tokens will be pushed. I have followed different tutorials and couldn't get a success. A multiComboBox is simply now shown.
The idea:
Simplified (testing) implementation of custom control:
sap.ui.define([
'sap/m/MultiComboBox',
'sap/m/HBox'
], function (MultiComboBox, HBox) {
'use strict';
/**
* Constructor for a new MultiCombobox with tokens.
*/
return MultiComboBox.extend('drex.control.DropDownWithTags', {
metadata: {
aggregations: {
_tokensContainer: { type: 'sap.m.HBox', multiple: false }
},
},
init: function () {
MultiComboBox.prototype.init.apply(this, arguments);
this.setAggregation('_tokensContainer', new HBox());
},
_addToken: function () {
this.getAggregation('_tokensContainer').insertItem({text: 'test'});
},
_handleSelectionLiveChange: function(oControlEvent) {
this._addToken();
MultiComboBox.prototype._handleSelectionLiveChange.apply(this, arguments);
},
renderer: function (rm, DropDownWithTags) {
rm.write('<div');
rm.writeControlData(DropDownWithTags);
rm.write('>');
rm.renderControl(DropDownWithTags.getAggregation('_tokensContainer'));
rm.write('</div>');
}
});
});
XML (no change, except for a name, could that be a problem?). Adding HBox aggregation to it does not help.
<drex:DropDownWithTags
items="{
path: 'diseaseList>/allDiseases'
}"
selectedKeys="{filterModel>/disease}"
selectionFinish="onSelectDisease">
<core:Item key="{diseaseList>id}" text="{diseaseList>Name}"/>
</drex:DropDownWithTags>
Any idea why it happens ? I cannot see my mistake.
there are many ways to do this. here is one way
sap.ui.define([
'sap/ui/core/Control',
'sap/ui/core/Item',
'sap/m/MultiComboBox',
'sap/m/HBox',
'sap/m/Text'
], function (Control, Item, MultiComboBox, HBox, Text) {
Control.extend('DropDownWithTags', {
metadata: {
aggregations: {
combo: { type: 'sap.m.MultiComboBox', multiple: false },
_hbox: { type: 'sap.m.HBox', multiple: false }
},
},
init: function () {
Control.prototype.init.apply(this, arguments);
this.setAggregation('_hbox', new HBox({
items: [
]
}));
},
renderer: function (rm, oControl) {
rm.write('<div');
rm.writeControlData(oControl);
rm.write('>');
rm.write('<div>');
rm.renderControl(oControl.getAggregation('combo'));
rm.write('</div>');
rm.write('<div>');
rm.renderControl(oControl.getAggregation('_hbox'));
rm.write('</div>');
rm.write('</div>');
},
onAfterRendering: function() {
var combo = this.getAggregation('combo')
var hbox = this.getAggregation('_hbox');
combo.attachEvent("selectionChange", function() {
hbox.destroyItems();
var text = this.getSelectedItems().map(function(item) {
return item.getText();
});
if (text.length > 0) {
hbox.addItem(new Text({ text: text.join(",")}))
}
})
}
});
var combo = new DropDownWithTags({
combo: new MultiComboBox({
items: [
new Item({
key: "test",
text: "test"
}),
new Item({
key: "test1",
text: "test1"
})
]
})
});
combo.placeAt("content")
});

How to build a quantity plus / minus composite control?

I try to build an input field for quantities with plus and minus buttons. More like to understand how composite controls are working. I found this documentation.
I thought I could use the new control like this:
new ex.perimental.Input({
width: "14em",
editable: true,
input: new sap.m.Input({
width: "8em",
value: {
path: "model>Quantity",
type: new sap.ui.model.type.Integer({}, {
minimum:0
})
},
description: "{model>Unit}"
}),
})
The control code looks like this:
sap.ui.define([
"sap/ui/core/Control",
"sap/m/Button",
"sap/m/Input"
], function(Control, Button, Input) {
"use strict";
return Control.extend("ex.perimental.Input", {
metadata: {
properties: {
width: {
type: "string",
defaultValue: "14em",
},
editable: {
type: "boolean",
defaultValue: false,
},
},
aggregations: {
_increaseButton: {
type: "sap.m.Button",
multiple: false,
visibility: "hidden",
},
_decreaseButton: {
type: "sap.m.Button",
multiple: false,
visibility: "hidden",
},
input: { type: "sap.m.Input", multiple: false },
_hBox: { type: "sap.m.HBox", multiple: false, visibility: "hidden" },
},
events: {
increase: {},
decrease: {},
},
},
_onDecrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this.getAggregation("input").getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) - 1;
}
oInput.setValue(newValue);
this.fireEvent("decrease", {
oldValue: oldValue,
newValue: newValue,
});
},
_onIncrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this.getAggregation("input").getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) + 1;
}
oInput.setValue(newValue);
this.fireEvent("increase", {
oldValue: oldValue,
newValue: newValue,
});
},
init: function() {
this.setAggregation(
"_decreaseButton",
new Button({
icon: "sap-icon://sys-minus",
press: this._onDecrease.bind(this),
})
);
this.setAggregation(
"_increaseButton",
new Button({
icon: "sap-icon://sys-add",
press: this._onIncrease.bind(this),
})
);
this.setAggregation(
"_hBox",
new sap.m.HBox({
items: [
this.getAggregation("_decreaseButton"),
this.getAggregation("_increaseButton"),
],
})
);
},
setEditable: function(sValue) {
debugger;
// aggregations will be null now
// I assume because they are reused in the HBox control
// this.getAggregation("_increaseButton").setEditable(sValue);
// this.getAggregation("_decreaseButton").setEditable(sValue);
// this.getAggregation("input").setEditable(sValue);
},
setWidth: function(sValue) {
this.getAggregation("_hBox").setWidth(sValue);
},
setInput: function(oInput) {
this.setAggregation("input", oInput);
var oHBox = this.getAggregation("_hBox");
oHBox.insertItem(oInput, 1);
},
renderer: function(oRenderManager, oControl) {
oRenderManager.write("<div");
oRenderManager.writeControlData(oControl);
oRenderManager.addClass("myStyle");
oRenderManager.writeClasses();
oRenderManager.write(">");
oRenderManager.renderControl(oControl.getAggregation("_hBox"));
oRenderManager.write("</div>");
}
});
});
It will be rendered but the setEditable is not working.
The buttons (used inside the HBox control again) are not reachable via getAggregation. Also the input field (set from outside) can't be accessed.
Not sure how to do it right. Anyone an idea?
Edit2
This is the latest version but still not working.
I am asking me how to put the externally defined input control into the right place inside the inner Hbox control and be able to access this control in methods like setEditable?
sap.ui.define([
"sap/ui/core/Control",
"sap/m/Button",
"sap/m/Input"
], function(Control, Button, Input) {
"use strict";
return Control.extend("ex.perimental.Input", {
metadata: {
properties: {
width: {
type: "string",
defaultValue: "14em",
},
editable: {
type: "boolean",
defaultValue: false,
},
},
aggregations: {
_hBox: { type: "sap.m.HBox", multiple: false, visibility: "hidden" },
},
associations: {
input: { type: "sap.m.Input", multiple: false, singularName: "input" },
},
events: {
increase: {},
decrease: {},
},
},
_onDecrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this._input.getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) - 1;
}
this._input.setValue(newValue);
this.fireEvent("decrease", {
oldValue: oldValue,
newValue: newValue,
});
},
_onIncrease: function(oEvent) {
var oResourceBundle = this.getModel("i18n").getResourceBundle();
var oldValue = this._input.getValue();
var newValue = 0;
if (!isNaN(Number(oldValue))) {
newValue = Number(oldValue) + 1;
}
this._input.setValue(newValue);
this.fireEvent("increase", {
oldValue: oldValue,
newValue: newValue,
});
},
init: function() {
this._decreaseButton = new Button({
icon: "sap-icon://sys-minus",
press: this._onDecrease.bind(this),
});
this._increaseButton = new Button({
icon: "sap-icon://sys-add",
press: this._onIncrease.bind(this),
});
this.setAggregation(
"_hBox",
new sap.m.HBox({
items: [
this._decreaseButton,
this.getAssociation("input"),
this._increaseButton,
],
})
);
},
setEditable: function(sValue) {
var bEditable = false;
if (sValue === true) {
bEditable = true;
}
this._decreaseButton.setEnabled(bEditable);
this._increaseButton.setEnabled(bEditable);
// seems not always working
this._input.setEditable(bEditable);
},
setWidth: function(sValue) {
this.getAggregation("_hBox").setWidth(sValue);
},
setInput: function(oInput) {
this.setAssociation("input", oInput);
this._input = oInput;
var oHBox = this.getAggregation("_hBox");
oHBox.insertItem(oInput, 1);
},
renderer: function(oRenderManager, oControl) {
oRenderManager.write("<div");
oRenderManager.writeControlData(oControl);
oRenderManager.addClass("myStyle");
oRenderManager.writeClasses();
oRenderManager.write(">");
oRenderManager.renderControl(oControl.getAggregation("_hBox"));
oRenderManager.write("</div>");
}
});
});
I still have problems with the association handling (updated code) I guess handling the association should be done different? Sometimes the input field is still null.
a control can only be aggregated by one control at a time.
This is the difference between associations (control may be at multiple at the same time) and aggregations.
What you can do in your init is:
this._decreaseButton = new Button (...)
Basically you only need one aggregation for your HBox.
If your buttons are then aggregated by the HBox they will know the models of the parent and also be destroyed.
The only thing you need to check if your Root control is a Ancestor of the created controls (use myControl.getParent()).
best regards,
Tobias