I have the following example (http://jsfiddle.net/a473n/1/). The code is this:
Ext.state.Manager.setProvider(new Ext.state.LocalStorageProvider());
Ext.create('Ext.form.field.Checkbox', {
stateful: true,
boxLabel: 'Click me',
stateEvents: ['change'],
stateId: 'my-checkbox',
renderTo: document.body
});
When I use Chrome's debugger to view Resources and see local storage, I can see an entry for 'my-checkbox', but when I click the button that represents the checkbox in the page UI, I don't see the value of checked or unchecked being written to local storage. The value in local storage never changes. What gives?
The state data comes from the getState method. For checkboxes, it is inherited from AbstractComponent, and it merely saves the size of the component (see the code). So you'll have to provide your own implementation of this method if your wish is to save and restore the value.
Of course, there is a second step, you'll also have to override the applyState method to apply the extra things you'll be saving.
I've updated your fiddle to demonstrate this:
Ext.state.Manager.setProvider(new Ext.state.LocalStorageProvider());
Ext.create('Ext.form.field.Checkbox', {
stateful: true,
boxLabel: 'dasfasdf',
stateEvents: ['change'],
stateId: 'my-checkbox',
renderTo: Ext.getBody()
,getState: function() {
return {
checked: this.getValue()
};
}
,applyState: function(state) {
this.setValue(state && state.checked);
}
});
VoilĂ ! Value-wise stateful checkbox!
If you want to do this with something more involved than a checkbox, it would probably be a good idea to also call the parent of the overridden methods. Check the implementations for the actual components, to see if you need it.
Related
I am passing an object 'item' from parent to 'child' component. It works just fine. Please refer this.
As you change the values from dropdown, it updates the UI. My issue is that the same exact code does not work in my application (running locally on my machine). I even tried adding {{item.type}} in html, but it does not change (sticks to original value). One thing I noticed that, if I put #change='onChange' and printed the value in onChange method and it prints updated value.
Really unable to find solution to fix this. Any help would be great. Thanks.
The issue is you are adding the type property to your model after the item was already bound to data, and Vue cannot detect changes to properties added that way.
The fix is to make sure there is a type property on item,
item: {
"direct_sale_price": "",
"is_auction": true,
"is_tender": false,
"type": null
}
or to properly add it using $set.
created: function () {
if (this.item.is_auction) {
this.$set(this.item, 'type', 'auction')
} else if (this.item.direct_sale_price) {
this.$set(this.item, 'type', 'direct-sale')
} else if (this.item.is_tender) {
this.$set(this.item, 'type', 'tender')
} else {
this.$set(this.item, 'type', 'plain')
}
}
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.
I'm trying to use two-way binding in an Ext JS 6.0.2 component. However, it's not working how I would expect it to, or at all, as far as I can tell. I've created a minimal example:
Ext.define('MyComponentController', {
extend: 'Ext.app.ViewController',
alias: 'controller.mycomponent'
});
Ext.define('MyComponentModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.mycomponent'
});
Ext.define('MyComponent', {
extend: 'Ext.Component',
xtype: 'mycomponent',
viewModel: {
type: 'mycomponent'
},
controller: 'mycomponent',
config: {
thing: 'a defualt value'
},
bind: {
thing: '{thing}'
},
twoWayBindable: 'thing'
});
var myComponent = Ext.create('MyComponent', {
thing: 'a new value'
});
// Use setTimeout to give bindings time to update.
setTimeout(function() {
console.log(myComponent.getViewModel().get('thing'));
}, 1000);
I also have a Sencha fiddle here: https://fiddle.sencha.com/#fiddle/1efk
What I would expect from running this code is to see a new value logged to the console. Instead, I get null. The value that is being set on my view is not being published to my view model, even though I have bind and twoWayBindable set. Have I misunderstood how to use two-way binding?
Take a look at this example: Fiddle
It's a fork of your example. I've added a few things:
Ext.application with a launch method and placed the creation of myComponent here.
rendered myComponent to Ext.getBody().
an update method for the thing config, which logs its value when changed.
a ViewModel binding, which logs its value when changed.
Ext does not set up bindings until the viewmodel is initialized. This does not happen until the view itself is initialized. That's why why it's necessary to render it. As Mitchell Simoens correctly pointed out, instantiating a view will initialize it and its viewmodel, even when it isn't rendered. In my example, however, the viewmodel bind callback is only executed when i'm actually rendering the view.
Also, keep in mind that bindings are scheduled and do not fire instantly.
In case you haven't seen it, here's Ext JS's two-way binding example.
I hope this clarifies things!
replace your console.log line with this:
console.log(myComponent.getThing());
Simple use case: I want to manually control when I load the store. When the store is loaded, I want the data to populate the list. However, I cannot get it to work.
When I set autoLoad to true on the store, it works. If I remove autoLoad, and add this to the view nothing happens.
List config:
{
xtype: 'list',
fullscreen: true,
disableSelection: true,
store: 'NewsStore',
itemId: 'newsList',
itemTpl: '<li><strong>{date} - {title}</strong><br/>{text}</li>',
plugins: [
{
xclass: 'Ext.ux.touch.PullRefreshFn',
pullText: 'Henter nyheter',
refreshFn: function () {
Ext.getStore('NewsStore').load();
}
}
]}
The listener on the panel will load the store:
listeners: {
painted: function () {
Ext.getStore('NewsStore').load();
}
}
I have tried to use the callback from the load method. I have tried to get the store from the list and update this, nothing works (the list is simply empty and does not display anything, even though I can see that the code is fetching the information). Any idea what I am doing wrong here?
You have to use setStore property to populate the data
var myStore = Ext.getStore('NewsStore');
Now access the list object and just
mylist.setStore(myStore);
I recently found this great component -> Knockout-Kendo.js.
I use it to handle some behaviors with kendoComboBox.
The synchronization with the viewmodel works perfectly.
I want to listen changes of the control to execute some actions based on the current selected value.
I don't see any property that I can bind in the 'data-bind' attribute to listen changes but I know that internally, the knockout-kendo component listen changes and this is how the viewmodel is able to by sync with the control.
If I try to listen the valueChange event of the control, the problem is my eventhandler is catched before the viewmodel and while running in my eventhandler, I just have the previous value of the control using the viewmodel.
Look at this binding configuration in the component. What I understand is I'm able to use 'enabled', 'search', 'data', 'value' and any other exposed properties of the telerik control. What would be nice would be to define in data-bind attribute a property 'change' with an eventhandler linked in my viewmodel and be sure my eventhandler would be called after the internal eventhandler of the knockout-kendo component.
createBinding({
name: "kendoAutoComplete",
events: {
change: VALUE,
open: {
writeTo: ISOPEN,
value: true
},
close: {
writeTo: ISOPEN,
value: false
}
},
watch: {
enabled: ENABLE,
search: [SEARCH, CLOSE],
data: function(value) {
ko.kendo.setDataSource(this, value);
},
value: VALUE
}
});
I know I can try to modify the order of bind of events to be sure my eventhandler must be called after the synchronization of the viewmodel but I think it's a very bad practice.
Anybody have an idea how I can solve this problem with elegance?
You haven't mentioned why you want to do this. I can imagine two reasons:
To trigger some UI behavior/logic directly;
To trigger business logic (which may in turn trigger UI changes of course);
For people landing at this question with the latter case, here's an alternative solution. (This answer may not be a straight up answer to the OP's question, but seems useful enough to post it here.)
Suppose you have this basic view model:
var ViewModel = function() {
var self = this;
self.kendoObservable = ko.observable("Some text")
};
There are two ways you can indirectly respond to changes by Kendo. First, for simple cases, there's computed observables:
// Option 1, add this to ViewModel
self.dependentObservable = ko.computed(function() {
return self.kendoObservable() === "" ? "Empty" : "Not empty"; // example
});
This dependentObservable will be modified each time the kendoObservable changes. Basic stuff.
If you want to do something more complex when kendoObservable changes, e.g. do an AJAX call or whatnot, you may need a manual subscription:
// Option 2, add this to ViewModel
self.kendoObservable.subscribe(function(newValue) {
// Possibly do an AJAX call here or whatnot. Example:
alert("The new value is: " + newValue);
});
This will allow you to fire some complex logic each time the kendoObservable changes. AFAIK you need to check yourself whether the newValue is actually a changed value, at least in some versions of KO.