I have two components: A panel and a custom text field.
The panel has a viewmodel and I want to bind a value (called testData) from that viewmodel to a property (called test) of the custom text field.
That works fine ...basically.
But when the test property of the text field is changed, the testData in the viewmodel of the panel does not update accordingly. I mean when the test property of the child element (the textfield) is modified, the testData property of the panel's viewmodel should contain the same value that is in test, just like a normal two-way bind.
I'm not sure what I'm doing wrong, but here is what I've tried to far:
https://fiddle.sencha.com/#fiddle/20pu&view/editor
Ext.define('MyMain', {
extend: 'Ext.panel.Panel',
alias: 'widget.main',
width: '100%',
bodyPadding: 10,
viewModel: {
data: {
testData: 'Example Data'
}
},
bind: {
title: '{testData}'
},
items: {
xtype: 'myField',
bind: {
test: '{testData}'
}
}
})
Ext.define('MyField', {
extend: 'Ext.form.field.Text',
alias: 'widget.myField',
fieldLabel: 'Data',
width: '100%',
config: {
test: null // when test is changed, it should also affect the {testData} bind of the main component, causing the title to change
},
setTest(value) {
this.test = value + ' modified!' // because of the bind, this /should/ automatically get appied to the viewmodel's `testData` and thus to the panel title
this.setValue(this.test) // whenever the `test` property is changed, we write the contents to the value of the text field (just to visualize the `test` property).
// But as you can see, the panel title will still just say `Example Data` and not `Example Data modified!` as it should.
},
getTest(){
return this.test
}
})
Ext.application({
name : 'Fiddle',
launch : function() {
Ext.create('Ext.container.Viewport', {
items: [{
xtype: 'main'
}]
})
}
})
Update: (after reading your comments on other answers)
In general, mentioning the property in the config block and include it in publishes will make any property two-way bindable.
ExtJS will generate the getter and setter methods for it. The setter method takes care of binding. Now, whenever anyone updates the property value (using the setter), the new value will be passed on to the bound viewModel and in turn to the other components.
Accessing the property directly, this.test or this.viewModel.data.testData and assigning values to them will not be reflected in the controls bound to this property.
In case you are providing an implementation for the setter function (setTest) of a published property, ensure that this.callParent(...) gets called from it.
The usage of field's value property to display the contents of test caused the earlier confusion. Here is a fiddle with two-way bindable test property without any special handling in the MyField class.
Click on the 'Get test' button, the value should be 'Example Data' (from viewModel).
'Set testData' button will update the value in the viewModel. Use the 'Get test' button again to verify that the value of test has also been updated.
'Set test' button assigns a new value to the field's test property and this will be reflected in the panel's title.
Have a look at this forked fiddle.
In your implementation, the setTest method is directly changing the value of this.test to value + ' modified!'. This will not update the value of testData in viewModel as binding works via the getter and setter functions implemented of the properties specified in the config.
If you want to change title while changing Textfield then you have to bind value property because changing textfield's value only changes value property of field.
bind: {
test: '{testData}',
value : '{testData}'
},
If you don't want to bind it with value then on change event you have to set value of test property.
listeners : {
change : function(field, newValue, oldValue, eOpts ){
field.setTest(newValue);
}
}
Please refer fiddle.
First of all, you need to make the test config twoWayBindable.
This object holds a map of config properties that will update their
binding as they are modified.
Secondly, you don't need to define getters and setters for the config object, in your case.
Each config item will have its own setter and getter method
automatically generated inside the class prototype during class
creation time, if the class does not have those methods explicitly
defined.
You might to, but it will override the default methods which take care of updating the binding, among other things.
By standardize this common pattern, the default generated setters
provide two extra template methods that you can put your own custom
logic into, i.e: an "applyFoo" and "updateFoo" method for a "foo"
config item, which are executed before and after the value is actually
set, respectively.
The twoWayBindable config relies on the update template method, and when you specify your own setter, the update method will never get called, and the binding won't be updated.
In other words, when leveraging the config feature, you mostly never
need to define setter and getter methods explicitly. Instead, "apply"
and "update" methods should be implemented where necessary.
So, in your example, here are the steps you need to take:
Remove the setTest and getTest method declarations.
Add the twoWayBindable config containing test.
twoWayBindable: ['test']`
Hook up any additional logic into the applyTest or updateTest template methods. For example, updating the field value after the test value gets set.
updateTest(testValue) {
this.setValue(testValue)
}
Here is the working fiddle: https://fiddle.sencha.com/#fiddle/20rs&view/editor
In order to be able to bind custom class properties you need to list these in the twoWayBindable config.
Don't modify the value to be set in the setter and don't call the setter recursively. It is better to write an update<Fieldname>() function. Those are meant to handle updates in your view and they usually don't modify your data structures.
Based on 2.): Override the view update function of the form field to catch changes done to the value.
Here is the complete fiddle:
https://fiddle.sencha.com/#fiddle/218m&view/editor
Some things to note here:
after 3 seconds, the ViewModel testData value is updated
after 6 seconds, the setTest() setter of the field is called
after 9 seconds, the setValue() method from your input field is triggered
at the end, you could change the input field value to change the panel title
This is to illustrate the various scenarios.
How should I set the selected item of a ComboBox component in Sitecore SPEAK UI?
My ComboBox is populated by a QueryDataSource component which is looking at a folder of items in my core DB.
I can retrieve the currently selected value (which is text, not an ID) using the following code:
var value = this.MyComboBoxId.viewModel.selectedItemId();
and I would have expected to be able to set the selected value using:
var value = "SomeValueWhichExistsInTheList";
this.MyComboBoxId.viewModel.selectedItemId(value);
but this doesn't seem to work. The documentation here mentions using
rebind(items, selectedItem, selectedValue, displayFieldName, valueFieldName)
but I don't want to have to re-populate it, just change the selected item. My code is within the initialize method of my model.
Edit
I found that if the ComboBox does not have DisplayFieldName or ValueFieldName values set in the rendering properties you have to set the value to the appropriate itemId. DisplayFieldName and/or ValueFieldName should be set to the name of a field you have created - you cannot bind to the item name.
In the initialize method, use the following code to set the value:
app.yourQueryDataSource.on("change:hasItems", function () {
app.yourComboBox.set("selectedValue", yourValue);
});
Above approach did not work for me and hence i used the
app.<yourcontrolid>.viewModel.rebind()
function as documented in Sitecore SPEAK combobox documentation and that worked.
I'm using a dojo store memory as a datasource for dijit form select. The problem I'm having is that the select control ignores the sort I've set on the data store and instead sorts data on the label field. I'm trying this:
mhusStore = new Memory({ data: data, idProperty: "MHID", sort: [{ attribute: "SegIDOrder", descending: false }] }); //verified the sort is on SegIDOrder in debug mode (it also comes out of the db this way
this.selectUSMAS.set("labelAttr", "MHID");
//this.selectUSMAS.set("sort", "SegIDOrder");//tried this no result
this.selectUSMAS.set("store", mhusStore);
any ideas how I can get the select to use the order of the memory store?
Thanks
already answered here:
How to change order of elements in a dijit.form.Select
this.selectUSMAS.set("sortByLabel", false);
Does Kendo k-ng-model support two-way binding, and if not, what is the best approach to simulate it?
A bit of context:
In angular, when an update to ng-model is done, all interested parties are updated (e.g. view is refreshed). In kendo, when using k-ng-model, I cannot find a way of doing the same, where I would want to set the value from the controller / directive directly.
Here is an example: Coding Dojo
And, just in case, the code as well:
<input kendo-auto-complete
k-ng-model="vm.kendoSelection"
ng-model="vm.angularSelection"
k-data-source="vm.countryNames"
k-data-value-field="'name'"
k-data-text-field="'name'" />
sample controller:
angular.module("MyApp", [ "kendo.directives" ])
.controller("Controller", function(){
// just some data source
this.countryNames = [
{ name: "Albania" },
{ name: "Andorra" }
];
// how to set the k-ng-model here, so that it is also propagated properly (like on
// the view, and that other events like k-on-change work)?
this.kendoSelection = { name: "Albania" };
});
EDIT:
Even after the answer from Dion Dirza, the k-on-change is not firing (altough it is a solution in good direction)
Example with k-on-change
Dion Dirza
As documentation said, that k-ng-model should store or return actual type of widget value. Therefore to make your variable kendoSelection work, you should define it as actual type of widget value, which in this case is array type for auto-complete widget.
Change your code
vm.kendoSelection = [{ name: "Albania" }];
Because you are storing object array here so your variable should be array of object. Put a caution here that your object should contains a property that defined as widget data-value-field which it will be used for comparison with current data source.
What I have:
I have x-editable popup which should check input parameters with help of validate method
$(".vacancy-edit-select-status").editable({
value: $this.model.get('value').id,
source: source,
validate: function(value) {
$this.model.previousAttributes(); //UNDEFINED!!!
//....
},
success: function (response, newValue) {
$this.model.set('value', newValue);
}
});
What a problem:
Inside validate method previousAttributes() method return 'undefined'. (I've changed some model attributes before change x-editable popup's variables, so the history shouldn't be empty)
Question:
How can I access model history from validate method?
previousAttributes populating during set, validate calling before population of previousAttributes - reference
The answer is simple and maybe not clear enough from my question:
Backbone clones model state only on change event.
Moreover, when you have a collection as an model attribute there are also some nuances with previousAttributes. For example, to write attribute change to the history you have to rewrite the whole attribute. I mean
this.model.get('myCommentsCollection').push('newComment'); //DOESN'T WRITE ANYTHING TO previousAttributes
So to write something to model.previousAttributes you should do:
var comments = _.clone(this.model.get("comments"));
this.model.set('myCommentsCollection', comments);