Bind constraints in Input control of SAPUI5 - data-binding

My rest service expose me a group of fields: each filed has a value and a list of attributes: enabled, maxLength (in case of string), minLength (in case of string), decimals (number of decimal digits - in case of float).
In OpenUi5 I have:
value and enabled are properties of Input control Link (Good!! I can bind properties with model contains the attributes)
maxLength and decimals are optionsof String type and Float type (Link) but I can't bind options with a model :-/
minLength I can't find a property/option
I would like map (bind) each attribute with component so that automatically the library control for me without writing more code.

there is a property called maxLength for Input Control.
So the only problem I see is binding minLength and decimals for which there is little bit effort is needed.
Solution
Create your own input control by extending the existing Input
Control.How to achieve it?
Sample Code Structure:
jQuery.sap.require("sap.m.Input");
jQuery.sap.declare("sap.m.ComplexInput");
sap.m.Input.extend("sap.m.ComplexInput", {
metadata: {
properties: {
minLength: {
type: "int"
},
decimals: {
type: "int"
},
events: {
//define your own events like checkMinLength,checkDecimals
}
},
onInit: function () {
//on init do something
},
onAfterRendering: function () {
//called after instance has been rendered (it's in the DOM)
},
_somePrivateMethod: function () {
/*do someting...*/
},
somePublicMethod: function () {
/*do someting...*/
},
}
});
sap.m.ComplexInput.prototype.exit = function () {
/* release resources that are not released by the SAPUI5 framework */
//do something
};
Adding CustomData and using wherever you want to.
Then you can access custom data in validation process or on liveChange or so..
Bind the other properties to the value of customData
var input = new sap.m.Input({
value: '{value}',
enabled: '{enabled}',
maxLength: '{maxLength}',
customData: [
new sap.ui.core.CustomData({
key: 'minLength',
value: '{minLength}'
}),
new sap.ui.core.CustomData({
key: 'decimals ',
value: '{decimals}'
})
],
change: function(oEvent) {
var src = oEvent.getSource();
var minLen = src.getCustomData()[0].getValue();
var decimals = src.getCustomData()[1].getValue();
if (src.getValue() && src.getValue().length > minLen) {
src.setValueState('Success');
} else {
src.setValueState('Error');
}
}
});

Related

Extjs beforerender to set async-obtained variable

My ExtJS application displays certain UI elements depending on a boolean variable.
This boolean variable, however, is the result of calling an async function. As a result, the boolean is set to a Promise that is fulfilled, rather than true or false proper. This affects whether the UI elements are actually displayed (a Promise is not exactly a boolean, after all).
The code looks like this:
Ext.define('userDefinedComponent', {
extend: 'Ext.Container',
requires: ['someHelperFile'],
initComponent: function () {
var me = this,
var enabled = someHelperFile.someAsyncFunc() // enabled is a boolean that is returned as fulfilled Promise instead
Ext.apply(me, {
// layout and padding
items: [
{
xtype: 'internallyDefinedForm',
fieldConfigs: {
// other fields
'someFormField': {
hidden: !enabled, // depends on enabled
}
}
},
{
xtype: 'internallyDefinedGrid',
columnConfigs: {
// other columns
'someColumn': {
hidden: !enabled, // deends on enabled
}
},
}
]
})
}
})
I want the field enabled to really be a boolean rather than a Promise. In other words, I want to wait for the someAsyncFunc to run the result, before setting hidden property of the internallyDefinedForm and internallyDefinedGrid.
What are my possibilities? I was thinking of using a beforerender, like below:
Ext.define('userDefinedComponent', {
extend: 'Ext.Container',
requires: ['someHelperFile'],
initComponent: function () {
var me = this;
Ext.apply(me, {
// layout and padding
items: [
{
xtype: 'internallyDefinedForm',
fieldConfigs: {
// other fields
'someFormField': {
hidden: !me.enabled, // depends on enabled
}
}
},
{
xtype: 'internallyDefinedGrid',
columnConfigs: {
// other columns
'someColumn': {
hidden: !me.enabled, // deends on enabled
}
},
listeners: {
beforerender: function() { // this is the beforerender
me.enabled = someHelperFile.someAsyncFunc();
console.log("beforerender triggered in grid");
}
},
}
]
})
}
})
And in fact, using the beforerender for the internallyDefinedGrid only, I can see the text "beforerender triggered in grid" triggered very early. However, the fact remains that the behavior that I observe does not correspond to what I expect: although the async someAsyncFunc should return true based on the API response it gets, such that me.enabled is true, the actual UI associated with the internallyDefinedGrid behaves as if me.enabled is false instead. I observe that the column on the UI is hidden, and this is only possible when me.enabled is false, such that the column someColumn does not appear on the grid. After all, the hidden field of someColumn is set to !enabled.
I am confident that the UI for the grid behaves not like what I expect it to, so there is a problem with the async behavior. But I'm really lost as to how to set the asynchronously obtained enabled or me.enabled field adequately.
Any help is appreciated.
I would suggest to use a View Model and binding, as explained here.
Basically you define what your UI is depending on, under the data tag in the View Model (you can set the initial value here):
Ext.define('MyApp.TestViewModel', {
extend: 'Ext.app.ViewModel',
data: {
something: false,
},
}
Then you bind the visibility to this value in the view:
bind: {
hidden: '{something}'
}
or
bind: {
hidden: '{!something}'
}
You fetch the async data, and once you have the result, set the value in the View Model (this can be either the view or the controller):
this.getViewModel().set('something', RESULT_OF_ASYNC)
With binding ExtJS takes care of refreshing the visibility of your component every time when the value in the View Model is changed. There are good examples at the link I provided. This is a very powerful and complex feature of ExtJS, worth learning.

Displaying name and selecting id on select for Twitter Typeahead

I am trying to implement twitter typeahead into my project, having remote as the source. I am able to make the connection between the front end query text and the sql. The return response looks like this:
[
{
id: 1,
name: 'user one'
},
{
id: 2,
name: 'user two'
}
..
]
The typeahead displays the matching items but it includes the id along with the names in the selection, instead of just the name. Second, I want to get the id value on select but the :select always gives the name value instead of the id.
here is my code:
var source = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url:"{{ path('user_typeahead') }}"+'?string=%QUERY', // twig path
wildcard: '%QUERY',
filter: function (results) {
// Map the remote source JSON array to a JavaScript object array
return $.map(results, function (result) {
return {
value: result
};
});
}
}
});
// Initialize the Bloodhound suggestion engine
source.initialize();
$('#typeahead').typeahead(null, {
display: 'value',
source: source.ttAdapter(),
limit:5,
highlight: true,
hint: true
});
$('#typeahead').bind('typeahead:select', function(ev, suggestion) {
console.log(suggestion);
});
Try this,
Display should be set to the property of the json object. display: 'id'
Chain the custom event. Access your id from suggestion.id.
$('#typeahead').typeahead(null, {
display: 'id',
source: source.ttAdapter(),
limit:5,
highlight: true,
hint: true
}).bind('typeahead:select', function(ev, suggestion) {
console.log(suggestion);
});

Issue Related to Autocomplete Textbox in knockout js

I have webapplication with knockoutjs
i have to implement Autocomplete textbox in a webform,
I got following tutorial
https://www.npmjs.com/package/knockout.autocomplete
i have added following code:
var viewModel = {
keywords: ko.observableArray([
'abstract', 'add', 'alias', 'as', 'ascending',
'async', 'await', 'base', 'bool', 'break'
])
};
ko.applyBindings(viewModel);
<input data-bind="autocomplete: { data: keywords, maxItems: 6 }" value=""/>
it works fine, but it binds text only, and i want to bind Text and Value Pair.
how can i achieve in above code?
Thanks
I haven't tested this, but it looks like it allows you to pass a formatter, so you can set up how it will be displayed to the user.
var viewModel = {
keywords: ko.observableArray([
{ name: 'Abstract', value: 'abstract' },
{ name: 'Break', value: 'break' }
]),
format: function (value) {
// This is what is shown in the list, only pass what you want
return value.name;
},
selected: ko.observable(),
onSelect: function (value) {
// value should be your name/value object
viewModel.selected(value);
}
};
<input data-bind="autocomplete: {
data: keywords,
maxItems: 6,
format: format,
onSelect: onSelect
}" value=""/>

How do you add values to AutoForm's form object without using a form?

I've got a form created through AutoForm.
As far as data sources, I can fill in parts of the form and use:
AutoForm.getFormValues('form-id').insertDoc // returns the contents of the form
When I validate the form I can do:
var formValues = AutoForm.getFormValues('form-id').insertDoc;
var isValid = MyCollection.simpleSchema().namedContext("myContext").validate(formValues);
// if isValid returns true, then I enable the Submit button
Instead of filling in parts of the form, I want to manually add information into whatever object Autoform uses for validation and submission to a collection.
For example, there are data fields in the schema that simply don't need to appear in the form itself.
Take a shopping cart:
ShoppingCartSchema = {
totalPrice: {
type: Number,
optional: false
},
itemsSelected: {
type: [Object],
optional: false
}
};
The data for itemsSelected is obviously provided through user input on the form.
The data for totalPrice is something that should not be from a form input. It's generated in the code.
But totalPrice still needs to be validated as a required field before AutoForm submits the form to a collection.
So how do you add totalPrice onto the object that Autoform eventually validates?
You could use an autovalue if you wanted to.
ShoppingCartSchema = new SimpleSchema({
'items': {
type: [Object],
},
'items.$.name': {
type: String
},
'items.$.price': {
type: Number
},
totalPrice: {
type: Number,
autoValue: function () {
if (this.field('items').isSet) {
let total = this.field('items').value.reduce(function (sum, item) {
return sum + item.price;
}, 0);
if (this.isInsert) {
return total;
} else {
return { $set: total };
}
}
}
},
});
Autoform Hooks can help you manipulate the data before you save it into the Collection.
In your case .
AutoForm.hooks({
form-id: { // The Autoform ID
onSubmit: function (insertDoc, updateDoc, currentDoc) {
if (customHandler(insertDoc)) { // Your Logic here
this.done(); // This is Required
} else {
this.done(new Error("Submission failed"));
}
return false;
}
}
});
For More Information Please refer Autoform Readme

Grid content JSON conversion

I have a grid where user and add new rows as many as they want. After adding all the rows, they click the "Save" button. On Save button click, I want to send all the data entered by the user in JSON format to the server side code (i.e. a servlet in my case)
Below is the model and store definition:
Ext.define('Plant', {
extend: 'Ext.data.Model',
fields: [
// the 'name' below matches the tag name to read, except 'availDate'
// which is mapped to the tag 'availability'
{name: 'common', type: 'string'},
{name: 'botanical', type: 'string'},
{name: 'light'},
{name: 'price', type: 'float'},
// dates can be automatically converted by specifying dateFormat
{name: 'availDate', mapping: 'availability', type: 'date', dateFormat: 'm/d/Y'},
{name: 'indoor', type: 'bool'}
]
});
// create the Data Store
var store = Ext.create('Ext.data.Store', {
// destroy the store if the grid is destroyed
autoDestroy: true,
model: 'Plant'
});
On click of the save button, I am able to get the store like this:
{
text: 'Save',
handler : function(){
//Getting the store
var records = grid.getStore();
console.log(records.getCount());
Ext.Ajax.request({
url: '/CellEditing/CellEditingGridServlet',
method: 'POST',
jsonData: {
//How to assign the store here such that
//it is send in a JSON format to the server?
},
callback: function (options, success, response) {
}
});
}
But I don't know like how to convert the store content into JSON and send it in the jsonData of the ajax request.
I want the JSON data something like this in the server side:
{"plantDetails":
[
{
"common": Plant1,
"light": 'shady',
"price": 25.00,
"availDate": '05/05/2013',
"indoor": 'Yes'
},
{
"common": Plant2,
"light": 'most shady',
"price": 15.00,
"availDate": '12/09/2012',
"indoor": 'No'
},
]
}
Please let me know how to achieve this.
Regards,
Agreed with Neil, the right way to do this is through an editable store outfited with a proxy and a writer. See example here: http://docs.sencha.com/ext-js/4-1/#!/example/grid/cell-editing.html
Store
writer :
{
type : 'json',
allowSingle : true
}
Experiment with allowSingle as per your use case
In your controller
//if you want to set extra params
yourStore.getProxy().setExtraParam("anyParam",anyValue);
// sync the store
yourStore.sync({success : function() {
yourGrid.setLoading(false);
.. },
scope : this // within the scope of the controller
});
You should be creating the model with a new id ( you can ignore it at the server side and use your own key generation , but it lets extjs4 for its internal purposes know that a new record has been created).
creating a model instance
var r = Ext.create('yourModel', { id: idGen++, propA : valA , ... });
insert to grid
store.insert(0,r);
var editPlugin = grid.getPlugin(editPluginId);
editPlugin.startEdit(0,1);
Once you receive a response back the id's can be update to their true value.
in the Store
reader :
{
type : 'json',
root : 'yourdata',
successProperty : 'success',
idProperty : 'id'
}
If you were to use the same grid for handling and editing then you could use the write event or the appropriate event
for more advanced handling in the Store
listeners :
{
write : function(store,operation, eOpts)
{
var insertedData = Ext.decode(operation.response.responseText);
.. do something
}
}
I would recommend using the mvc architecture of Extjs4
This is what I tried and it seems to work:
var store = Ext.create('Ext.data.Store', {
// destroy the store if the grid is destroyed
autoDestroy: true,
model: 'Plant',
proxy: {
type: 'ajax',
url: '/CellEditing/CellEditingGridServlet',
writer: {
type: 'json',
root: 'plantDetails'
}
}
handler : function(){
grid.getStore().sync();
But I am getting an additional parameter in the JSON at the server side:
"id": null
I don't have this id set in my model then where is this coming from? Is there some way to set some values to it rather than having a default null value?

Resources