In my application, we have a panel of back, next buttons that are included in every screen for navigation. From the next button, I want to call a Controller using:
myApp.app.getController('folder.MyInfoController').submitMyInfoForm(nextButtonId);
However, I get a TypeError: controller.doInit is not a function. I have an init method in my controller which is already working.
You are getting this error simply because "folder.MyInfoController" is not a controller - that is to say, it does not extend Ext.app.Controller which Ext.app.Application::getController is expecting to find. Now for some bonus points...
My psychic senses are telling me that in all likelihood you are trying to find a view-controller - which is notably not the same as an application-controller. They both share a base class but serve different purposes and ultimately have different implementations. This is well defined in the API.
As a preface to the next paragraph, I'd also point out that what you are doing looks like an anti-pattern. The biggest advantage of using the view-controllers is that they afford you all the conveniences of MVC whilst keeping your components decoupled from each another - there are only specific cases where you can justifying accessing one outside of the component scope and I can't think of any good reason why you'd need to access one from a global context.
That said, you can find a view-controller by obtaining a reference to the instantiated component (of type "folder.MyInfo" - or whatever you've called it) and asking it for it's view-controller. Note that there is a 1:1 relationship between a component and it's view-controller - each instance of the former has a unique instance of the latter.
Related
I'm working on an Angular (6) project and I'm trying to (elegantly) extend a Base Class for my containers. The app is really a "web tool" rather than a traditional "web app" (i.e. it follows a sequential order, rather than allowing the user to move around pages as they please).
If the user navigates on their own, I need the rendering container to check to see that all of its dependencies have been met (i.e. have all prior stages been completed?). If they haven't, I need to bring the user back to the earliest container that has all of it's dependents met.
Basically I made a Base Class that runs this check on initialization and manages any necessary redirect behavior. This all works as expected, but my issue is that the base class needs the StageGuard service (responsible for the aforementioned dependency check and redirect) passed to it through each container's constructor.
Considering I'll probably expand upon this base class to do more things that all containers will need, the need to pass items and services (other than unique properties associated with the container like, Stage Name) to the base class via the super() call.
I'm not sure how I'd get around this, only because the issue kind of makes sense -- the Base Class can't use the instances of services created because it's never a standalone instance itself. That said, you can probably understand why I'm plagued by a "there has to be a better way!" sensation.
Apologies if this is classical inheritance 101 and my googling is failing me, any insight is greatly appreciated.
Premises
Before angry hearts start blaming the singleton pattern and related practices, or criticizing this matter with theoretical crusades, I state that the question represents a real issue in the real life, analysed by people with a bit of brain and common sense.
Also, before technical answers and comments start to raise up, I specify that it is not a matter of implementation. My library does it well according to the rules of engagement and I am not looking for different possible solutions.
Question
Managing UI components, there are some which need to be unique.
For example, a dialog window, which has to be accessed and used by other different components for their own purpose of informing the user or proposing some options or interaction.
Dialog needs its own configuration settings which must be provided at the instantiation time, like for example the CSS classes to recognize which option button is pressed, handlers for specific events such as minimize, maximize, close and so on.
var options = {
confirmOption: '.confirm-btn',
cancelOption: '.cancel-btn',
maximize: function(dialog) {
$('.long-message').show();
}
};
If it was a normal class allowing multiple instances, in line with a spread and consistent syntax, I would do:
var dialog = new Dialog(options);
As the Dialog class is a singleton, the instantiation with the new keyword throws an error as it is logic to expect, while the proper getInstance method has to be used:
var dialog = Dialog.getInstance();
As options should be passed at the construction time, the syntax becomes:
var dialog = Dialog.getInstance(options);
and I know many will turn up the nose, while purists may say that it does not depict an immutable class, as getting the instance with or without arguments will get into something different.
My point is that:
getting several instances, the object returned is always the same, apart of the internal properties the first instantiation may set;
getting an instance after the first time, possible arguments passed to getInstance are ignored, as internal members have already been set, saving possible overwriting of settings which must be immutable;
internal members of the class which do not affect the consistency of the object, may be edited through proper methods.
What is your point regarding this situation?
Would you accept such syntax?
If not, which syntax would you use, trying to produce readable code, to respect the singleton constraint and the possibility to set config settings?
I am working on a project where in there are almost 90+ modules.
All modules has a set of input fields and on submit the data should be saved on the server.
At any given point in time only one module is active. But there can be open modules in the background.
Submit button is common to all modules, meaning there is only one Submit button throughout the application.
Below picture explains it more.
The prime motto is to keep the individual module changes to minimum and a way to handle certain things(validation, reload etc) in the module from a central place.
The current approach I am planning is,
Use a 'moduleInit' directive that all module should include in its
partial.
The directive takes the $scope of the module and pass it to a
common service/factory (pushConfigService)
The pushConfigService stores and keep this scope as long as the
module is open. Once the scope is destroyed the reference of the
same will be removed from the pushConfigService.
The footer panel is another directive with Submit button in it and
calls a save function in the pushConfigService which in turn calls
a $scope function in the module to get the form data.
pushConfigService talks to a bunch of other services like
dirtyChecker, apiGenerator and finally post data to the server.
Each module will have a set of scope methods defined with some standard names. Eg: _submit, _onSubmit, _cancel, _reload etc.
Another way to handle this, broadcast the submit event and each module listens to the same. There is possibility more actions will be added to the footer panel.
So I am little bit hesitant to use the broadcast approach.
My question, Is it a good idea to pass controller scope to a service? Any alternate suggestions?
Thanks in advance.
I believe your core concept is a nice way to handle this setup. Yet I'd suggest to split business logic from UI. I don't have a sample of your code so it is a little hard to build an exact example. Yet since you're using the $scope variable I'm going to assume you're not using a styleguide like or similar to John Papa's. His ways encourage you to not use the $scope and to stay close to actual JavaScript "classes".
How does this make a difference?
Instead of passing the whole scope, you'd be able to just pass the instance of your specific module. For one it is less confusing to you and colleagues to have a concrete interface to operate on instead of having to figure out the composition of given scope. In addition it prevents services from being able to alter the $scope.
The latter could be considered a good practice. Having just the controllers alter the scope make it easy to find the code which alters and manages the UI. From there on the controller could access services to do the actual logic.
Taking it one step further
So passing the class instance instead of scope should be an easy adjustment to the already proposed setup. But please consider the following setup as well.
It seems there are quite some different ways to handle and process the data provided by the module/end user. This logic is now implemented in the controller. One might think some of these modules share similar handling methods (big assumption there). You could move this logic to, so to speak, saving strategies, in services. On activation of a module, this module will set its preferred saving strategy in the service which handles the submit button click. Or more precisely, the save data method which should be called from the onClick handler in the controller.
Now these services/strategies might be shared among controllers, potentially setting up for a better workflow and less duplicated code.
Lately (2.x / 3.x) I just used xtype & factory methods to receive a instance of a class which was as simply as fast. Now I have started 4.x and my first App with MVC. As described in the tutorial the MVC pattern requires me to extend a class for each view I wan't to use, even if I use it just one time. But the best practice written by Sencha itself says:
just extend for re-useability or adding of functionality
In my case I need to register a whole bunch of classes even if they could be created from one base class except of some params like title, width,...
Another point is that the Controller overwrites any StoreId by convention and also requires a strict typing, means the class-name must end with an s. But as far as I know I cannot spare neither the model nor the store within the the controller store/model-array so is there any other point for this convention cause it seems not to spare typing.
Next point is that after merging from 3.X to 4.X the application initial load time has extended which seems to be caused due to either the many new classes that need to get defined or due to the fact that all controllers get instantiated at startup due to the default behavior of the MVC pattern. Is there any way to not auto instantiate a controller and just doing it lazy, for example when I request it on the application controller?
Yes I know, that are a bunch of questions but I guess they all around the same topic.
EDIT
After some sourcecode-digging I am no longer sure about the
requirement of the s when naming a store. I thought I stumbled over
this while going through the MVC tutorial. Can anyone verify this?
EDIT 2
My conclusions
Lacy rendering is quite simple. First of all the Controller should not be mentioned in the ApplicationController controller array. To create a instance of such a controller use the ApplicationController.getController(pureClassName)
[Note that each controller contains a reference to the ApplicationController called application] Now you need to be aware of the fact that the init(application) method and the onLaunch(application) method get no longer invoked by the ApplicationController, you need to do this yourself. When calling getController() the ApplicationController first lookup if there is already a instance of this controller in the internal reference cache if not it creates a instance and inject the controllername as Id. So controllers are a sort of singleton which is perfectly fine.
The controller itself creates all the getter for the registered stores, models and views and, that's a guess, it instantiate them (at least the stores)
About naming restrictions for stores, there no restrictions about ending with an s.
These are very valid points.
First, it has to be mentioned that you don't have to use MVC with ExtJS 4. You can still use ExtJS 3 style in your code.
I assume that if you understand the advantages of MVC and decide to adapt it, then yes - you will have to extend classes and there is some overhead, but admittedly you will end up with a cleaner, more reusable code. It has to be said that while you need to extend top-level views, the items within them can still be coded old style. In addition to this, within the init() of the controller you can modify certain view configs (which allows less of class extension, but more controller code).
I have to admit that if you have experience with ExtJS 3 and you're migrating to MVC style of an app, you will eventually see that the benefits outweigh the work involved.
Personally it's the first time I've been made aware of this 's' business with stores. So I can't comment much on this.
Lastly, a properly-written ExtJs 4 app, one that makes use of dynamic loading should load faster than an ExtJS 3 app. You can also compile an Ext version that only includes code used in your app. And yes, you can instantiate controllers (and their views and stores) when you need them, which works like a charm:
loadPage: function(aControllerName)
{
// save recent page in a cookie
Ext.util.Cookies.set('RecentPage', aControllerName);
// Dynamically load the controller
var iController = this.getController(aControllerName);
// Manually initialise it
iController.init();
// Load the page (by getting the first view of the controller).
var iPage = this.getView(iController.views[0]).create();
// Add the page to the content panel.
var iContentPanel = this.getContentPanel();
iContentPanel.removeAll(true);
iContentPanel.add(iPage);
iContentPanel.doLayout();
}
1.Izhaki answered your question well about the lazy controller initialization.
2.I am not really following you on the gripe about Store names. There are no restrictions on store names. They are merely suggestions of naming conventions.
3.The Ext.define method is great to define your classes - similar to Java or other OO languages. This is NOT required however and you can simply use Ext.create method to create an instance of a framework component and pass it custom config object.
You can also use Ext.define to create you base class and then call Ext.create('MyBaseClass',{title:'mynew tile'}); to get a slightly modified version of your base class.
I encourage you to read through the Sencha guides on MVC, and Class system and also review their examples to get better understanding.
I was watching a video on making a good javascript application infrastructure. Basically what it said was:
Your application consists of components
Components are parts of the page that can act on their own.
components can be registered into te application.
upon registration, they get their own sandbox.
A sandbox is an component's interface to the application core.
The core is built on top of a javascript library.
Components only have access to their sandbox, not to other components, nore the core or the underlying library.
Now, what I'd like to make is an application where you can easily make new components. Components have their own part on tha page, their own div in which they can work. And here comes the first part of my question: I want to give these components a copy of the jQuery object, but that has an internal restriction applied so that it can only work inside a certain containing element.
The second part is,that even if a component has limited access to the DOM using jquery, it can still access the document. I have tried both setting Window and Document to null, before running my test script, but the browser doesn't allow this. Is there any way that I can truly restrict the possibilities of an object to the methods of 1 object that I pass to it?
You're mis-understanding the point here. The intention isn't "make it 100% imposible for a component to acces anything it shouldn't". The ONLY way to do that is the insanely complicated step that Facebook took which is to parse the JS/HTML code and re-write it to dis-allow certain references, etc. I'm betting it took their dev team 1,000+ hours to do and there are still holes in it.
Basically the intention is to give each component a sandbox to play with and then say "please only use this". The authors then comply with the request.
Your sole other option is iFrames, in which case a component can do whatever it wants and it won't effect anything (assuming you're on a different sub-domain and you provide a parent-window proxy).