OpenUI5 binding property with a function, instead of direct access - javascript

I would like to bind a property (flag_baz in this case) from a JSONModel to a checkbox.
Thing is that the json model looks like this.
{
foo: "Foo",
bar:"Bar",
flag_baz : "X"
}
in this case X means "true" and an empty string means "false"
What i would like to do is evaluate a function for binding from model to the checkbox (that would translate "X"/"" to true/false) and evaluate some other function when binding from the checkbox to the model (that would translate from true/false back to "X"/"").
i would like to have something like this:
var checkBox = new Checkbox();
checkBox.bindProperty("checked", "flag_baz", funcFromStringToBool, funcFromBoolToString);
i know the funcFromStringToBool is called a formatter.
how would i add the funcFromBoolToString function?
Hope this makes sense.
Thx in advance.

Well in case some cares i've found the answer on my own.
All bindings can use a type like so
checkBox.bindProperty("checked", {
path : "flag_baz",
type : new BooleanStringType()
});
the BooleanStringType class would look like this:
sap.ui.model.SimpleType.extend("BooleanStringType", {
//called when going from model to ui
formatValue : function(flag_baz){
return flag_baz === "X";
},
//called when going from ui back to the model
parseValue : function(flag_baz){
return flag_baz ? "X" : "";
},
validateValue : function(flag_baz){
//some validation if needed
}
});

Related

Vue-Form-Generator schema is not reactive to computed properties

I have computed property in my data this.coinPairingOptions that needs to render its radio buttons based on some of the other fields in this schema. I have reduced the amount of code in the file to save space.
data: function () {
return {
schema: {
{model: "symbolPair", type: "radios", label: "Pair with", values:
this.coinPairingOptions, required: true}
},
computed: {
coinPairingOptions() {
console.log("computing coinPairingOptions")
let coin = this.model.symbol.toUpperCase();
let options = [];
if (this.model.exchange === 'Coinbase') {
options = this.getCoinbasePairs
} else if (this.model.exchange === 'Binance') {
options = this.getBinancePairs
} else {
}
console.log(options.get(coin));
return options.get(coin);
},
}
In the dev tools I can see the computed property changing to the correct values however it is not changing in the data. Apparently, this is appropriate behavior, but what is a way around this? I have tried putting {{this.coinPairingOptions}} in the html and it errors because it's a computed property with not value initially.
Any help would be appreciated!
You can't use computed property in data, because data evaluates before the computed properties did.
You can use a watcher to achieve the intended result. Have a look at the documentation, you can add the argument immediate to trigger the callback immediately with the current value of the expression.
Computed properties are already accessible in the template by using {{}}. You don't need to put a this in front of the computed.

How to extract function on a `v-for` table

Noob here. I am trying to build a dropdown for only the div id matches my specific name.
For example, my table column names are : A, B, C.
I only want to enable dropdown for column A
My table is a template that looks like follows:
template(v-for="field in tableFields")
th(:id="'_' + field.name")
select(v-if="field.name ==='A'" v-model="selectedScope"
option
option(v-for="scope in scopes" v-bind:value="scope" ) {{scope}}
This works but i want to extract the v-if="field.name ==='A'" to a function.
I have the following but it didn't work:
template(v-for="field in tableFields")
th(:id="'_' + field.name")
select(v-if="shouldProvideSelectOption(field)" v-model="selectedScope"
option
option(v-for="scope in scopes" v-bind:value="scope" ) {{scope}}
And under computed, i have something like this:
computed: {
shouldProvideSelectOption: function (field) {
return field.name === 'A'
}
},
Why?
use a method instead of computed property :
methods : {
shouldProvideSelectOption: function (field) {
return field.name === 'A'
}
}
Edit
Like #RoyJ said in the comment below :
computeds do not take arguments (except for setters). A computed is used like a variable

Axios - how can I conditionally append fields

I have this Axios patch but I only want to include the last three, if the hyperlink_column isn't null. How can I do this?
Thanks,
Mick
axios.patch('/' + self.table + '/' + self.rowId,{
name: self.name,
SQL:self.SQL,
area_id:self.area_id,
report_type_id:self.report_type_id,
mode:self.mode,
database_connection_id:self.database_connection_id,
pagination: self.pagination,
show_export_buttons : self.show_export_buttons,
filters : self.filters,
description : self.description,
category_id : self.category_id,
hyperlink_column : self.hyperlink_column,
linked_report : self.linked_report,
link_parameter : self.link_parameter,
Since by the look of it, it looks like the second parameter is an object, so one way of doing it, would be to invoke a function as the second parameter and passing your data inside self as an argument. The function will then check if self.hyperlink_column exists and then return an object accordingly. like this
Repl
PS : This is done using closure.

Aurelia: always call method in the view (problems after upgrade)

We've upgraded Aurelia (in particular aurelia-framework to 1.0.6, aurelia-bindong to 1.0.3) and now we're facing some binding issues.
There's a list of elements with computed classes, and we had a method int the custom element that contained the list:
getClass(t) {
return '...' +
(this.selected.indexOf(t) !== -1
? 'disabled-option' :
: ''
) + (t === this.currentTag
? 'selected-option'
: ''
);
}
And class.one-way="$parent.getClass(t)" for the list element, everything was OK.
After the upgrade it simply stopped to work, so whenever the selected (btw it's bindable) or currentTag properties were modified, the getClass method just wasn't called.
I partially solved this by moving this logic to the view:
class="${$parent.getClass(t) + (selected.indexOf(t) !== -1 ? 'disabled-option' : '') (t === $parent.currentTag ? 'selected-option' : '')}"
I know that looks, well... bad, but that made t === $parent.currentTag work, but the disabled-option class still isn't applied.
So, the question is:
How do I force Aurelia to call methods in attributes in the view?
P.S.
I understand that it might cause some performance issues.
Small note:
I can not simply add a selected attribute to the list element since I don't to somehow modify the data that comes to the custom element and I basically want my code to work properly without making too many changes.
UPD
I ended up with this awesome solution by Fabio Luz with this small edit:
UPD Here's a way to interpret this awesome solution by Fabio Luz.
export class SelectorObjectClass {
constructor(el, tagger){
Object.assign(this, el);
this.tagger = tagger;
}
get cssClass(){
//magic here
}
}
and
this.shown = this.shown(e => new SelectorObjectClass(e, this));
But I ended up with this (defining an extra array).
You have to use a property instead of a function. Like this:
//pay attention at the "get" before function name
get getClass() {
//do your magic here
return 'a b c d e';
}
HTML:
<div class.bind="getClass"></div>
EDIT
I know that it might be an overkill, but it is the nicest solution I found so far:
Create a class for your objects:
export class MyClass {
constructor(id, value) {
this.id = id;
this.value = value;
}
get getClass() {
//do your magic here
return 'your css classes';
}
}
Use the above class to create the objects of the array:
let shown = [];
shown[1] = new MyClass('someId', 'someValue');
shown[2] = new MyClass('someId', 'someValue');
Now, you will be able to use getClass property:
<div repeat.for="t of shown" class.bind="t.getClass">...</div>
Hope it helps!
It looks pretty sad.
I miss understand your point for computing class in html. Try that code, it should help you.
computedClass(item){
return `
${this.getClass(item)}
${~selected.indexOf(item) ? 'disabled-option': ''}
${item === this.currentTag ? 'selected-option' : ''}
`;
}
Your code not working cause you miss else option at first if state :/
Update:
To toggle attribute state try selected.bind="true/false"
Good luck,
Egor
A great solution was offered by Fabio but it caused issues (the data that was two-way bound to the custom element (result of the selection) wasn't of the same type as the input and so on). This definitely can be fixed but it would take a significant amount of time and result in rewriting tests, etc. Alternatively, yeah, we could put the original object as some property blah-blah-blah...
Anyway:
There's another solution, less elegant but much faster to implement.
Let's declare an extra array
#bindable shownProperties = [];
Inject ObserverLocator
Observe the selected array
this.obsLoc.getArrayObserver(this.selected)
.subscribe(() => this.selectedArrayChanged);
Update the shownProperties
isSelected(t) {
return this.selected.indexOf(t) !== -1;
}
selectedArrayChanged(){
for(var i = 0; i < this.shown.length; i++){
this.shownProperties[i] = {
selected: this.isSelected(this.shown[i])
}
}
}
And, finally, in the view:
class="... ${shownProperties[$index].selected ? 'disabled-option' : '')} ..."
So, the moral of the story:
Don't use methods in the view like I did :)

How to assign value to ng-model if it is empty

I want to assign value to ng-model if it is empty. I have assign default value to ng-model input from controller but when user makes change to remove that value and makes input empty then I want to assign default value to ng-model.
For example.
Controller:
App.controller('mainController',['$scope', function($scope) {
$scope.assignOne= 16;
}]);
View:
<input ng-model="assignOne" ng-change="changeMe()"/>
Now, input value becomes 16.
If user make changes and manually removes this value 16 then I want default value 1 instead of empty string in changeMe() function.
Note:
I can check for empty ng-model and override value of ng-model in controller. But, Is there any way better way to use, something which angular provides.
I am just new to AngularJS.
Try $watch as follows
$scope.$watch(
"assignOne",
function handleChange( newValue, oldValue ) {
console.log( "assignOne:", newValue );
if(newValue == '') {
newValue = 16;
}
}
);
another solution to meet your sample:
App.controller('mainController',['$scope', function($scope) {
$scope.assignOne= 16;
// function called by " ... ng-change="changeMe() "
$scope.changeMe = function() {
if ($scope.assignOne == '') {
$scope.assignOne = 1;
}
}
}]);
PS: for the previous answer, there is a little miss: it doesn't change the "assignOne" var, but only a local copy, which is lost at the end of the function call... Correct it by changing $scope.assignOne !
PS 2: the 2 solutions are good, but can resolve different requirements :
$watch is like a trigger on the model object, and is the most efficient if you want check business rules on the navigator side
ng-change is more interesting for handling some interactions, which are not stricts rules...
The 2 ways could be used...

Categories