Angular Formly, detect keycode in onKeypress - angular-formly

I have an Angular Formly-based form in my Angular-SPA. In one of the fields the user can enter data with the help of a physical barcode-scanner. The scanner enters the numbers from the barcode followed by 'enter' (keycode: 13).
When the 'enter' is entered, the form should preform an action (not a form submit-action, but an internal search to make sure the entered number doesn't already exists in the system), then the user can proceed to fill out the form.
In vanilla javascript, its possible to do this by a simple onKeypress-event:
function checkInput(event) {
if (event.keyCode == 13) {
console.log("Preforming search");
}
}
<input type="text" onKeypress="checkInput(event)">
In Formly however, it's possible to add a property 'onKeypress', but it doesn't seem to pass the same event as vanilla javascript. This is some of my Angular Formly code:
templateOptions : {
placeholder: "ISBN",
onKeypress: function(model, options, _this, event) {
console.log(model);
console.log(options);
console.log(_this);
console.log(event);
}
}
When I run this code, the event-object is 'undefined', instead of being the same event as in vanilla javascript.
Is there any way to access the actual event in the onKeypress-property of Angular Formly-forms?
Cheers!

Update
Found a solution! I was trying to do this to a custom type, which is then inherited to the actual input-element.
Seems like Formly doesn't allow inheritance of the onKeypress-property. I moved the onKeypress-property to the actual element, and I was able to solve the problem.
It's a bit unfortunate that this can not be inherited, but have to be defined for each element instead.
My problem (simplified) :
formlyConfig.setType({
name: 'multiInput',
templateUrl: 'app/editor/multiInput.html',
defaultOptions: {
wrapper: ['bootstrapLabel', 'boostrapHasError'],
templateOptions: {
inputOptions: {
type: 'input',
wrapper: null
},
onKeypress: function(model, options, _this, event) {
// DO SEARCH (NOT WORKING)
}
}
}
}
// --------------------------- //
{
className: 'col-xs-10',
type: "multiInput",
key: "isbns",
templateOptions: {
label: 'ISBN',
inputOptions: {
templateOptions: {
required: true,
disabled: true,
placeholder: 'ISBN'
}
},
key: "isbn",
}
}
}
My solution:
formlyConfig.setType({
name: 'multiInput',
templateUrl: 'app/editor/multiInput.html',
defaultOptions: {
wrapper: ['bootstrapLabel', 'boostrapHasError'],
templateOptions: {
inputOptions: {
type: 'input',
wrapper: null
}
}
}
}
// --------------- //
{
className: 'col-xs-10',
type: "multiInput",
key: "isbns",
templateOptions: {
label: 'ISBN',
inputOptions: {
templateOptions: {
required: true,
disabled: true,
placeholder: 'ISBN',
onKeypress: function(model, options, _this, event) {
// DO SEARCH (WORKING!)
}
}
},
key: "isbn"
}
}
}
It's not the best solution, I want more inputs to have the onKeypress-event. But it looks like I might have to add them separately.

Related

How to create a custom button in Jodit to wrap the text in a code tag?

Basically I want to be able to generate html like <code>{a: true}</code>
As far as I can tell, the button should do the same thing as the "underline" button for example, except it will wrap the text in <code> instead of <u>;
I have tried using this:
{
buttons:
'bold,strikethrough,underline,italic,eraser,|,superscript,subscript,|,ul,ol,align,|,outdent,indent,|,font,fontsize,brush,paragraph,|,image,video,table,link,|,undo,redo,\n,selectall,cut,copy,paste,copyformat,|,hr,symbol,source,fullsize,print,code',
language: lang,
placeholder,
toolbarAdaptive: false,
uploader: {
insertImageAsBase64URI: true,
},
controls: {
code: {
name: 'code',
iconURL: 'someurl.com',
tagRegExp: '_PxEgEr_/^(code)$/i',
tags: ['code'],
tooltip: 'Code',
},
},
}
The button shows up in the toolbar, but nothing happens when I click it. The documentation shows buttons that insert text, but I need a button that wraps text instead.
Ok I figured it after going through their code, it's not well documented, but this is how you do it:
{
buttons: 'blockquote,code',
controls: {
code: {
name: 'code',
iconURL: 'someiconurl.com',
tooltip: 'Insert Code Block',
exec: function (editor) {
editor.execCommand('formatBlock', false, 'code');
},
isActive: (editor, control) => {
const current = editor.s.current();
return Boolean(
current && Jodit.modules.Dom.closest(current, 'code', editor.editor)
);
},
},
blockquote: {
name: 'blockquote',
iconURL: 'someiconurl.com',
tooltip: 'Insert blockqoute',
exec: function (editor) {
editor.execCommand('formatBlock', false, 'blockquote');
},
isActive: (editor, control) => {
const current = editor.s.current();
return Boolean(
current && Jodit.modules.Dom.closest(current, 'blockquote', editor.editor)
);
},
},
},
}

static passProps not changing on Navigation.push

I am using react-native-navigation v2 from Wix, trying to push a screen to an existing stack. Here is my push code:
Navigation.push(this.props.componentId, {
component: {
name: 'chapel.search'
}
})
And my options object
static options (passProps) {
console.log('Firing static method')
return {
component: {
name: 'chapel.search',
topBar: {
visible: true,
leftButtons: [
{
id: 'back',
testID: 'back',
icon: require('../../Images/back.png')
}
],
title: {
component: {
name: 'chapel.navtitle',
alignment: 'center',
passProps: { text: 'Search' }
}
},
rightButtons: []
}
}
}
}
I never see the log statement and the topbar options do not change. Should they?
When I use Navigation.mergeOptions with the above options object in the constructor of my target screen, the options appear, so this is what I am using for now.
Using android, have not tested with iOS as yet. Will update when I do.
I'm originating static options like this in my component:
static get options() {
return {
...
}
}
And when i push from another screen and want to override some defaults, i do it like below:
Navigation.push(this.props.componentId, {
component: {
name: 'chapel.search',
passProps: {
myProp: myprop1
},
options: {
topBar: {
title: {
text: newTitleOverridingStaticOne
}
}
}
}
});
I don't know if static options (passProps){...} is valid, but you can try like i show above to check if it is resolved

How to manage GraphQL child objectType that can be nullable in an output type?

I'm setting up a nodeJS GraphQL API and I'm experimenting a blocking point regarding one of my resource output type.
The feature is a form that contain three different level :
Level 1- formTemplate
Level 2- formItems (templateId, type (video, image, question) - 1-N relation with formTemplate)
Level 3- formQuestions (0-1 relation with formItem if and only if formItems.type is 'question')
My GraphQL resource is returning all the templates in the database so it's an array that for each template is returning all his items and each item of type "question" needs to return an array containing the associated question.
My problem is : I really don't know how to return an empty object type for the formItems where type is different from "question" or if there is a better approach for this kind of situation
I've tried to look at GraphQL directives and inline fragments but I think it really needs to be manage by the backend side because it's transparent for the API consumer.
const formTemplate = new GraphQLObjectType({
name: 'FormTemplate',
fields: () => {
return {
id: {
type: new GraphQLNonNull(GraphQLInt)
},
authorId: {
type: new GraphQLNonNull(GraphQLInt)
},
name: {
type: new GraphQLNonNull(GraphQLString)
},
items: {
type: new GraphQLList(formItem),
resolve: parent => FormItem.findAllByTemplateId(parent.id)
}
}
}
})
const formItem = new GraphQLObjectType({
name: 'FormItem',
fields: () => {
return {
id: {
type: new GraphQLNonNull(GraphQLInt)
},
templateId: {
type: new GraphQLNonNull(GraphQLInt)
},
type: {
type: new GraphQLNonNull(GraphQLString)
},
question: {
type: formQuestion,
resolve: async parent => FormQuestion.findByItemId(parent.id)
}
}
}
})
const formQuestion= new GraphQLObjectType({
name: 'FormQuestion',
fields: () => {
return {
id: {
type: new GraphQLNonNull(GraphQLInt)
},
itemId: {
type: new GraphQLNonNull(GraphQLInt)
},
type: {
type: new GraphQLNonNull(GraphQLString)
},
label: {
type: new GraphQLNonNull(GraphQLString)
}
}
}
})
My GraphQL request :
query {
getFormTemplates {
name
items {
type
question {
label
type
}
}
}
}
What I'm expected is
{
"data": {
"getFormTemplates": [
{
"name": "Form 1",
"items": [
{
"type": "question",
"question": {
"label": "Question 1",
"type": "shortText"
},
{
"type": "rawContent"
"question": {}
}
]
}
]
}
}
I'd design your "level 2" items so that the "type" property corresponded to actual GraphQL types, implementing a common interface. Also, in general, I'd design the schema so that it had actual links to neighboring items and not their identifiers.
So if every form item possibly has an associated template, you can make that be a GraphQL interface:
interface FormItem {
id: ID!
template: FormTemplate
}
Then you can have three separate types for your three kinds of items
# Skipping VideoItem
type ImageItem implements FormItem {
id: ID!
template: FormTemplate
src: String!
}
type QuestionItem implements FormItem {
id: ID!
template: FormTemplate
questions: [FormQuestion!]!
}
The other types you describe would be:
type FormTemplate {
id: ID!
author: Author!
name: String!
items: [FormItem!]!
}
type FormQuestion {
id: ID!
question: Question
type: String!
label: String!
}
The other tricky thing is, since not all form items are questions, you have to specifically mention that you're interested in questions in your query to get the question-specific fields. Your query might look like
query {
getFormTemplates {
name
items {
__typename # a GraphQL builtin that gives the type of this object
... on Question {
label
type
}
}
}
}
The ... on Question syntax is an inline fragment, and you can similarly use it to pick out the fields specific to other kinds of form items.
Thank you David for your answer !
I've figured it out how to solve my problem using inline fragments and UnionTypes that seems to be the most adapted for this use case. Here is the code :
const formItemObjectType = new GraphQLUnionType({
name: 'FormItemObject',
types: [formItemContent, formItemQuestion],
resolveType(parent) {
switch (parent.type) {
case ('question'): return formItemQuestion
default: return formItemContent
}
}
})
and the GraphQL query using inline fragment:
query {
getFormTemplates {
name
items {
...on FormItemContent {
type,
meta
}
...on FormItemQuestion {
type,
meta,
question {
label
}
}
}
}
}

Allow object of objects within inputs in action

I'm wondering how I have objects of objects as inputs in an action - for example,
module.exports = {
friendlyName: 'Signup',
description: 'Signup a user for an account.',
inputs: {
bride: {
firstName: {
description: 'The first name of the bride',
type: 'string',
required: true
}
},
groom: {
lastName: {
description: 'The first name of the groom',
type: 'string',
required: true
}
}
}
exits: {},
fn: async function (inputs, exits) {
sails.log.debug(inputs.bride.firstName); // I would like to be able to reference input like this
return exits.success();
}
};
How can I do this and access inputs.bride.firstName (instead of having to do inputs.brideFirstName)?
I receive the following error trying to do this:
'Invalid input definition ("bride"). Must have `type`, or at least some more information. (If you aren\'t sure what to use, just go with `type: \'ref\'.)' ] } }
Another way is to implement a custom validation with objects of objects
module.exports = {
friendlyName: 'Signup',
description: 'Signup a user for an account.',
inputs: {
bride: {
description: 'The first name of the bride',
type: 'json', // {'firstName': 'luis'}
custom: function(value) {
return _.isObject(value) && _.isString(value.firstName)
}
},
groom: {
description: 'The first name of the groom',
type: 'json', // {'lastname': 'lazy'}
custom: function(value) {
return _.isObject(value) && _.isString(value.lastName)
}
}
}
exits: {},
fn: async function (inputs, exits) {
sails.log.debug(inputs.bride.firstName); // luis
return exits.success();
}
};
More information on: https://sailsjs.com/documentation/concepts/models-and-orm/validations#?custom-validation-rules
You should use a json type for this case. Sails don't check attributes in json type.
module.exports = {
friendlyName: 'Signup',
description: 'Signup a user for an account.',
inputs: {
bride: {
description: 'The first name of the bride',
type: 'json', // {'firstName': 'luis'}
required: true
},
groom: {
description: 'The first name of the groom',
type: 'json', // {'lastname': 'lazy'}
required: true
}
}
exits: {},
fn: async function (inputs, exits) {
sails.log.debug(inputs.bride.firstName); // luis
return exits.success();
}
};
More info about types: https://sailsjs.com/documentation/concepts/models-and-orm/attributes#?type

Adding a column to a dstore backed dgrid

I have a grid with five columns - username, email, enabled, locked and remove.
Username, email, enabled and locked are sourced from the server. Remove is a client-side element used to indicate that a row should be removed.
I would like to either inject the default value of remove in the store on the client side as the grid content is loading, or set it as the user interacts with the CheckBox widget.
How can I catch the code which is requesting the objects from the server and add another column?
Or, is there a better way to do this.
var TrackableRest = declare([Rest, SimpleQuery, Trackable]);
var store = new TrackableRest({target: '/api/users', useRangeHeaders: true, idProperty: 'username'});
aspect.after(store, "fetch", function (deferred) {
return deferred.then(function (response) {
response.remove = false;
return json(response);
})
});
var grid = new (declare([OnDemandGrid, Selection, Editor]))({
collection: store,
className: "dgrid-autoheight",
columns: {
username: {
label: core.username
},
email: {
label: core.email
},
enabled: {
label: core.enabled,
editor: CheckBox,
editOn: "click",
sortable: false,
renderCell: libGrid.renderGridCheckbox
},
locked: {
label: core.locked,
editor: CheckBox,
editOn: "click",
sortable: false,
renderCell: libGrid.renderGridCheckbox
},
remove: {
editor: CheckBox,
editorArgs: {"checked": false},
editOn: "click",
label: core.remove,
sortable: false,
className: "remove-cb",
renderHeaderCell: function (node) {
var inp = domConstruct.create("input", {id: "cb-all", type: "checkbox"});
return inp;
},
renderCell: libGrid.renderGridCheckbox
}
},
selectionMode: "none"
}, 'grid');
In addition, I don't want to send the remove column to the server.
My final implementation was to code the remove column like so:
remove: {
editor: CheckBox,
label: core.remove,
sortable: false,
className: "remove-cb",
renderHeaderCell: function (node) {
var inp = domConstruct.create("input", {id: "cb-all", type: "checkbox"});
return inp;
}
}
The code to perform the removes is as follows:
var removeBtn = new Button({
label: core.remove
}, 'user-remove-btn');
removeBtn.startup();
removeBtn.on("click", function (event) {
var markedForDeletion = query(".dgrid-row .remove-cb input:checked", "user-grid");
if( markedForDeletion.length > 0 ) {
lib.confirmAction(core.areyousure, function () {
markedForDeletion.forEach(function (node) {
var row = grid.row(node);
store.remove(row.data.username);
});
});
}
});
Thus the remove column became a client-side only control that was handled by the grid and the event handler.

Resources