Used Yeoman's Knockout generator (c.a. early 2015) which includes in require.js and router.js. Just using KO's loader.
Am attempting to call a function (ko.observable or not) in component "a" from component "b". All the fluff below attempts to do merely:
// In componentB:
ComponentA.sayFoo();
Read KO docs on components and loaders, hacked for hours, etc. I don't want the overhead of say postal.js - and also could not get subscriptions (KO pub/sub) to work - I'm guessing for the same reason: the view models set up this way have no references to each other (?) - so the subscribers in one module don't see the publishers in another (right?) (… a bit over my head here %-)
1) Is this because the modules don't see each other… that this generated code does not place the KO stuff in a global namespace?
2) Trying to reach from one module to the other, seems to hinge on the getting the ref via the callback parms, using the function below, or is that incorrect? :
ko.components.get (name, callback) ;
startup.js using require looks like this:
define(['jquery', 'knockout', './router', 'bootstrap', 'knockout-projections'], function($, ko, router) {
// Components can be packaged as AMD modules, such as the following:
ko.components.register('component-a', { require: 'components/a/component-a' });
ko.components.register('component-b', { require: 'components/b/component-b' });
// [Scaffolded component registrations will be inserted here. To retain this feature, don't remove this comment.]
// [Scaffold component's N/A (I think?)]
// Start the application
ko.applyBindings({ route: router.currentRoute });
});
The (component) module A is straight forward, like this:
define(['knockout', 'text!./component-a'], function(ko, templateMarkup) {
function ComponentA (params) { console.log ('CompA'); } ;
ComponentA.prototype.sayFoo = function () { console.log ('FOO!'); } ;
ComponentA.prototype.dispose = function(){};
return { viewModel: ComponentA, template: templateMarkup };
});
Similarly, module B is:
define(['knockout', 'text!./component-b'], function(ko, templateMarkup) {
function ComponentB (params) { console.log ('Compb'); } ;
ComponentB.prototype.doFoo = function () {
//// B Needs to fire ComponentA.foo() … SEE CODE ATTEMPT BELOW
};
ComponentB.prototype.dispose = function(){};
return { viewModel: ComponentB, template: templateMarkup };
});
So this is where I'm stuck:
ComponentB.prototype.doFoo = function () {
ko.components.get ('component-a', ( function (parms) {
console.log ('parms.viewModel : ' + parms.viewModel );
// parms.viewModel is (unexpectedly) undefined ! So how to get the ref?
console.log ('parms.template : ' + parms.template );
// does have expected html objects, eg. [object HTMLwhatever], [object HTML...]
})) ;
This should be easy, or I'm dumbly leaving out something obvious!?
Maybe the modules need to be defined / set up differently?
Any suggestions would assist! Thx
This is just not how you'd normally communicate between knockout components.
Your options are:
1) Use https://github.com/rniemeyer/knockout-postbox. This is probably the best option as it integrates nicely with knockout. It is well documented and if you have troubles setting it up, you can always ask for help here.
2) Use any other global javascript EventBus (f.i.
postal.js) and emit/subscribe to events in your components.3) Have your root ViewModel pass common observables to each component as parameters - that way each component could modify/subscribe to the same observable.
4) (Probably what you want, although the worst scaling solution) If you give ids to the different components you could use
ko.dataFor(document.getElementById("id"))to directly access the properties and methods of your components.EDIT: In response to the comment:
Exactly - in your case the
{ route: router.currentRoute }object IS your root ViewModel. It currently only has one property calledroute, but you could definitely extend that.For instance:
Then you can pass that observable to multiple components as a parameter like this:
And finally you can use the new observable from within the component like this:
You can now subscribe to the observable, change it and so on. It will be shared between components, so changing it one component will trigger the subscriptions in all components.