The controller that controls an event using the recommended this.control: construct :
Ext.define('Mb.controller.Sav_vpc', {
extend: 'Ext.app.Controller',
init: function() {
console.log('controller.init called');
this.control({
'#storeMenu menuitem': {
click: this.onStoreMenuClicked
}
});
}
});
The function onStoreMenuClicked gets called twice, because the init method of the controller gets called twice, therefore it listens twice to the event.
But when is the controller.init() called ? And why is it called twice ?
Here is my application.launch function:
Ext.define('Mb.Application', {
extend: 'Ext.app.Application',
launch: function() {
console.log('launching app');
var controller = Mb.app.getController('Name');
console.log('end launching app');
});
...
This will give this output in the console:
controller.init called
launching app
controller.init called
end launching app
When calling getController inside application.launch, the init method of the controller gets called again, even the controller was already initialized.
I don't know if this is by design or if it is kind a bug, but I found a solution based on the recommendations of #AlexTokarev 'Inter-controller communication'.
Controller:
Ext.define('Mb.controller.Sav', {
extend: 'Ext.app.Controller',
init: function(){
this.listen({
controller: {
'*': {
savOnLaunch: this.onUserLoaded
}
}
})
},
Application launch:
Ext.define('Mb.Application', {
extend: 'Ext.app.Application',
launch: function() {
this.fireEvent('savOnLaunch');
}
Related
I have a question regarding ExtJS controllers. My code:
Ext.define('app.controller.Clients.Clients', {
extend: 'Ext.app.Controller',
stores: ['Clients.Clients'],
models: ['Clients.Clients'],
views: ['Clients.Clients'],
init: function() {
this.control({
'gridClients button[action=deleteClient]': {
click: this.onButtonClickDelete
},
'gridClients button[action=refreshClients]': {
click: this.onButtonClickRefresh
},
'gridClients button[action=printClients]': {
click: this.onButtonClickPrint
}
})
},
onButtonClickDelete: function(button, e, options) {
alert('DELETE?');
},
onButtonClickRefresh: function(button, e, options) {
alert('REFRESH?');
},
onButtonClickPrint: function(button, e, options) {
alert('PRINT?');
}
});
I'm going to refer to a grid named 'gridClients', and I'd like to know if there is any way to create a variable inside the driver file...
I'm going to refer to a grid named 'gridClients', and I would like to know if there is any way to create a variable inside the driver file, to refer to that grid.
Example, I would like something similar to:
Var Grid = Ext.ComponentQuery.query (#gridClients) [0];
And use it like this:
OnButtonClickRefresh: function (button, e, options) {
Grid.getStore (). Load ();
}
I really do not know where to declare that var...
In a controller, you are expected to work with the refs. Example:
Ext.define('app.controller.Clients.Clients', {
extend: 'Ext.app.Controller',
stores: ['Clients.Clients'],
models: ['Clients.Clients'],
views: ['Clients.Clients'],
init: function() {
...
},
refs:[{
ref:'clientsGridExample',
selector: '#gridClients'
}],
OnButtonClickRefresh: function (button, e, options) {
this // inside a controller, these functions are scoped to controller
.getClientsGridExample() // refs are automatically converted to getter methods
.getStore().load(); // Do whatever you want to do
}
});
It's pretty clear, if you check the Ext.app.Controller documentation.
You can set refs in your controller and use generated getter to get the grid you need. For example, if you have ref with value clientsGrid, getter getClientsGrid() will be created by ExtJS.
`
Ext.define('app.controller.Clients.Clients', {
extend: 'Ext.app.Controller',
stores: ['Clients.Clients'],
models: ['Clients.Clients'],
views: ['Clients.Clients'],
refs: [
{ ref: 'grid', selector: '#gridClients' }
],
init: function() {
this.control({
'gridClients button[action=deleteClient]': {
click: this.onButtonClickDelete
},
'gridClients button[action=refreshClients]': {
click: this.onButtonClickRefresh
},
'gridClients button[action=printClients]': {
click: this.onButtonClickPrint
}
})
},
onButtonClickDelete: function(button, e, options) {
this.getGrid().doSomething();
},
onButtonClickRefresh: function(button, e, options) {
alert('REFRESH?');
},
onButtonClickPrint: function(button, e, options) {
alert('PRINT?');
}
});
`
Inside the OnButtonClickRefresh listener you can get the grid like this:
var grid = button.up("grid");
Link to the fiddle
I have "components" (I'm using Angular 1.4.4 so they are directives) that need some data when bootstrapping, passed as an attribute. But sometimes that data is being loaded async.
I actually come to this:
module('whatever').directive('my-directive', function() {
return {
restrict: 'E',
scope: {
valueBinded: '=valueBinded'
},
controller: ['$scope', function($scope) {
// binded value is accesible through scope
var unbindActivateListener = $scope.$watch("valueBinded", function(newVal, oldVal) {
if (typeof newVal !== "undefined") { // Check if valueBinded contains valid information
unbindActivateListener();
activate();
}
});
function activate() {
console.log($scope.valueBinded) // Logs
}
}],
template: '<input type="text" ng-model="valueBinded"/>';
};
});
```
Which constantly watches for value and once is not undefined it calls to activate() method.
Obviously, adding this on each component is not nice. Is there a way where this can be refactored? Or preventing loading a directive until data is retrieved?
As part of my layout, I have a banner directive:
.directive('appBanner', [function () {
return {
restrict: 'E',
replace: true,
controller: function ($scope) {
$scope.$on('BANNER_ID_UPDATED', function (e, data) {
// code to fetch banner from db here
});
}
};
}]);
Now, depending on the page or content that's displayed, the banner may have to change. I've been trying to use events to communicate between my banner directive and my other directives. It will fire the following event:
$scope.$emit('BANNER_ID_UPDATED', id);
This doesn't always work. When I place some console.log calls, it seems that the event is sometimes fired before it is being listened for. How can I fix this, or how can I otherwise ensure communication between two unrelated directives?
There is a lot of potential pitfalls when using events. For this particular case I would propose to use a service (you can bundle it with banner directive into the banner submodule).
Your directive can then register the listener to trigger banner update. Then call some updateBannerData(data) of this service which will call the listener to trigger banner change. Here is some code directly from my head (hopefully without errors...)
.factory('bannerService', function() {
var bannerService = {
listener: null,
onUpdate(listener) {
bannerService.listener = listener;
}
updateBannerData: function(data) {
if(listener) {
listener(data);
}
}
};
return bannerService;
})
// inject bannerService here:
.directive('appBanner', [function (bannerService) {
return {
restrict: 'E',
replace: true,
controller: function ($scope) {
bannerService.onUpdate(function (data) {
// code to fetch banner from db here
});
}
};
}]);
I have a function I'm binding on:
angular.module('app').directive('resizable', function($window) {
return function(scope) {
angular.element($window).bind('resize', function() {
scope.$apply(function() {
//console.log($window.innerWidth);
scope.windowWidth = $window.innerWidth;
});
})
}
});
But this doesn't fire on onload. I need the initial screen width upon page load. How do I get this using Angular?
UPDATE:
I've also tried this ...
angular.module('ccsApp').directive('setSize',
['$document', function($document) {
return {
restrict: 'A',
link: function($scope, elements, attrs) {
$document.on("load", function() {
$scope.$apply(function () {
console.log('initial=');
});
});
}
}}
]
);
This code is in a directive. So you probably don't want the function to be executed when the application is loaded, but only when this directive is used. So, simply execute the function directly in the directive function:
angular.module('app').directive('resizable', function($window) {
return function(scope) {
// define the function
var updateWindowWidth = function() {
// console.log($window.innerWidth);
scope.windowWidth = $window.innerWidth
};
// call it immediately to initialize the scope variable as soon as the directive is used
updateWindowWidth();
// and make sure it's called every time the window is resized
angular.element($window).bind('resize', function() {
scope.$apply(updateWindowWidth);
});
};
});
You should probably also make sure that the event handler is unbound when the directive is destroyed. Otherwise, every time the directive is used, an additional handler is added to the window.
You could inject $window into a .run function of your module. Although there would be no scope available.
It is unclear what your are trying to do with the window size so our ability to provide helpful answers is limited
How does one abstract a directive properly?
As a really basic example, let's say I have this:
http://plnkr.co/edit/h5HXEe?p=info
var app = angular.module('TestApp', []);
app.controller('testCtrl', function($scope) {
this.save = function() {
console.log("hi");
}
this.registerListeners = function() {
console.log('do stuff to register listeners');
}
this.otherFunctionsNotToBeChangedWithDifferentInstances() {
console.log('these should not change between different directives')
}
return $scope.testCtrl = this;
});
app.directive("tester", function() {
return {
restrict: 'A',
controller: 'testCtrl',
template: '<button ng-click="testCtrl.save()">save</button>'
};
});
The tester directive has some methods on it, but only two will be changed or used depending on where the directive is placed. I could pass in the function as a directive attribute, but I am wondering if there is a better way to do this. I have been looking at providers, but I am unsure how or if those would even fit into this.
Instead of letting your directive assume that testCtrl.save() exist on the scope, you would pass in that function as an attribute. Something like this: http://jsbin.com/jidizoxi/1/edit
Your directive binds the value of the my-on-click attribute as a callable function. Your template passes in the controllers ctrlOnClick() function, and when the buttons ng-click calls myOnClick() Angular will call ctrlOnClick() since they are bound to each other.
EDIT:
Another common approach is to pass in a config object to the directive. So your controller would look something like:
$scope.directiveConfig = {
method1: function() { ... },
method2: function() { ... },
method3: function() { ... },
...
}
And your template:
<my-directive config="directiveConfig"></my-directive>
The directive then gets a reference to that object by:
scope: {
config: '='
}
The directive can then call methods on the object like this: $scope.config.method1().