Passing a bunch of properties into a wire factory?

20 views
Skip to first unread message

Ben Tremblay

unread,
Dec 4, 2014, 3:06:25 PM12/4/14
to cuj...@googlegroups.com
I have javascript code I cannot change.
I want to define an external spec and use the wire factory.
My external spec uses the create factory, with args and properties.
The names of the properties passed in will vary depending upon which component instances I want to create.

Using the create factory, I can pass in a prototype module ref, and a subclass module ref, but I cannot build my properties object dynamically. I use $exports to ensure only the expected component is returned. I have to do this because create only takes some facets and fails with additional ones, so my create block is wrapped in its own namespace.

I want to:
Create a subclass S of base class C,
And set a dynamic collection of properties on my created instance.

It looks like this is impossible, because the properties facet seems to require predefined property names, rather than a ref to an arbitrary collection of properties (passed in via provide in the wire factory). I cannot deference the props from a container, they must be assigned to the root of my new instance.

Is there a workaround?

The alternative seems to be a custom factory, but I cannot get that to work in my project.

Thanks,

Ben

Edan Schwartz

unread,
Dec 5, 2014, 10:12:43 AM12/5/14
to
At what point are your dynamic properties set? If it's by another spec which is importing the spec-in-question, you could do something like this:

// importing-spec.js
define
({
  componentV1
: {
    wire
: {
      spec: 'exporting-spec',
      provide: {
        someProps: { version: 1}
      }
    }
 
},

  componentV2: {
    wire
: {
      spec: 'exporting-spec',
      provide: {
        someProps: { version: 2}
      }
    }
 
}
});


// exporting-spec.js
define
({
  $export: {
    create: 'SomeClass',
    properties: { $ref: 'someProps' }
  }
});

// main.js
wire
(importingSpec).
 
then(function(ctx){
    ctx
.componentV1.version === 1;  // true
    ctx
.componentV2.version === 2;  // true
 
});

I've had situations where I want to allow the user of my module to provide configuration options. I ended up doing something like this:

// spec/module.js
define
({
  config
: {
    userName
: 'defaultValue'
 
},
 
  u
serController: {
    create
: 'controllers/User',
    props
: {
     
'name': { $ref: 'config.userName' }
   
}
 
}
});

// app/UserModule.js
define
('app/UserModule', [
  // notice I'm not using an amd 'wire!' plugin,
 
// so I'll be getting the raw spec
 
'spec/module',
 
'wire'
], function(moduleSpec, wire) {
  var UserModule = function(config) {
    this.config = config;
  }

  UserModule.prototype.start = function() {
    // Update the spec object with the config
    var mergedSpec = _.extend({}, moduleSpec, this.config);

    // { $ref: 'config.userName' }
    // will now resolve to this.config.userName
    return wire(mergedSpec).
      then(function(ctx) {
        ctx.userController.render();
      });
  }
});

// main.js
var userModule = new UserModule({
  name: 'billyTheKid'
});
userModule.start();


One downside to this is that by not using the amd `wire!` plugin, your AMD optimizer won't know which modules to package. I have a solution for this using the RequireJS optimizer, which I can show you if this is the way you want to go. 

It also exposes the modules asynchronous behavior to the end user (ie. start returns a promise). But if you're using wire, you're probably neck deep in promises anyways :)

There may be another solution using the wire factory `defer` option, but I've never been able to totally grok how that would work.

Benjamin Tremblay

unread,
Dec 5, 2014, 11:38:00 AM12/5/14
to cuj...@googlegroups.com
Edan,

Your solution seems right. I will try it. Meanwhile, here’s what I should have included in my question.
( I was finger typing on my phone)

This is the original, non-dynamic spec:

define({
settingsA:{
module: 'modules/settings/settingsA'
},
settingsB:{
module: 'modules/settings/settingsB'
},
strings:{
module: 'modules/resources/strings'
},
controllers:{
create: 'modules/app/controllerRegistry',
properties: {
main: {
create: {
module: 'modules/controllers/base',
args:{
module: 'modules/controllers/main',
}
},
properties:{
settings:{
$ref: 'settingsA'
}
}
},
about: {
create: {
module: 'modules/controllers/base',
args:{
module: 'modules/controllers/about',
}
},
properties:{
settings:{
$ref: 'settingsB'
},
strings:{
$ref: 'strings'
}
}
}
}
}
});

The task I have is to externalize the “controller spec” and make it so we can create new controllers with default properties and only have to add more details if necessary.
So the problem I see here is “properties” must be named in the spec! It does not seem like I can pass in a dynamically changing collection of JSON properties.

controllerSpec.js

define({
subclass : {},
controllerFactory : {
create : {
module : 'modules/controllers/base',
args : {
$ref : 'subclass'
}
},
properties : {
settings : {
$ref : 'settingsA'
}
}
}
});

I can successfully feed in “subclass” here from the “provide” facet in my new main.js. But I cannot feed in the dynamic “properties”…. Any attempt to use a $ref in properties seems to leave me with an unresolved expression. How can I do that?


Thanks,

Ben



Benjamin Tremblay

unread,
Dec 5, 2014, 11:59:20 AM12/5/14
to cuj...@googlegroups.com
Okay, my example wasn’t quite right.

define({
subclass : {},
props : {},
controllerFactory : {
create : {
module : 'modules/controllers/base',
args : {
$ref : 'subclass'
}
},
properties : {
$ref: 'props'
}
},
$exports: 'controllerFactory'
});


I only want  to export one part. 
I define subclass and props, and want to set them externally via provide.
And what happens is properties does not seem to be able to evaluate what is passed in as a group of properties applied directly to conrollerFactory,


    ctx.componentV1.someProps.version === 1;  // true
    ctx.componentV2.someProps.version === 2;  // true
  });

-- 
You received this message because you are subscribed to a topic in the Google Groups "cujojs" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cujojs/CUVXxI_zSB8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cujojs+un...@googlegroups.com.
To post to this group, send email to cuj...@googlegroups.com.
Visit this group at http://groups.google.com/group/cujojs.
To view this discussion on the web visit https://groups.google.com/d/msgid/cujojs/466d9ec8-9bf3-4e50-86f0-350f7aa0861c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Benjamin Tremblay

unread,
Dec 5, 2014, 3:57:49 PM12/5/14
to cuj...@googlegroups.com
Edan,

Your solution works perfectly and I have learned. 

Thanks Much!

Ben 

Benjamin Tremblay

--

Edan Schwartz

unread,
Dec 8, 2014, 10:19:34 AM12/8/14
to cuj...@googlegroups.com
Cool, glad I could help.
Reply all
Reply to author
Forward
0 new messages