Bitmask to boolean conversion in the model - checkbox

I have a model that receives int bitmasks from the backend:
{"user": 7, "group":5, "other":1}
and I now want to show a form with checkboxes like this:
user: [X] read [X] write [X] execute
group: [X] read [ ] write [X] execute
other: [ ] read [ ] write [X] execute
where user can toggle on or off and then the updated bitmask is sent back to the server in a store.sync operation.
I know how to make and align the checkboxes, but ExtJS checkboxes in a form bind to boolean values through a correlation between the checkbox name and the model field name, and not to parts of bitmask.
So I have to convert back and forth between the bitmask int and a bunch of booleans. How would I implement that in a reusable manner?

I think the checkbox group component is a good candidate to render your checkboxes and also to implement the conversion logic.
Here is a reusable component to do the two-way conversion of bitmasks:
Ext.define('Fiddle.Bitmask', {
extend: 'Ext.form.CheckboxGroup',
xtype: 'fiddlebitmask',
isFormField: true,
columns: 3,
items: [{
boxLabel: 'Read',
name: 'read',
inputValue: 1,
excludeForm: true,
uncheckedValue: 0
}, {
boxLabel: 'Write',
name: 'write',
inputValue: 1,
excludeForm: true,
uncheckedValue: 0
}, {
boxLabel: 'Execute',
name: 'exec',
inputValue: 1,
excludeForm: true,
uncheckedValue: 0
}],
getModelData: function () {
let obj = {};
obj[this.name] = this.getValue();
return obj;
},
setValue: function (value) {
if (value) {
var binary = Ext.String.leftPad((value).toString(2), 3, '0');
value = {
read: Number(binary[0]),
write: Number(binary[1]),
exec: Number(binary[2])
};
}
this.callParent([value]);
},
getValue: function () {
var value = this.callParent();
var binary = `${value['read']||0}${value['write']||0}${value['exec']||0}`
return parseInt(binary, 2);
}
});
And the working fiddle: https://fiddle.sencha.com/#view/editor&fiddle/2clg
edit Component completed with getModelData implementation to support usage with form.getValues/form.updateRecord.

What version of Ext are you using? If your version supports ViewModels then I would do the conversion in the ViewModel and bind it to the view.
There is the convert and calculate config on fields as well but they are converting in one way only.

Related

How to set length of a combo box to only allow one of two values

I have the combobox minLength set to 5. I want the combobox to require the length to be 5. If it is longer than 5, I want the minLength to be set to 10. So the outcome should be: the min length can be either 5 or 10, nothing else. (The ComboBox will not accept an input with a length of less than 5 or greater than 10, it also will not accept a length equal to 6, 7, 8, or 9).
Here is my code for example:
xtype: 'combobox',
minLength: 5,
maxLength: 10,
maskRe: /[0-9.-]/,
validator: function(v) {
//this only allows numbers and a hyphen to be entered into the combo box,
//while also requiring 4 digits to be entered after the hyphen
//(only if a hyphen is present)
return /^[0-9]*(-?[0-9]{4})?$/.test(v)? true : 'A Postal Code should only include a hyphen if there are four digits after the hyphen!';
},
As far as I know there is no built-in solution for that, but you can do it. Check the code below and this fiddle, this is for ExtJS 7.5 Classic Material, but it can likely be adopted to other version / toolkit.
Some notes:
You have to set a store, even if it's empty, otherwise the change listener will not be called. (I don't know if it is a bug or a feature.)
The solution uses a ViewModel and binds the minLength to this. The reason you need setMinLength and setMaxLength is that ExtJS doesn't provide it and getter / setter is required for binding.
Important: since this is not an 'official' solution there is no guarantee it will always work.
Ext.application({
name: 'MyApp',
launch: function () {
Ext.create('Ext.form.Panel', {
renderTo: Ext.getBody(),
items: [{
xtype: 'combo',
minLength: 5,
maxLength: 10,
maskRe: /[0-9.-]/,
viewModel: {
data: {
minLength: 5
}
},
store: [],
bind: {
minLength: '{minLength}'
},
setMinLength: function (v) {
this.minLength = v;
},
getMinLength: function () {
return this.minLength;
},
validator: function (v) {
return /^[0-9]*(-?[0-9]{4})?$/.test(v) ? true : 'A Postal Code should only include a hyphen if there are four digits after the hyphen!';
},
listeners: {
change: function (combo, newValue, oldValue, eOpts) {
const newMinLength = newValue == null ? 5 : newValue.length >= 5 ? 10 : 5;
console.log('New min length will be: ' + newMinLength);
combo.getViewModel().set('minLength', newMinLength);
}
}
}]
});
}
});

Ionic 2 / 3: Number Input from Alert

I'm using Ionic 3.x on macOS.
I have the following issue:
I have an array containing a number and an array of names.
table: { number: number, names: string[] } = {
number: 0,
names: ['']
};
I want to set the number of the array using an input for the user. I stumbled upon the AlertController.
I have written the following function thing to add a number:
addTable(){
let prompt = this.alertCtrl.create({
title: 'Add Table',
subTitle: 'Enter the table number',
inputs: [{
name: 'tableNumber',
placeholder: 'Number',
type: 'number'
}],
buttons: [
{
text: 'Cancel'
},
{
text: 'Add',
handler: data => {
//this.tables.push(data);
this.table.number = data;
}
}
]
});
prompt.present();
}
But this always sets table.number to object [object]. If I write it as this.table.number = +data; it has the value NaN. The push version also doesn't work.
How do I set table.number to a number that the user put in?
The name of the input
name: 'tableNumber'
gets added as a property name to the resulting object. You can access it like this:
handler: data => {
this.table.number = data.tableNumber;
}

Using angular-translate with formly in a select form

I'm trying to use angular-translate to translate the data from a select input on a form made formly, but I can't make it work properly.
Basically, when using angular-translate for other types of input, I would have to do something like
'templateOptions.label': '"NAME" | translate',
'templateOptions.placeholder': '"NAME" | translate'
following this pattern, I've tried to put my data like this:
"templateOptions.options": [
{
"name": "{{ 'OPT1' | translate }}",
"id": 1
},
{
"name": "{{ 'OPT2' | translate }}",
"id": 2
}
]
But that gives me nothing on the dropdown menu. Can someone give me any directions here?
The complete code can be found on the link http://output.jsbin.com/horawaqaki/
Thanks for the help!
In this case you can use $translate service inside your controller. This service can return you translated values, but it is asynchronous operation. Because of this you should add some flag in your controller in order to show form only when you receive this translations (in this example I'm going to use vm.areFieldGenerated and then show/hide form and element with ng-if).
So, basically you should pass an array of localization keys and $translate service will return you the following object:
{
'NAME': 'Name',
'OPT1': 'Working!',
'OPT2': 'Working indeed!'
}
And after that you are able to use this value to localize your field title or options.
Your function for generating fields and assigned translated values to the options will look like this:
// variable assignment
vm.env = getEnv();
vm.model = {};
vm.options = {formState: {}};
vm.areFieldsGenerated = false;
generateFields();
// function definition
function generateFields() {
$translate(['NAME', 'OPT1', 'OPT2']).then(function(translationData) {
vm.fields = [
{
key: 'item',
type: 'select',
templateOptions: {
valueProp: 'id',
labelProp: 'name',
options: [
{name:'Not working!', id: 1},
{name:'Not working indeed!', id: 2}
]
},
expressionProperties: {
'templateOptions.label': transationData['NAME'],
'templateOptions.options': [
{
name: translationData['OPT1'],
id:1
},
{
name: translationData['OPT2'],
id:2
}
]
}
}
];
vm.originalFields = angular.copy(vm.fields);
vm.areFieldsGenerated = true;
});
}
I've created working example here.
More examples with $translate on angular-translate.github.io.

Rally SDK 2 custom sorters

How do I get a more complex sort on a query, I have this query currently:
var store = Ext.create('Rally.data.custom.Store',{
data: changes,
limit: 'Infinity',
pageSize: 5000,
sorters: [
{
property: 'ReleaseScope',
direction: 'ASC'
},
{
property: 'ScheduleState',
direction: 'DESC'
}
]
});
Because the ScheduleState is hydrated I can't sort by the normal numerics, can I define the order using some kind of matcher?
i.e. say I want to show in order [Accepted, Completed, In-Progress, Defined, Backlog]
And, if I wanted to complicate this further and show stories with story points first, something like
All stories with a story point value != 0
Sorted by schedulestate [accepted, completed, in-progress, defined etc..]
stories with no story point value
some other sort here possibly
You can pass a sorterFn rather than a property/direction combo to implement custom sort logic:
sorters: [
{
sorterFn: function(a, b) {
var scheduleStates = ['Accepted', 'Completed', 'In-Progress', 'Defined'],
aState = a.get('ScheduleState'),
aIndex = _.indexOf(scheduleStates, aState),
bState = b.get('ScheduleState'),
bIndex = _.indexOf(scheduleStates, bState);
return a - b;
}
}
]
The above function should sort them based on schedule state descending I think.

Sencha Touch 2 Search List

am having issue designing and getting a search field to work, i don't know how to get this working, i can see any documentation or sample code on Sencha Touch 2. any help will be appreciated. this my current stage:
`Ext.define('ikhlas.view.SearchProfile', {
extend: 'Ext.Panel',
xtype: 'searchpanel',
config:{
title: 'Search',
iconCls: 'search',
scrollable: true,
styleHtmlContent: true,
items: [
{
xtype: 'fieldset',
title:'Search Profile',
iconCls:'add',
items: [
{
xtype: 'searchfield',
name:'searchfield',
placeHolder:'Search',
},
]
},
]
}
});`
And my controller looks like this (Noting has been done, i don't know how to start help pls):
Ext.define('ikhlas.controller.SearchField',{
extend: 'Ext.app.Controller',
config:{
refs:{
submitpanel:'loginpanel'
},
control:{
}
},
});
And here are the list of Data's i want to Auto search:
data: [
{firstName: 'Tommy', lastName: 'Maintz'},
{firstName: 'Rob', lastName: 'Dougan'},
{firstName: 'Ed', lastName: 'Spencer'},
{firstName: 'Jamie', lastName: 'Avins'},
{firstName: 'Aaron', lastName: 'Conran'},
{firstName: 'Dave', lastName: 'Kaneda'},
{firstName: 'Michael', lastName: 'Mullany'}
i want the search field to work in such a way that as the user is typing in character, it will auto pop suggestion just like: http://docs.sencha.com/touch/2-0/#!/example/search-list
In your controller you should listen for two events, clearicontap and keyup for the searchfield.
...
control: {
'searchfield': {
keyup: 'onSearchQueryChanged',
clearicontap: 'onSearchReset'
}
},
onSearchQueryChanged: function(field) {
// as in sample
//get the store and the value of the field
var value = field.getValue(),
store = this.getStore(); //you should actually point to the real store
//first clear any current filters on thes tore
store.clearFilter();
//check if a value is set first, as if it isnt we dont have to do anything
if (value) {
//the user could have entered spaces, so we must split them so we can loop through them all
var searches = value.split(' '),
regexps = [],
i;
//loop them all
for (i = 0; i < searches.length; i++) {
//if it is nothing, continue
if (!searches[i]) continue;
//if found, create a new regular expression which is case insenstive
regexps.push(new RegExp(searches[i], 'i'));
}
//now filter the store by passing a method
//the passed method will be called for each record in the store
store.filter(function(record) {
var matched = [];
//loop through each of the regular expressions
for (i = 0; i < regexps.length; i++) {
var search = regexps[i],
didMatch = record.get('firstName').match(search) || record.get('lastName').match(search);
//if it matched the first or last name, push it into the matches array
matched.push(didMatch);
}
//if nothing was found, return false (dont so in the store)
if (regexps.length > 1 && matched.indexOf(false) != -1) {
return false;
} else {
//else true true (show in the store)
return matched[0];
}
});
}
},
onSearchReset: function(field) {
this.getStore().clearFilter();
}
...
This example will emulate the same behavior as in ST2 SDK, that is filtering a store of Ext.List. Naturally, you will probably end up implementing your own logic for filtering.
Note that searchfield is nothing more but a styled textfield, usually with clear button to the right (depends on browser/os), as defined in HTML5.

Resources