deftjs / deftjs Goto Github PK
View Code? Open in Web Editor NEWExtensions for Large-Scale Sencha Touch and Ext JS Applications
Home Page: http://deftjs.org/
License: MIT License
Extensions for Large-Scale Sencha Touch and Ext JS Applications
Home Page: http://deftjs.org/
License: MIT License
An Ext JS view having a Deft Controllable mixin results in only the final superclass controller being exposed.
We used a workaround in the code here, but probably too specific for general use. There is some tracing functionality that you could use to see what's happening.
cnstaging@971dc02#L0R668
cnstaging@98176eb
I can provide a simple example of real code if needed... but its more complex looking at Ext, than just pseudo for this.
'Views'
[10] Ext.panel.Panel > [20] Workspace > [30] Embedded
[10] Ext.panel.Panel > [20] Workspace > [40] Window
'Controllers'
[n/a] Ext.panel.Panel > [50] Workspace > [60] Embedded
[n/a] Ext.panel.Panel > [50] Workspace > [70] Window
"> = extends"
20, 30, 40 all reference a different controller. The current version of Deft JS results in only the controller defined from [20] to be exposed.
50,60,70 bring in new abilities/functions depending on the role. In the current version of Deft JS, it results in only [50] being the responding getController()
Do you have plans to support this type of feature?
It is currently awkward to initialize ViewControllers because they are created indirectly via Views.
As opposed to controller-oriented frameworks, where the View Controller is initialized and created, pushed onto a presenting Window or View Controller and then subsequently creates its associated UI components, Ext JS and Sencha Touch are UI component-oriented frameworks, where the UI components are created directly and UI containers are populated via component descriptors (JSON-esque Objects populated with xtypes and configuration values). Consequently, with Deft JS, View Controllers are created in response to the creation of Views (rather than the other way around).
There is currently no way to pass an initializing configuration object to the ViewController's constructor for a Controllable View because the View Controller instance is created behind-the-scenes. To remedy this, we need a controllerConfig
config that can be specified in addition to the controller
annotation on a Controllable View.
Consider:
Ext.define('ExampleViewController', {
extend: 'Deft.mvc.ViewController',
config: {
value: null
}
});
Ext.define('ExampleView', {
extend: 'Ext.Container',
alias: 'widget.exampleView'
mixins: ['Deft.mixin.Controllable'],
controller: 'ExampleViewController'
});
and
Ext.create('ExampleView', {
controllerConfig: {
value: 'expected value'
}
});
or
Ext.widget( 'exampleView', {
controllerConfig: {
value: 'expected value'
}
});
In either case, the ExampleViewController's constructor will be passed the controllerConfig
initialization object when called, and in this example the value
config will be initialized with "expected value". The getValue() method in that ViewController instance will return "expected value".
Apparently, to initialize configs, Ext JS's AbstractComponent implementation just brute-force Ext.apply()'s the configuration object passed to the constructor onto the object instance itself, whereas Sencha Touch's AbstractComponent uses the Ext JS class system's initConfig() method.
Consequently, Ext JS's components never call initConfig(). Without that method call, Deft JS injection of configs never occurs.
Sencha might change that behavior in the future to make it more consistent across these two platforms in the future. This is going to require writing a platform and version specific workaround.
I get the following error when trying to include deft.js in a sencha touch 2.0 project.
Uncaught TypeError: Property 'log' of object #<Object> is not a function
Add support for 'untrusted' promises in Deft.Promise.when() - aka wrapping Promises from other API's that implement the .then() API.
Per the Sencha Forum discussion and Deft JS mailing list discussion on this topic:
Deft.mixin.Injectable
- the constructor arguments should passed as parameters.Hi,
The version you link to download on the deftjs.org site is very out of date. It references 0.1.1. It took me about half an hour to figure out why Deft.mvc.ViewController was failing to load, until I realised the download was incorrect.
Cheers,
Josh
Deft.Injector::configure()
should only eagerly resolve providers that were specified in that call, rather than evaluating all of the registered providers.
Developers new to Promises and Deferreds often are curious how to use these in the context of operation queues, where a set of functions needs to be run in sequence, in parallel, or as a pipeline.
@briancavalier posted an excellent GitHub Gist demonstrating how to achieve this with his library when.js: https://gist.github.com/2713087/
It would be helpful to provide similar convenience methods were provided as part of the Deft JS library, adapted to match the Deft JS Promise API.
Specifically, these methods would be implemented as new static methods of a Deft.promise.Chain class / package (with alternate class name Deft.Chain).
Oops. Not sure what I was thinking. At all.
My component hierarchy is App.ListMap <- App.Map <- Ext.Map. Injecting the geo attribute creates a new local property (this.geo) but does not update the config property (this.getGeo()) defined in Ext.Map. Calling this.getGeo() inside ListMap should return the injected value.
Ext.define('App.ListMap', {
extend: 'App.Map',
mixins: [ 'Deft.mixin.Injectable' ],
inject: {
geo: 'geolocation'
},
...
}
Deft.Injector.configure({
geolocation: {
className: 'Ext.util.Geolocation',
parameters: [{
autoUpdate: false,
allowHighAccuracy: true
}]
},
});
I want to creeate a controller that listens also what happens on viewport like this
Ext.define('Users.controller.Main', {
extend: 'Deft.mvc.ViewController',
requires: ['Users.view.UsersViewport'],
control: {
myviewport : {
selector: 'viewport'
},
....
The problem is that deftjs code raises an error "Uncaught Ext.Error: Error locating component: no component found matching 'viewport'. "
This happens because when parsing control settings it calls locateComponent
locateComponent: function(id, config) {
var matches, view;
view = this.getView();
if (id === 'view') {
return view;
}
if (Ext.isString(config)) {
matches = view.query(config);
if (matches.length === 0) {
Ext.Error.raise({
msg: "Error locating component: no component found matching '" + config + "'."
});
}
if (matches.length > 1) {
Ext.Error.raise({
msg: "Error locating component: multiple components found matching '" + config + "'."
});
}
return matches[0];
} else if (Ext.isString(config.selector)) {
matches = view.query(config.selector);
The problem is here: view.query(config.selector);. The view is the viewport and queries for himseld. There is no match. That is not Ok.
In ExtJS 4 MVC, listening for viewport work Ok, and generates also a getter for this viewport.
Hi,
When a class is created through the Deft.mixin.Controllable
mixin, if the class creation fails a generic error is thrown. Would this not be more useful to provide information related to why the class creation failed?
For example if you configure a store but the proxy fails to create, it's difficult to debug the exact point of failure. I'm happy to provide a pul request to fix this issue, I just need to know whether this is a valid request.
Cheers,
Josh
I made this control object:
control: {
myButton: {
selector : 'button[text="click me!"]',
listeners: {
click: 'onButtonClick'
}
}
}
but in onButtonClick() "this" is the button and not the controller, like stated in the documentation.
See: cujojs/when#60
DeftJs.org mentions a feature "Navigation - support for hierarchical views, route-aware" on its road map.
Will this feature enable routing in Ext Js MVC apps?
What is the status of the feature? (in a month, year, on back burner :))
Currently routing is only available in Touch, and by the looks of it, Sencha is not in a hurry to port it to Ext Js...
Thanks a lot!
The general idea is to intercept the config's setter and automatically remove any listeners specified via observe
from the previous, and add corresponding listeners to the new value.
The following test:
Deft.mvc.ViewController Observer creation should attach listeners to observed objects using nested property declaration.
is failing with:
TypeError: Object [object Object] has no method 'fireEvent'
For Ext JS 4.0.7.
To replicate:
Deft.Promise.any(), all(), map() and reduce() all currently accept an Array of Promises or values as input.
Extend this to support alternatively passing a Promise of an Array of Promises or values.
A user defined constructor on a View will break the Deft.mixin.Controllable from being able to hook into the object construction.
See the most basic constructor added on Lines 24-28 of the Controllable.js Spec
https://gist.github.com/3033957
Chances are this is breaking because of the "Ext.Function.createSequence" chain it's creating, but then being overridden the user defined constructor.
Question? Is there a way to hook or force call this when a view has a constructor?
One possible solution is to provide/expose a method similar to how Ext.util.Observable does. This would give us the best of both worlds, the current (automated) method for %80 of the time, and a manual way for those %20 edge cases.
Reference:
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.util.Observable
"this.mixins.observable.constructor.call(this, config);"
Simplify developer effort for using the Controllable mixin by automatically Ext.require()
-ing the referenced view controller class, rather than forcing the developer to specify the view controller class again in the requires
class annotation.
Let's say that I have a Grid with record and a contextmenu that appera at rightclick on a row. Controlling the actions of the contextmenu
from the Grid's ViewController would not work because will throw this error
Uncaught Deft.mvc.ViewController.addComponentReference(): Error locating component: no component(s) found matching 'contextmenuusers menuitem[action=add]'
.
....
control:{
...
cmUsersEdit:{
selector:'contextmenuusers menuitem[action=edit]',
listeners:{
click:function (menuitem) {
....
Which is the best way to do treat this type of situation.
when.js recently added this feature in a development branch.
Ongoing discussion of that enhancement to when.js can be found here: cujojs/when#30
See also: https://github.com/cujojs/when/blob/object-properties/when.js
Might be worth adding similar support to Deft JS Promises.
As of v0.8, Deft JS has dependencies on Ext framework classes that are not part of the ext.js
, ext-dev.js
and ext-debug.js
builds.
Prior to the changes introduced in this commit, certain Deft JS features would break in deployment builds because associated Ext class overrides would silently fail. Now, the associated loader dependency errors will be shown in the error console.
By accurately reflecting Ext framework dependencies, the individual JS class files can be used in combination with the Sencha build tool by power users to generate a truly minified build (omitting any unused classes).
The deft.js
and deft-debug.js
builds should only be used in combination with the ext-all.js
, ext-all-debug.js
or ext-all-dev.js
builds of Ext JS.
In all other cases, the loader should be configured to resolve Deft
to the individual source files in src/js/Deft
.
The logic nested in a closure within this method incorrectly assumes that arguments
corresponds to the original function invocation. Consequently, the intended check for the presence of an initialValue
parameter is wrong.
This functionality was broken when issue #6 was resolved.
Currently a Deferred will throw an Error when resolve(), reject(), update() or cancel() are called after it has been cancelled.
This should change, because it would otherwise require pending state changes above all calls to these methods. Pragmatically, the real expectation is that a cancelled Deferred / Promise will silently ignore subsequent attempts to complete it. In all other cases an Error should still be thrown when resolve(), reject(), update() or cancel() is called on a completed (but not cancelled) Deferred / Promise.
Integrate into unit tests.
Ext.define( 'App.controller.AbstractViewController', {
extend: 'Deft.mvc.ViewController',
init: function(){
//never called
return this.callParent( arguments );
}
});
Ext.define( 'App.controller.ConcreteViewController', {
extend: 'App.controller.AbstractViewController',
init: function(){
//called
return this.callParent( arguments );
}
});
to create a view with Sencha Architect and use the controllable mixin, Within Archiect I have created an override to include the mixin:
Ext.define('article.view.override.Preview', {
requires: 'article.view.Preview',
mixins: [ 'Deft.mixin.Controllable', 'Deft.mixin.Injectable' ]
}, function() {
Ext.override(article.view.Preview, {
});
});
The mixin
targetClass.prototype.constructor = Ext.Function.createSequence(targetClass.prototype.constructor, function() {....
code is reached, but the body never gets executed, and the controller is not created.
if I move the mixins: [ 'Deft.mixin.Controllable', 'Deft.mixin.Injectable' ] into the article.view.Preview class it works .
Ext JS 4.x syntax for mixins accepts either an array (of strings) or an object literal.
A minor issue that required us to change our existing code style on controllable views.
Reference:
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Class-cfg-mixins
Current Syntax
mixins: [ 'Ext.util.Observable' ]
Does not work
mixins: {
deftjs : 'Deft.mixin.Controllable',
observable: 'Ext.util.Observable'
}
I've built a nice desktop app OS using ExtJS 411.
It works great.
My plan was to use DeftJS to load my module instances into my windows dynamically.
That works, but..
My desktop is designed in a way that it can have multiple instances of the same module in different windows.
In order to avoid the duplicate id conflict, I figured I could hack something as follows;
init: function() {
var idPrx = this.view.id;
debugger;
this.control[idPrx + '-modButton'] = {
click : 'myClickFunc'
}
},
Now this works in the sense that I can instantiate a window and get no errors, but, the event doesn't fire as I would have expected it.
However, if I spawn a second window. I get a nasty error. My desktop OS actually has a nice Error / Msg system, so it generated the following:
ID: Error1
Date: 2012-08-17
Time: 17:22:45
Error Message: Error locating component: no component found with an itemId of 'module2-1-AppModule-modButton'.
sourceMethod: "locateComponent"
sourceClass: "Deft.mvc.ViewController"
Now, to understand this in context, my first window is named 'module-2-1-AppModule' and the button is named 'module-2-1-AppModule-modButton'.
The second window is named 'module-2-2-AppModule'. the button is named 'module-2-2-AppModule-modButton'.
So, I've not had a chance to dive into the inner workings of Deft, but I suspect it don't like the way I'm using it.
But to me, and my understanding of Deft. I want to be able to inject any module I wish and any amount and ensure that I'm not dealing with duplicate IDs issues when I have multiple instances.
I have a few workaround ideas, but hoping that I can stick to DeftJS.
Any ideas?
I understand that the Promise/A spec requires that exceptions raised in callback do not halt execution. However, silenting them completely leads to a lot of confusion, especially before one realizes that Deft is catching all their JS errors.
Errors can be logged to the console without crashing the execution thread. For example, in Chrome:
console.error(error.stack)
will give the same output as an uncatched error, with the "correct" stack trace. I don't know for other browsers, but even if the full stack cannot be provided, a notice should definitely be issued.
It would be be nice if DeftJS allowed me to append injections to those declared in a superclass.
For example, say I have a BaseViewController that declares:
inject: [ 'someObject' ]
I then extend that in MyConcreteViewController and declare:
inject: [ 'someOtherObject' ]
The problem seems to be that the list of injections overwrites the superclass instead of appending to it. I was assuming it would work like the other configuration properties appear to, where I can declare config: values and rather than overwrite, they add onto those declared in a superclass.
So, while it's not a huge issue to declare inject: [ 'someObject', 'someOtherObject' ] in the child class, it does reduce the advantages of inheritance a bit.
Deft.Logger
checks for the existence of Ext.Logger
to differentiate between Ext JS (where logging is done only through Ext.log()
) and Sencha Touch (where logging is done through Ext.Logger
).
Unfortunately, Ext.Logger.log() is defined in some versions of Ext JS, with all methods mapped to Ext.emptyFn
(an empty function).
Refactor this to use Ext.getVersion( 'extjs' )
rather than making the determination based on the existence of Ext.Logger.log().
using deftjs 0.6.4
browser : chrome 18 on windows 7.
was doing:
Deft.Injector.configure({
preferences: {
className: 'MyApp.preferences.Preferences',
eager: true
},
...
});
created MyApp.preferences.Preferences as singleton
Ext.define("MyApp.preferences.Preferences",{
singleton : true,
...
});
Got this in Chrome console:
ext-dev.js:7941Uncaught Error: [Ext.create] MyApp.preferences.Preferences' is a singleton and cannot be instantiated
When the Injector is asked to resolve an identifier that references nonexistent (or non-required) class, the changes in Issue #11 cause it fail with an error related to availability of classReference.singleton
rather than letting the developer know that the class was not found.
Current error message:
TypeError: 'null' is not an object (evaluating 'classDefinition.singleton')
It should instead tell the developer the class definition wasn't found.
Add better error handling and reporting for this situation.
Otherwise, when errors occur the Jasmine SpecRunner will attempt to traverse Sencha class system instances as Objects and pretty print all the keys and values up to 40 levels deep... this tends to completely lock up the browser.
Deft.Promise::map() only passes the value to the mapping function.
To match Array::map() behavior, it should also pass the current index and original Array.
The ViewController should generally be the injection target for state and services. In the rare cases where you need access to those View Controller properties in a view (generally just for initializing the store associated with a Grid, Tree or DataView), they should be accessible within the view's initComponent()
method by way of getController()
.
However, in Deft v0.6.7 and earlier, getController()
is not valid until after the View's constructor has been executed. When initComponent()
is triggered by a View's constructor, getController()
is not be available.
Moving forward, getController()
should be valid when the View constructor method executes.
The new setup sequence should be:
controller
annotation should be created and fully instantiated.getController()
method referencing that View Controller instance should be added to the View instance.constructor
method should be executed.controlView()
method should be called, passing the fully instantiated View instance.init()
method should be fired once the View has rendered / initialized.Once 'live events' (see issues #4 and #5) has been implemented, steps 5 and 6 will be revisited and simplified.
We've been considering enhancing the Controllable mixin to add a getController()
accessor to the view that would provide access to the corresponding view controller. Using this a view would be able to access its view controller to obtain references to any objects that were injected there and allowing both view and view controller to interact with the same injected prototype instance. Currently the controller has to access the store via whatever component it might have been assigned to (not ideal).
Thoughts?
In Sencha's controllers, the application is passed in, and set to controller.application.
How do I get a reference to the application from a Deft ViewController? Or do I not want to do that?
How do I fire an app-wide event?
Thanks
Neil
See TODOs in spec/Deft/promise/Promise.coffee
.
Looks like a promising framework, but the README contains no instructions for best-practice on how to integrate it with either Sencha Touch 2 or ExtJS 4. Please provide an install/usage section with detailed instructions.
my HEAD looks like this (using Phonegap):
Somehow Sencha Touch is autoloaded (using the microloader?), so DeftJS should be loaded only after the Ext namespace has been defined. Please advice.
<!-- resources -->
<script type="text/javascript" src="resources/javascript/i18n.js">
</script>
<script type="text/javascript" src="resources/javascript/locale_da.js">
</script>
<!--
<script type="text/javascript" src="lib/deftjs/build/deft-debug.js">
</script>
-->
<!-- phonegap -->
<script id="cordova" type="text/javascript" src="cordova-1.8.0.js"></script>
<!-- The line below must be kept intact for Sencha Command to build your application -->
<script id="microloader" type="text/javascript" src="sdk/microloader/development.js"></script>
<script type="text/javascript">
// document.addEventListener("deviceready", app.mainLaunch, false);
</script>
</head>
Deft.mvc.ViewController::onViewDestroyed()
is never called on Sencha Touch (due to the differing destruction lifecycle path). Consequently, the cleanup logic implemented there is not executed on that platform.
Proposed solution: move the clean-up logic to the destroy()
method. This will require any subclasses to call this.callParent()
, but that should be fairly well understood as a standard practice when overriding destroy()
method.
Add an otherwise()
convenience method to Deft.promise.Deferred and Deft.promise.Promise instances for registering a failure callback.
After Updating to the latest version of deft I am getting the following error.
Uncaught TypeError: Cannot read property 'buffer' of undefined
Ext.define.doFiresencha-touch-all.js:17366
Ext.define.firesencha-touch-all.js:17316
Ext.define.doDispatchEventsencha-touch-all.js:22436
Ext.define.dispatchsencha-touch-all.js:59135
Ext.define.publishsencha-touch-all.js:59209
Ext.define.doFiresencha-touch-all.js:17389
Ext.define.firesencha-touch-all.js:17316
Ext.define.doDispatchEventsencha-touch-all.js:22436
Ext.define.dispatchEventsencha-touch-all.js:22417
Ext.define.doFireEventsencha-touch-all.js:26678
Ext.define.fireEventsencha-touch-all.js:26637
Ext.define.constructor
My code causing the issue.
Ext.define("App.controller.Activities", {
extend: "Ext.app.Controller",
mixins: [ 'Deft.mixin.Injectable' ],
inject: {
geo: 'geolocation'
},
config: {
refs: {
index: 'activitiesindex'
},
control: {
index: {
initialize: 'viewPrivateList'
}
}
},
viewPrivateList: function(){
...
},
});
Sencha code throwing the error:
options = listener.options;
...
if (!firingFn) {
firingFn = boundFn;
if (options.buffer) {
firingFn = Ext.Function.createBuffered(firingFn, options.buffer, scope);
}
if (options.delay) {
firingFn = Ext.Function.createDelayed(firingFn, options.delay, scope);
}
listener.firingFn = firingFn;
}
I'm not sure if this is a Sencha or Deft issue but It only breaks after upgrading Deft to the latest.
I have a form that has two submit buttons, one in the upper right hand corner and another at the bottom.
{
xtype: 'button',
action: 'submit',
ui: 'action',
align: 'right'
},
...
{
xtype: 'button',
action: 'submit',
ui: 'action'
},
Currently the only way to add a listener to both the buttons is to give it a unique identifier and create two separate listeners.
titleSubmitButton: {
selector: 'button#edit_title_submit',
listeners: {
tap: 'onSubmitTap'
}
},
formSubmitButton: {
selector: 'button#edit_form_submit',
listeners: {
tap: 'onSubmitTap'
}
},
It'd be nice if there was a way to add a listener to both buttons using a single selector.
listeners: {
selector: 'button[action=submit]',
tap: 'onSubmitTap'
},
I would expect to see a log "THIS SHOULD BE SHOWN" as a correct behavior. the promise object should be resolved.
the code will only work if the resolve will occur after the promise.then.
var defr = new Deft.promise.Deferred();
var promise = defr.getPromise();
defr.resolve();
promise.then(function(){
console.log("THIS SHOULD BE SHOWN");
});
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.