Ext.form.Panel's beforesubmit listener does not respect return: false when attached via controller - extjs

I have a formpanel with a beforesubmit listener, which should prevent the submission if the form is invalid.
Sencha Fiddle availble here:
https://fiddle.sencha.com/#fiddle/3j5l (just comment the beforesubmit: 'onFormBeforeSubmit' line within the controler/panel and inspect the console to see the difference)
The listener is attached via a controller trough the init function like this:
//controller init function
init: function () {
var me = this;
me.listen({
component: {
'formpanel': {
beforesubmit: 'onFormBeforeSubmit'
}
}
});
},
onFormBeforeSubmit: function () {
console.log(arguments);
var me = this, form = me.getView();
console.log('beforesublit event fired');
if (!form.validate()) {
console.log('form is invalid!');
return false;
}
}
And all seems fine - the submit procedure is started, the onFormBeforeSubmit() method is executed, the form is considered invalid, but althought there is a return false statement - the form is submitted to the server.
Then, i tried to attach the listener simply via the listeners config of the panel like this:
//panel definitions...
listeners: {
beforesubmit: 'onFormBeforeSubmit'
}
And then it worked as expected.
As you can see the executed function is the same.
One thing i mentioned is that it receives different arguments - if triggered via the listeners config - it has a 5 arguments. Via controller - they are 4. The 5th one is an obect like this:
beforesubmit: "onFormBeforeSubmit"
scope: "self"
Can someone explain me why is this? Is it a bug or an expected behavior?
And after all - where is the right place to attach the listeners - in the controller or within the view??
Thanks.

First of all, you don't have to do this in init function.
Simply use control block of your viewcontroller like this:
control: {
formpanel: {
beforesubmit: 'onFormBeforeSubmit'
}
},
Please refer the documentation of control, it is much more straightforward to use that.
But it still not enough, and I think you are right, this is a bug. FormPanel's submit actually still using already deprecated function to fire events.
Please try the following override, it should fix this and allows you to use event listeners defined in controllers:
Ext.define('FixPanelEventFiring',{
override: 'Ext.form.Panel',
submit: function(options, e) {
var me = this,
formValues, form;
options = options || {};
formValues = me.getSubmitValues({
enabled: me.getStandardSubmit() || !options.submitDisabled
});
form = me.element.dom || {};
if (this.getEnableSubmissionForm()) {
form = this.createSubmissionForm(form, formValues);
}
options = Ext.apply({
url: me.getUrl() || form.action,
submit: false,
form: form,
method: me.getMethod() || form.method || 'post',
autoAbort: false,
params: null,
waitMsg: null,
headers: null,
success: null,
failure: null
}, options || {});
return me.fireEventedAction('submit',
[me, formValues, options, e],
'doBeforeSubmit',
this,
null,
'after'
);
},
});
Please be sure you include this in your overrides. You can also test this in fiddle, just add it before everything else. I'm not 100% sure it is perfect, I can imagine there are other issues with this, so please test this well.

Related

Extjs6 Custom js event

I have the following flow: before the app launches I want to check something on the server. Based on the response I want to make a decision. I've created an utility class that wraps my js event and also an app controller.
Bellow is app controller:
Ext.define('MyApp.controller.AppController', {
extend: 'Ext.app.Controller',
appEventDispatcher:function (){
// Create a dummy DOM element
var dummy = document.createTextNode('');
// Create custom wrappers with nicer names
this.off = dummy.removeEventListener.bind(dummy);
this.on = dummy.addEventListener.bind(dummy);
this.trigger = function(eventName, data){
if( !eventName ) return;
var e = new CustomEvent(eventName, {"detail":data});
dummy.dispatchEvent(e);
}
}
});
And my utility class:
Ext.define('MyApp.util.Util', {
statics : {
checkSomethingOnServer: function(customEvent){
var store = Ext.StoreManager.lookup('appStore');
store.load({
scope: this,
callback: function(records, operation, success){
if (success === true){
customEvent.trigger('success', true);
if (success === false)
debugger;
customEvent.trigger('fail', true);
}
}
});
}
}
});
Using the utility class I load a store. In the callback method, I trigger my custom event. This event is handled in the app.js file.
The code works in fiddle and also using app watch, when I want to build the code some errors are occurring complaining(syntax error).
I've created also a fiddle.
How to create a custom event in ExtJS and how to use it? I need the same behavior as with the js event but Extjs implementation.
In ExtJS, you would just attach an event listeners to the store with your custom event's name:
store.on('myownevent', function(success) {
console.log(success);
});
and your code may go ahead and fire events on the store by that name:
store.load({
scope: this,
callback: function(records, operation, success){
store.fireEvent('myownevent', success);
}
});
If no listener for that event is attached, nothing happens; if one or more listeners are attached, they are executed in the order of priority, for those with the same priority, in the order they were added.
https://fiddle.sencha.com/#view/editor&fiddle/21g7

ng-click doesn't work with external JavaScript

I am creating an ionic project and I am trying to integrate with Algolia autocomplete.js. I managed to make the search system work, however I added a ng-click on my search results and this function is not working as presented in this codepen that I did as example below:
http://codepen.io/marcos_arata/pen/VKVOky
Inside my algolia's result template:
<a ng-click="add_name({{{ name }}})">
Function that should be run when clicked:
$scope.add_name = function(name) {
alert('User added!');
console.log(name);
}
I tried to inject the results inside the scope but didn't work as well:
autocomplete('#search_name', { hint: false, debug: true, openOnFocus: true },[{
source: index.ttAdapter({ hitsPerPage: 15 }),
templates: {
header: '',
suggestion: function(hit) {
$scope.hit = hit;
return template.render(hit);
}
}
}]);
http://codepen.io/marcos_arata/pen/VKVOky
---- SOLVED ----
Instead of creating a ng-click function inside your templates, you can handle the event click of your search inside your "autocomplete:selected" function and use the dataset and suggestion results.
.on('autocomplete:selected', function(event, suggestion, dataset) {
$scope.name = suggestion.name;
console.log($scope.name);
## create any functions with the suggestion and dataset results inside
});
EDITING THE ANSWER:
Here is the codepen:
Apparently the suggestion keep the name clicked, so you dont need an extra function:
.on('autocomplete:selected', function(event, suggestion, dataset) {
$scope.name = suggestion.name;
console.log($scope.name);
});

Angular UI Router Reload Controller on Back Button Press

I have a route that can have numerous optional query parameters:
$stateProvider.state("directory.search", {
url: '/directory/search?name&email',
templateUrl: 'view.html',
controller: 'controller'
When the user fills the form to search the directory a function in the $scope changes the state causing the controller to reload:
$scope.searchDirectory = function () {
$state.go('directory.search', {
name: $scope.Model.Query.name,
email: $scope.Model.Query.email
}, { reload: true });
};
In the controller I have a conditional: if($state.params){return data} dictating whether or not my service will be queried.
This works great except if the user clicks the brower's forward and/or back buttons. In both these cases the state (route) changes the query parameters correctly but does not reload the controller.
From what I've read the controller will be reloaded only if the actual route changes. Is there anyway to make this example work only using query parameters or must I use a changing route?
You should listen to the event for succesful page changes, $locationChangeSuccess. Checkout the docs for it https://docs.angularjs.org/api/ng/service/$location.
There is also a similar question answered on so here How to detect browser back button click event using angular?.
When that event fires you could put whatever logic you run on pageload that you need to run when the controller initializes.
Something like:
$rootScope.$on('$locationChangeSuccess', function() {
$scope.searchDirectory()
});
Or better setup like:
var searchDirectory = function () {
$state.go('directory.search', {
name: $scope.Model.Query.name,
email: $scope.Model.Query.email
}, { reload: true });
$scope.searchDirectory = searchDirectory;
$rootScope.$on('$locationChangeSuccess', function() {
searchDirectory();
});
Using the above, I was able to come up with a solution to my issue:
controller (code snippet):
...var searchDirectory = function (searchParams) {
if (searchParams) {
$scope.Model.Query.name = searchParams.name;
$scope.Model.Query.email = searchParams.email;
}
$state.go('directory.search', {
name: $scope.Model.Query.name,
email: $scope.Model.Query.email,
}, { reload: true });
};...
$rootScope.$on('$locationChangeSuccess', function () {
//used $location.absUrl() to keep track of query string
//could have used $location.path() if just interested in the portion of the route before query string params
$rootScope.actualLocation = $location.absUrl();
});
$rootScope.$watch(function () { return $location.absUrl(); }, function (newLocation, oldLocation) {
//event fires too often?
//before complex conditional was used the state was being changed too many times causing a saturation of my service
if ($rootScope.actualLocation && $rootScope.actualLocation !== oldLocation && oldLocation !== newLocation) {
searchDirectory($location.search());
}
});
$scope.searchDirectory = searchDirectory;
if ($state.params && Object.keys($state.params).length !== 0)
{..call to service getting data...}
This solution feels more like a traditional framework such as .net web forms where the dev has to perform certain actions based on the state of the page. I think it's worth the compromise of having readable query params in the URL.

What would keep setter methods from being created in sencha touch

I have a simple controller that I'm starting to build in sencha touch 2:
Ext.define('ScoreKeeper.controller.GameScores', {
extend: 'Ext.app.Controller',
config: {
refs: {
requestButton: 'button[name=RequestButton]',
responseArea: '#scoreResponse'
},
control: {
requestButton: {
tap: 'sendRequest'
}
}
},
sendRequest: function (){
Ext.Ajax.request({
url: 'message',
method: 'GET',
disableCaching: false,
scope: this,
callback: function(options, success, response) {
this.setResponse(response.responseText);
}
});
},
setResponse: function (responseText){
this.setResponseArea(responseText);
}
});
When I tap my requestButton the sendRequest method fires correctly but when it gets to the setResponse method, it fails on the this.setResponseArea step.
When I checked my browser dev tools, it looks like the getter methods for both of my refs get created, but neither of the setter methods do.
What would keep these methods from being created?
Assuming #responseArea refers to an HTML element like a <div>. To attribute the response from your server to the HTML element, do this:
this.getResponseArea.setHtml(responseText);
instead of
this.setResponseArea(responseText);.
Don't forget getResponseArea gets you the HTML element with #responseArea as id. When you got it, you can do whatever you want with it; for example:
this.getResponseArea.setValue(responseText);
or
this.getResponseArea.getValue();.
That's why there is a get but no set.

Overriding back button in android - Phonegap Angularjs

I have following as my index.js (used to initialize phonegap)
function onBackKeyDown() {
angular.element('[ng-controller=NavCtrl]').scope().back();
}
var app = {
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('load', this.onLoad, false);
document.addEventListener('deviceready', this.onDeviceReady, false);
window.addEventListener("orientationchange", orientationChange, true);
},
onLoad: function() {
},
onDeviceReady: function() {
document.addEventListener("backbutton", onBackKeyDown, false);
}
};
This should technically run the function onBackKeyDown when back button is pressed which it does. I can see the logs in logcat.
This as per the documentation should override the default behavior, but apparently when i click back button it not only fires the function but executes the default behavior as well.
I am taken back to my login screen, whereas the behavior described is something else.
Please can anyone point me to the right direction, and let me know what I am not doing correct.
Not sure if it will do the trick but you could check if the onBackKeyDown function get an event as first parameter. If yes then try something like :
function onBackKeyDown(evt) {
evt.preventDefault();
evt.stopPropagation();
angular.element('[ng-controller=NavCtrl]').scope().back();
}

Resources