Updating button attribute in a view from controller - Sencha Touch - model-view-controller

I have a simple form in a view and I have a field that looks like this:
xtype: 'numberfield',
label: 'Number',
name: 'num',
disabled: true
This field is in a 'formpanel'.
I am trying to set disabled to false when the form button is pressed (enable the field).
Now, there is a button in the form that triggers a function in the view (onButtonTap) and fires a function in the controller (enableField) and sends the panel and values to it:
onButtonTap: function(button, e, eOpts) {
var panel = button.up('formpanel')
var values = panel.getValues();
panel.parent.fireEvent('enableField', panel, values);
}
And in my controller this is enableField:
enableField: function(panel, values) {
panel.config.items[0].items[1].disabled = false;
}
I am sure my controller works fine and the function enableField is actually fired, but nothing happens to the button and it stays disabled..
What am I missing?

In the controller(within refs) add the following code to get the 'numberfield' to be enabled:
refs{
numberField: 'numberfield[name="num"]'
}
And on button tap enable the field by ths:-
onButtonTap: function(button, e, eOpts) {
this.getNumberField().enable();
}

Related

Custom Validation Rules do not work for Webix Colorpicker

I'm using webix as my JavaScript front end.
I've created a form with a colorpicker view. In Webix, by default, the colorpicker only allows users to pick from a pre-selected range of colors, and returns the hex code of the color selected.
This behavior can be overridden to allow entering of any color by using the option editable: true. However, editable: true allows users to enter anything into the colorpicker as if were a free text field.
I'm trying to use a custom validation to work around this. I should be able to return false in the custom validation function to alert the user user of an invalid value and to prevent the form from being saved until it's fixed. However, the custom validation function never gets called when used on the colorpicker.
Here's a webix snippet of what I'm trying:
https://snippet.webix.com/28oadxzl
webix.ui({
view:"form",
id: "theForm",
name: "theForm",
elements:[
{
view:"colorpicker",
label:"color",
name:"color",
id: "color",
editable: true,
validate: webix.rules.iscolor,
on: {
onChange: function(newv, oldv) {
// this gets called every time the color picker is changed.
webix.message({text: "onChange: " + newv})
this.validate();
}
}
},
{
view:"button",
type:"form",
value:"ok",
width:200,
align:"right",
click: function() {
// this gets called when the "ok" button is clicked.
webix.message({text: "click"});
$$("theForm").validate();
}
}
],
rules: {
iscolor: function(value) {
// never gets called! Should be called on colorpicker change and on "ok" click.
return false; // replace with regex hexcode validation.
webix.message({text: "iscolor: " + value})
}
}
});
You were almost right: https://snippet.webix.com/4mw3mxk8
The thing is that:
as a key in rules, you must use the name of the control ("color" in your case)
webix.rules includes only predefined validation rules (isEmail, isNotEmpty, etc)

kendo-datasource autoSync causes navigation placeholder to return to upper left corner of the grid

I'm using kendo-ui with angularJS 1.5 and I have a simple kendo-grid bound to a datasource with transport configured using functions as follows:
private buildDataSource() {
this.dataSource = new kendo.data.DataSource({
autoSync: true,
change: this.dataSourceChangeHandler.bind(this),
error: this.dataSourceErrorHandler.bind(this),
transport: {
read: this.dataSourceRead.bind(this),
create: this.dataSourceCreate.bind(this),
update: this.dataSourceUpdate.bind(this),
destroy: this.dataSourceDestroy.bind(this)
},
[...]
});
}
private dataSourceUpdate(e: kendo.data.DataSourceTransportUpdate) {
var updatedItem: KendoCosto = e.data;
[...]
e.success(updatedItem, undefined, undefined);
}
The grid options looks like this:
this.gridOptions = {
dataSource: this.dataSource,
change: this.gridChangeHandler.bind(this),
editable: {
mode: "incell",
confirmation: false
},
navigatable: true,
selectable: "multiple, cell",
allowCopy: true,
toolbar: [
"create"
],
[...]
The grid works fine and the read, create, update, destroy behave as expected.
My problem is that whenever I change a value in a grid's cell and hit enter, I would like to have keyboard navigation "placeholder" (the grid has navigatable: true) to remain on the edited cell, but it happens to be moved to the upper left corner cell.
This behavior happens only when dataSource's autoSync is set to true.
I've also tried to "set" the current cell via the ".current" method of the grid's api but it doesn't seem to work:
// this is bound to the grid's change event and it is supposed to
// store the currently selected cell in a property of the class
// that builds both the datasource and the grid
private gridChangeHandler(e: kendo.ui.GridNavigateEvent)
{
this.thisGrid = this.thisGrid || e.sender;
this.currentCell = e.sender.current();
}
// Then on the change event of the datasource I do
private dataSourceChangeHandler(event: kendo.data.DataSourceChangeEvent)
{
if (this.currentCell && this.thisGrid) {
this.thisGrid.select(this.currentCell);
this.currentCell = undefined;
}
}
any suggestions ?
Thanks in advance !
--- edit ---
The code I posted/pasted in the comment is absolutely unreadable so I'm repeating the code here:
To have your solution work, I had to modify my dataBound handler this way.
private gridDataBoundHandler(e: kendo.ui.GridDataBoundEvent) {
if (this.thisGrid && this.currentCell) {
setTimeout((() => {
// this.thisGrid.editCell(this.currentCell);
this.thisGrid.current(this.currentCell);
}).bind(this)
, 10);
}
}
without the timeout, the navigation placeholde was still resetting back to the upper left corner.
First, I think the grid change event is the wrong event to attach to as it only fires when the user selects a row/cell with the mouse...it will not fire on tab events.
So, I would use the grid save event, which fires after you make an edit and "commit" the change through enter, tab, mouse off, etc.
Second, the e.sender.current() includes the current identifying information like "grid_active_cell" and "k-state-focused" and "k-dirty-cell", etc. By the time you get to the dataSource change event, the cell has actually lost all that decoration and your this.currentCell is essentially pointing at a non-existent selector. So, you need to grab a more "permanent" identifier.
So, using the grid save event:
save: function (e) {
var row = $(e.sender.current()).closest("tr");
var colIdx = $("td", row).index(e.sender.current());
var model = e.sender.dataItem(row);
currentCell = "tr[data-uid='" + model.uid + "'] td:eq(" + colIdx + ")";
}
And then in the grid DATABOUND event(as the dataSource change event is still followed by events that change the cell focus to the top-left, but grid.dataBound is further in the chain and seems to work better):
dataBound: function (e) {
if (currentCell) {
grid.editCell(currentCell);
grid.current(currentCell);
}
}
Demo(with variable changes as I do not have your whole class, based on a kendo grid demo): http://dojo.telerik.com/#Stephen/OjAsU
Note that this solution(not my implementation, but your technique in general) will break tabbing from cell to cell, i.e. tabbing will commit the edit but the dataSource change event will always put the focus back on the just-edited cell instead of moving to the tabbed-to cell. This breaks user expectation of what tab does. So, you should consider trying to capture the enter key press only instead of relying on the grid events(which fire regardless of tab or enter).

How to avoid stacking navigation history in tab-bar states

Tab A - Tab B - Tab C
States like below;
tabs.a, tabs.b, tab.c
I want to close app like there is no navigation history when switching in each of tab states
For example: I was in Tab A then I clicked to Tab B and then I clicked to Tab C from now on if user pushes back button the app should close. In normal behaviour navigation history stacks up and if I push back button I'll go to Tab B from Tab C. How to avoid this behaviour
Below is my codes;
.state('tabs', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html"
})
.state('tabs.a', {
url: "/a",
views: {
'a-tab': {
templateUrl: "templates/a.html",
controller: 'AController'
}
}
}).state('tabs.b', {
url: "/b",
views: {
'b-tab': {
templateUrl: "templates/b.html",
controller: 'BController'
}
}
}).state('tabs.c', {
url: "/c",
views: {
'c-tab': {
templateUrl: "templates/c.html",
controller: 'CController'
}
}
});
<ion-tabs class="tabs-royal tabs-striped">
<ion-tab title="A" href="#/tab/a">
<ion-nav-view name="a-tab"></ion-nav-view>
</ion-tab>
<ion-tab title="B" href="#/tab/b">
<ion-nav-view name="b-tab"></ion-nav-view>
</ion-tab>
<ion-tab title="C" href="#/tab/c">
<ion-nav-view name="b-tab"></ion-nav-view>
</ion-tab>
</ion-tabs>
You can intercept the back button in each of your controllers.
From the documentation:
registerBackButtonAction(callback, priority, [actionId]) Register a
hardware back button action. Only one action will execute when the
back button is clicked, so this method decides which of the registered
back button actions has the highest priority.
For example, if an actionsheet is showing, the back button should
close the actionsheet, but it should not also go back a page view or
close a modal which may be open.
The priorities for the existing back button hooks are as follows:
Return to previous view = 100 Close side menu = 150 Dismiss modal =
200 Close action sheet = 300 Dismiss popup = 400 Dismiss loading
overlay = 500
Your back button action will override each of the above actions whose
priority is less than the priority you provide. For example, an action
assigned a priority of 101 will override the 'return to previous view'
action, but not any of the other actions.
In your controllers you can register a listener for the back button and exit if it has been pressed:
.controller('AController', function($scope, $ionicPlatform){
var deregister = $ionicPlatform.registerBackButtonAction(
function () {
ionic.Platform.exitApp();
}, 100
);
$scope.$on('$destroy', deregister)
})
.controller('BController', function($scope, $ionicPlatform){
var deregister = $ionicPlatform.registerBackButtonAction(
function () {
ionic.Platform.exitApp();
}, 100
);
$scope.$on('$destroy', deregister)
})
.controller('CController', function($scope, $ionicPlatform){
var deregister = $ionicPlatform.registerBackButtonAction(
function () {
ionic.Platform.exitApp();
}, 100
);
$scope.$on('$destroy', deregister)
});
NOTES:
Your last tab (C TAB) should have the name: c-tab:
<ion-tab title="C" href="#/tab/c">
<ion-nav-view name="c-tab"></ion-nav-view>
</ion-tab>
I have done it with a simple way
Added a function in controller scope of tabs state
.state('tabs', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html",
controller: function($scope, $ionicTabsDelegate, $ionicHistory) {
$scope.rt = function(e, index) {
$ionicTabsDelegate.select(index);
//e.preventDefault();
$ionicHistory.nextViewOptions({historyRoot:true});
}
}
})
and added ng-click on each ion-tab directive in tabs.html template like below;
<ion-tab ng-click="rt($event, 0)" title="A" href="#/tab/a">
The second parameter of rt function is the index of tab
I used $ionicHistory.nextViewOptions({historyRoot:true});
From the documentation
nextViewOptions()
Sets options for the next view. This method can be
useful to override certain view/transition defaults right before a
view transition happens. For example, the menuClose directive uses
this method internally to ensure an animated view transition does not
happen when a side menu is open, and also sets the next view as the
root of its history stack. After the transition these options are set
back to null.
Available options:
disableAnimate: Do not animate the next transition.
disableBack: The next view should forget its back view, and set it to null.
historyRoot: The next view should become the root view in its history stack.
That way I achieved what I want

kendo ui form update cancel button

I am trying to a simple kendo ui form with 'Save' and 'Cancel' buttons. I am using the Kendo.Observable to bind the data to the form.
The functionality I am trying to achieve is, if the 'Save' button is clicked, the form data will be saved. Else, if 'Cancel' is clicked the form will come back to read-only mode with the previous data that was present. To do this, I am first saving the model data in a 'originalvalue' property on click of Update button. If 'Cancel' is clicked, the 'fields' model data is restored to the 'originalvalue'. But the issue is that the , 'originalvalue' does not contain the original value. It gets updated when the user is editing during 'Save'.
The question is - how do I retain the original model data so that it can be refreshed on cancel?
Please find below the code. Appreciate your help, thanks.
<script type="text/javascript">
var viewModel = kendo.observable ({
updated: false,
originalvalue: {},
update: function(e) {
var original = this.get("fields");
this.set("originalvalue", original);
this.set("updated", true);
},
save: function(e) {
e.preventDefault();
if (validator.validate()) {
// make an ajax call to save this data
this.set("updated", false);
}
},
cancel: function(e) {
var original = this.get("originalvalue");
validator.destroy();
this.set("fields", original);
this.set("updated", false);
},
fields: {}
});
viewModel.set("fields", formArray);
kendo.bind($("#outerForm"), viewModel);
// prepare the validator
var validator = $("#outerForm").kendoValidator().data("kendoValidator");
I had to make the exact thing on a form I am currently developing. I am using a DataSource object for the data so I had to use cancelChange().
The thing I did there:
1. I made a Datasource with a schema:
... schema: {
model: {id: "id"}}
...
2. I got the object I was editing with the mapped id:
clientDataSource.cancelChanges(clientDataSource.get(this.get("contactID")));
where the ContactID is created in a setData function where I have passed the ID:
this.set("contactID", contactID);
As I may have notices and understood, you have another problem here where you arent using a DataSource but rather data for fields?
The problem here is that your originalValue is inside the Observable object and it is referenced to the variable original and thus it has observable properties. You should have the variable originalValue defined outside the observable object:
var originalValue;
var viewModel = kendo.observable ({ ...
And you should send the formArray also to that variable so you will have the defaults load before even the observable object is loaded such as:
originalValue = formArray;
viewModel.set("fields", formArray);
So when you need to cancel it you should have:
cancel: function(e) {
var original = originalValue;
validator.destroy();
this.set("fields", original);
this.set("updated", false);
},
I havent tested it but it should provide you some guidance on how to solve that problem.

Cancel panel opening from toolbar widget onClick handler

I have a Firefox extension which adds a toolbar Widget with a panel which should display when the widget is clicked. Under certain circumstances, the panel should not show when the toolbar widget is clicked.
I am instantiating the toolbar and panel like so:
var popup = panel.Panel({
width: 310,
height: 400,
contentURL: self.data.url('panel.html'),
contentScriptFile: self.data.url('panel.js'),
// NOTE: You can't use the contentStyleFile option here.
});
var toolbarOptions = {
id: 'someid',
label: 'Some Label',
contentURL: self.data.url('icon-16.png'),
panel: popup
};
// There doesn't seem to be a way to remove the toolbar in PB mode.
var toolbar = widgets.Widget(toolbarOptions);
How can I cancel the panel opening from the widget click handler? It seems to always open no matter what logic I put in there.
toolbar.on('click', function() {
if (dontShowPanel()){
// I want to somehow cancel the panel opening at this point.
} else {
panel.show();
}
});
I have tried to return false; from the click hander which doesn't seem to work. I have also tried to call panel.hide(). That doesn't seem to work either.
I'm using version 1.10 of the add-on SDK.
Your click event handler is called before the panel shows up which means that you can still change the panel at this point. However, something that is non-obvious: changing the panel of the Widget object won't have any immediate effect, you need to change it for the WidgetView object (the widget instance in the particular browser window). That object is being passed as a parameter to the click event handler. So your toolbar options could look like this:
var toolbarOptions = {
id: 'someid',
label: 'Some Label',
contentURL: self.data.url('icon-16.png'),
onClick: function(view) {
if (dontShowPanel()){
view.panel = null;
} else {
view.panel = popup;
}
}
};
When you create the widget, you need to add the panel instance as a property:
var panel = require("panel").Panel({
width: 250,
height: 250,
contentURL: data.url('panel.html')
});
require("widget").Widget({
id: 'id',
label: 'my-label',
contentURL: data.url('http://www.mozilla.org/favicon.ico'),
panel: panel
});
Update: sorry I didn't understand the entire question. As far as I know there is no way to conditionally prevent show the panel based on the click event, in a way that will preserve the anchoring.

Resources