angular-widget icon indicating copy to clipboard operation
angular-widget copied to clipboard

Define widget manifest inside widget / or lazy load dependencies

Open facultymatt opened this issue 11 years ago • 10 comments

The current method of defining widgets requires that all definitions are within the hosting app. It would be useful to allow definitions within the widget itself.

Some example use cases are:

  • Your widget needs JS from cdn hosted sources - this JS can't be included in the combined 'scripts.js' file defined in the host manifest.
  • One widget depends on a few modules that are not included in the host applications config property.

Possible approaches:

  • Allow lazy loading of modules and files, similar to: https://github.com/ocombe/ocLazyLoad.
  • Recognize new manifests registered in widgets.
  • Add helper methods, similar to addServiceToShare that can be called from the config block of widget. for example: widgetsProvider.addFile('funky-thing.js'); and widgetsProvider.addModule(FunkyThing');. All files would need to resolve before the widget was loaded.

facultymatt avatar Dec 08 '14 03:12 facultymatt

I'm not sure I understand. If a widget requires more files, why can't you list those files in the manifest generator in the hosting app?

shahata avatar Dec 10 '14 09:12 shahata

Yes, however I was under the assumption you could only create one manifest pattern for all widgets.

I just tried it again, and realized you can provide specific manifests, for example:

widgetsProvider.setManifestGenerator([function() {
    return function(name) {
      return {
        module: name + 'Widget',
        config: [], 
        priority: 1, 
        html: 'widgets/' + name + '.html',
        files: [
          'widgets/' + name + '.js',
          'widgets/' + name + '.css'
        ]
      };
    };
  }
]);

widgetsProvider.setManifestGenerator([function() {
    return function() {
      return {
        module: 'specialWidget',
        config: ['someModule', 'anotherModule'], 
        priority: 1,
        html: 'widgets/specialWidget.html',
        files: [
          'widgets/specialWidget.js',
          'widgets/specialWidget.css',
          'widgets/someModule.js',
          'widgets/anotherModule.js'
        ]
      };
    };
  }
]);

Where the first generator is a generic generator manifest, and the second is a specific manifest for one widget. The second, more specific manifest, will take precedence if the widget name matches the defined module name, in this case module: 'specialWidget'. You could also define specialWidget first, so long as you set the priority higher it will work as expected.

It may seem obvious but I didn't understand this usage pattern at first - I thought only the first, generic pattern worked.

It's worth considering if the documentation should be updated to include this example and use pattern to help other developers new to this library...

facultymatt avatar Dec 10 '14 11:12 facultymatt

Why not do just:

widgetsProvider.setManifestGenerator(function () {
  return function (name) {
    if (name === 'specialWidget') return {...};
    else return {...};
  };
});

Multiple generators is also supported and the manifest will indeed take the one with highest priority, but this is only needed when you have different entities loading widgets and they do not share the same config section.

BTW, in the code you sent it seems that the second manifest generator will return a manifest no matter what the widget name is, which seems like a bug to me. The second generator should return a manifest only for the specific widget name that it describes, otherwise it should return undefined or null

shahata avatar Dec 10 '14 11:12 shahata

OK, this clarifies the use cases for setManifestGenerator quite a bit.

The example you provide would work, but will quickly get unwieldy with many widgets... imagine 50 module definitions using if/else checks...

I believe the ideal use case would be to define a generic manifest, and be able to specify specific manifests for specific widgets. Maybe this means modifying setManifestGenerator to accept an optional string as the first param - if present will be used for this widget only.

widgetsProvider.setManifestGenerator('specialWidget', function() {
    return {};
});

Or we could make another method, something like:

widgetsProvider.registerManifestForWidget('specialWidget', function() {
    return {};
});

Thoughts?

facultymatt avatar Dec 10 '14 13:12 facultymatt

Of course, you should aspire to have a generic manifest. Still I don't think that a special API is needed for "specific" manifests since the following code:

widgetsProvider.setManifestGenerator(function myGenericManifest(name) {
    return {...};
});
widgetsProvider.registerManifestForWidget('specialWidget', function mySpecificManifest() {
    return {...};
});

Is exactly the same as:

widgetsProvider.setManifestGenerator(function myGenericManifest(name) {
    return {...};
});
widgetsProvider.setManifestGenerator(function mySpecificManifest(name) {
    return name === 'specialWidget' && {...};
});

shahata avatar Dec 10 '14 13:12 shahata

Good point... so does your second example do the same as the my second manifest generator example, while fixing the bug you noted?

facultymatt avatar Dec 10 '14 15:12 facultymatt

Yes, just make sure that the specific manifest has higher priority so that definitions order won't matter.

shahata avatar Dec 10 '14 15:12 shahata

@facultymatt. On your second example (https://github.com/wix/angular-widget/issues/9#issuecomment-66436047) you add several JS files including several modules (I assume that one of them depends on the others). AFAIK that is not going to work in current implementation of angular-widget as loading order of JS files is random.

I think JS files loading should be done by using a third party library like requireJS to allow better handling of JS files dependencies. I'm going to PR with this proposal.

alfonso-presa avatar Jul 15 '15 09:07 alfonso-presa

Multiple files loading order should not matter as long as each file is defining different angular modules. That said, I do agree that sometimes you need to control load order and I plan to add support for this pretty soon. Supporting requirejs when available is also a good idea.

shahata avatar Jul 17 '15 20:07 shahata

I am trying to lazy load the angular phonecatApp with your widget and having some problems.

I am also using the 3 apps in the demo.js so in total I got 4 apps.

My problem is that whenever I try to load the phonecat app, it looks like it is being added in the debugger but then immediately the default app provided in $routeProvider.otherwise(...) is being loaded.

There are no errors in the debugger...

Any ideas where I might start looking to figure out why this is happpening?

Regards Olaf

ghost avatar Aug 17 '16 22:08 ghost