Titanium JIRA Archive
Alloy (ALOY)

[ALOY-743] Alloy: Add proper dynamic loading of namespace objects

GitHub Issuen/a
TypeImprovement
PriorityMedium
StatusClosed
ResolutionFixed
Resolution Date2013-07-18T17:39:44.000+0000
Affected Version/sn/a
Fix Version/sAlloy 1.2.0, 2013 Sprint 15
ComponentsRuntime, XML
Labelsalloy, compile, namespace, qe-testadded
ReporterFokke Zandbergen
AssigneeTony Lukasavage
Created2013-07-14T17:24:07.000+0000
Updated2013-07-22T23:21:39.000+0000

Description

The Alloy ns attribute can be used in a creative manner to load a CommonJS or native module to call the createTagName method upon, since:
<MyComponent ns="require('my.module')" id="myId" />
Will compile to:
$.__views.myId = require('my.module').createMyComponent({ .. });
This works fine, but still feels hacky. I would propose to make a change to /Alloy/commands/compile/parsers/default.js so that whenever a namespace is given like this:
<MyComponent ns="my.module" id="myId" />
The generated code would be:
if (typeof my.module === 'undefined') var myModule = require('my.module');
$.__views.myId = myModule.createMyComponent({ .. });

Attachments

FileDateSize
Screen Shot 2013-07-18 at 1.09.50 PM.png2013-07-19T13:28:53.000+000016558

Comments

  1. Fokke Zandbergen 2013-07-14

    Here is the PR: https://github.com/appcelerator/alloy/pull/178
  2. Tony Lukasavage 2013-07-15

    I'm not sure I want to do that right away. For one thing, how could you possibly make a distinction between a globally accessible namespace for creating components and a require()'ed module like you are accounting for? For example, what if this existed in the "app/alloy.js"?
       var my = {
         module: {
           createView: function() {
             /* ... */
           }
         }
       }
       
    I mention this condition because I've heard of people wanting to wrap Titanium functions and namespaces in their own calls and still leverage them through XML. I get that it feels "hacky", but there may need to be a more explicit way to do it. Perhaps bringing the "module" attribute over from the tag in Alloy 1.2.0? Not sure yet, need to think about this one.
  3. Fokke Zandbergen 2013-07-15

    I thought global scope access from CommonJS modules was deprecated? Don't really get your example. The only way to get an object in the controller scope is by requiring a CommonJS module right?
  4. Fokke Zandbergen 2013-07-15

    BTW, first line of last code snippet needs to be:
       if (typeof myModule === 'undefined') var myModule = require('my.module');
       
    In the PR I also added support for doing require('dir/my.module').
  5. Tony Lukasavage 2013-07-15

    No, its not deprecated. In fact, it's now supported on all platforms. That's why you can access the Alloy namespace in your lib commonjs modules without explicitly requiring it. In any case, I don't like the idea of strings behaving differently based on whether or not we've defined special handling under the hood. It's not terribly intuitive. If feel like this would be much more intuitive:
       <MyComponent module="my.module" id="myId" />
       
  6. Fokke Zandbergen 2013-07-15

    Well I guess if you can access the global scope, all this is at most a nice 'service', but not required. One could just add this in alloy.js and then use ns="myNamespace" wherever they want?
       var myNamespace = require('namespace');
       
    But of course this pollutes the global scope (that's why I thought access to global vars was deprecated anyway).
  7. Fokke Zandbergen 2013-07-16

  8. Tony Lukasavage 2013-07-17

    You could do it without polluting the global scope though via Alloy.Globals, but that's not ideal either. But back to the core issue here... I don't like the idea of generating more code where the generated JS is basically guessing whether or not you are using a module. I'd prefer a clear, explicit syntax. And for the , or element one already exists. Take a look at how a native module is included: https://github.com/appcelerator/alloy/blob/master/test/apps/advanced/native_modules/views/index.xml It's a specialized . I'm betting with very little effort "module" and "method" can be ported to arbitrary view XML elements to give the same functionality you are looking for while still be roughly along the lines of this issue and ALOY-747. The expectation would still be that the resulting UI object implements add()/remove(), but the create function could be modified as it is already for native modules. Thoughts?
  9. Fokke Zandbergen 2013-07-18

    I think you're right and Module can do the job. I just want to ask for a small change that doesn't alter any of the current behavior of the test. I think this PR speaks for it self: https://github.com/appcelerator/alloy/pull/186 It fixes the Alloy.Module.js parser currently not being used at all and as a result of fixing this and just a small change in the default.js parser now:
       <MyView module="my.module" />
       <!-- equals: -->
       <Module module="my.module" method="createMyView" />
       
    Just another small note. Why do we even need the <Module /> tag? I think we can live with just the module and method attributes, since already:
       <Module module="my.module" />
       <!-- equals: -->
       <View module="my.module" />
       
  10. Tony Lukasavage 2013-07-18

    [~fokke] there's a couple reasons for

    It's expressive. It's very clear ata glance that a developer is referencing a native/commonjs module.

    It changes the node name to "View". In the underlying native module system, the default function for creating an instance of a module is "createView". If you leave your instance creation as the default, then you don't need to specify a "method" attribute in the tag.

    So it's just shorthand for common use cases. And as you stated, after all this dialog, this is probably doable already using the "module" and "method" functions. In fact, I'm going to put an example in the 1.2.0 repo that shows exactly how to do this. Will update when it's done.
  11. Tony Lukasavage 2013-07-18

    No code has been changed in Alloy as a result of this ticket, but I am able to (cleanly) add arbitrary UI components from commonjs modules via XML. These modules can make full use of TSS, inline styles, and XML eventing. To make it very clear how one should do this, I've added the following example app to the repo: PR: https://github.com/appcelerator/alloy/pull/187 example: https://github.com/appcelerator/alloy/tree/master/test/apps/advanced/commonjs_xml I think that answers pretty much all concerns we had to this point. Now for the sake of keeping the whole conversation tied together, I'm going to take a look at your latest PR [~fokke] and see what contained in it still needs to be addressed, if anything. PR: https://github.com/appcelerator/alloy/pull/186
  12. Tony Lukasavage 2013-07-18

    Note: this PR and test is the result of discussion and aggregation of the aforementioned PRs. PR: https://github.com/appcelerator/alloy/pull/188 example app: https://github.com/appcelerator/alloy/tree/master/test/apps/advanced/commonjs_xml Functional testing should be confirmed on all supported platforms with the following steps:

    Run the example app

    Assert that all views presented in the XML are present in the app. It should look roughly like the attached screenshot

    Click the views with the "click me" labels and ensure that they open an alert dialog

  13. Federico Casali 2013-07-22

    Verified as working as expected. Environment: Titanium SDK 3.1.2.v20130718094558 Appcelerator Studio 3.1.2.201307191853 Alloy 1.2.0 LiveView 0.1.28 (from Beta stream) Android device 4.2.2 - iPhone 5 6.1.4 Closing.

JSON Source