AngularJs and Webpack - How to import services, components and modules - angularjs

I've started using webpack in quite a large code base and have been using ES6's import statement to load the required dependencies for each module.
My file structure aims to be component based, with all the pieces specific to a component inside that component's folder. For example, the component file itself, and then templates, stylesheets, services, filters, etc, also specific to that component. Then we may have a generic service folder inside that module for services that are not specific to a particular component. eg:
- my-module
- component-a
- component_a.component.js
- component_a.component.spec.js
- component_a.html
- component_a.scss
- services
- some_service.service.js
main.module.js
main.run.js
main.config.js
My problem is that the main.module.js file is becoming unmaintainable, as I manually import all the required dependencies into this file and then register them into the angular world as required. eg:
import componentA from './component-a/component_a.component';
import someService from './services/some_service.service;
angular
.module('myModule', [])
.component('componentA', componentA)
.service('someService', someService);
You can imagine what this starts to look like when you have 10+ components. I've struggled to find a good solution for this problem, does anyone have any suggestions? Is there a best practice approach here?

I guess, every component/controller/service etc. can connect itself to the app in their own file.
Like you have got a file ome_service.service.js and inside you write:
myApp.service('omeService', function(){
var app = this;
// service stuff
});
So the registration is not in your main.module.js but in every file itself.
Although, you need to expose your app variable to be globally available. With a bundler like webpack this is easily handled. Gene Conroy-Jones wrote a nice post about how to make it possible via webpack here: https://medium.com/#drgenejones/using-webpack-with-legacy-angular-spas-automating-imports-using-require-context-58e0e9cc6e9c
With this approach, every new component would handle its registration on their own, and the central app would not have to carry all those imports.

Related

Is it possible to use single-spa (import map + SystemJS) and include TypeScript declarations?

I've created a few apps that follow the same pattern as the react-microfrontend projects listed on the Single-spa examples page. However, these examples are not using TypeScript.
I am receiving a TS error when App2 tries to import a function that exists in App1, because App2 is unable to find the typings for the App1 object. And this makes sense to me, since the importmap.json that is referenced in the index.html file of the root-config project only has a URL listed for the .js file of each microfrontend. I really don't want to add #ts-ignore statements everywhere, and I also don't want to do cross-app communication without being type safe.
An example of what I'm referring to:
This file in the Planets app references the Button component from the Styleguide app. I am unable to build this unless I place a #ts-ignore on the line before the import statement, because my Planets app cannot find the type declarations for Styleguide.
I am very new to import maps and SystemJS in general, so if anyone could point me in the right direction, I'd appreciate it. I've actually gone through the trouble of building my Styleguide as a regular npm library and just including it as a dependency in the Planets package.json file... which kind of defeats the whole purpose of using Single-spa in the first place.
importmap.json
{
"imports": {
"#react-mf/planets": "//localhost:9000/react-mf-planets.js",
"#react-mf/styleguide": "//localhost:9001/react-mf-styleguide.js"
}
}
page.component.ts
// #ts-ignore
import { Button } from "#react-mf/styleguide";
...
render() {
return (
// Unable to view the available props
<Button label="Hello World" />
);
}
Unfortunately no, they cannot be shared via the import map because that operates in the browser, and browsers do not understand TypeScript syntax.
Your alternatives are:
Publish your module's types to an npm registry and npm install it into each dependent microfrontend
One caveat here: you will need to update this with every new publish of the shared module, but that can be automated with various tools such as renovate
Mock the utility module's types
More info can be found at this Github issue: https://github.com/single-spa/single-spa/issues/609#issuecomment-665132965

What needs to be done to call third party Reactjs component from Anguarjs 1 using ngReact?

I have an existing Angularjs application, and I would like to start using Reactjs.
I tried ngReact, I can create my own Reactjs component using React.createClass and reactDirective, and Angular is able call this directive successfully. I am only using bower to add dependencies for angular, react, and ngReact, I am not using any other tools like gulp, grunt, webpack, etc.
The main reason that I would like to use Reactjs is to invoke third party Reactjs components, which is created using Reactjs, not created using ngReact. I could not find an example that is doing this. What do I need to do to make the Angular application to find the Reactjs components and invoke it?
My angularjs 1.4 app was also not using common UI build tools like webpack. I wanted to introduce some react components, and this was my approach.
I created a file, angularModuleOfReactComponents.js.
It imports react components, then makes an angular module (reactComponentsModule) to hold them:
import MyComponent from './MyComponent.js';
import AnotherComponent from './AnotherComponent.js';
(function(){
var m = angular.module('reactComponentsModule', []);
m.value('reactMyComponent', MyComponent);
m.value('reactAnotherComponent', AnotherComponent);
})();
You should be able to do something similar by importing third party libraries. You could also write a react class that uses a third party library and import your wrapper class.
I then used Babel to convert my jsx react components and my angularModuleOfReactComponents.js to CommonJS, and then I used browserify to convert to something the browser understands. (Although I used build tools, they do not involve my angular app. And, this isn't my permanent mechanism. This is a two step process that doesn't allow auto recompiling when source is changed. I was just trying to get something to work for proof of concept, and I'll be improving this process soon.)
In my angular app, I load angular library files, then react libraries, then ng-react, then my browserified angularModuleOfReactComponents.js and then my app's angular code (controllers, directives, services, etc.).
To use a react component, I declare a dependency on 'react' (from ng-react) and 'reactComponentsModule':
var myModule = angular.module('myModule', ['react', 'reactComponentsModule']);
And then I can inject whichever component(s) I need:
myModule.controller('myController', ['$scope', '$log', 'reactMyComponent', function($scope, $log, reactMyComponent) {
$scope.myprops = {name:'Jack'};
}]);
In html, ng-react's react-component directive is used like:
<react-component name="reactMyComponent" props="myprops" watch-depth="reference" />
To add on #user3141592 answer, I've done a blog post recently on how you can migrate your angular app to React using webpack and ngReact:
https://www.devpanda.me/2018/02/16/Simple-Angular-and-React-Hybrid-App/
I've posted a github repo as well :
https://github.com/danielcondemarin/angular-react-hybrid

Can Vue import components by dependency inject(DI) like angular?

I used angularjs(1.x) and new to Vue.There has DI in angular and refer a service in controller or a directive in template is easy, wherever the service or directive is. Angular will help you inject it automatically.
But I realize Vue refer a component by import(ES6) the path where the component in.If I change the component direcotry structure when refactoring(frequently), there will lost the component refer, and I should fix the path one by one. How troublesome it is.
I know there have a vue-injector like angular DI, but if it is easy to use?
Vue doesn't provide any formal dependency injection mechanism. It's completely up to you how you want to import dependencies.
Most Vue code samples do not use dependency injection at all and just use the ES6 module system via import and export.
If I change the component direcotry structure when refactoring(frequently), there will lost the component refer, and I should fix the path one by one. How troublesome it is.
You probably shouldn't be changing the directory structure frequently. Check out the Vue webpack template for an example of how to correctly structure a Vue project.
That being said, it doesn't really answer your question. Are you using webpack (or something similar)? You can configure precisely how webpack should locate modules via the resolve configuration property, so that you don't have to use relative import paths.
Another way is to register each Vue component globally so you don't need to import them at all.
Another way is to abuse ES6 modules by creating, say, a components.js module which imports each Vue component (wherever they are) and exports them all from that one module. Now you only need to import from that central module instead of hunting down the specific module location of each thing you want to import.
// components.js
import Button from 'path/to/button.vue';
import Alert from 'path/to/alert.vue';
export {
Button,
Alert,
};
// myform.js
import { Button } from 'path/to/components.js';
If you change the location of button.vue, you only need to update the import from inside the components.js file.
If you think you are going to restructure a lot, you can have one file that everyone imports and it imports all the rest, that way you only need to change in one place.
I personally prefer the import over the Dependency Injection, way easier to understand where everything is coming from.

Read local package/bower.json file to check the app version

I want to do some conditional operations inside my angularjs application for different versions of my app. How do I import the version property of my bower/package.json file from my Angular app locally.
I seem to get module not founderror when trying to import the json file (actually anything that's not .ts file). I am not planning to use $http.get(..) to read the json file asynchronously nor installing node/express packages to use require module.
I want to simply use the ES6 or SystemJS module loader for doing this task. Thank you :)
Solution in AngularJS Controller:
var appVersion = require('electron').remote.app.getVersion();

Webpack with angular 1.x and ES5

After reading hundreds of lines about browserify vs webpack and several how to of both I decided to go for webpack. The main reason it's because I liked the idea of bundling everything into a js file.
I have an angular project already working and I want to refactor it for webpack. The problem? My project is using angular 1.4.7, ng-animate and plain javascript (ES5) and all the tutorials and manuals are for ES6. I don't want to refactor my project so much. What's the way to go? I would like an example of each angular module : factory, directive, controller and so on. Many thanks
I typically have a feature.module.js file which has my module definition and requires all of the directives / services contained within the module. Also has the external dependancies.
/* module.js */
angular.module('my.module',['dependancy1', 'dependancy2']);
//External libraries
require('./dependancy1.module.js');
require('./dependancy2.module.js');
//Internal components
require('./thing.directive');
require('./thing.service';
/* service.js */
angular.module('my.module')
.directive('yourDir', function myDir(){...});
I'm dealing with the same problem now. And I found something that works (work in progress, but at least I can see progress). My steps:
Install yeoman
Run this angular-webpack generator. Select 'ES5' when asked (the other option is 'ES2015', which I guess is the same that 'ES6')
Start modifying the automatically generated boilerplate with your Angular code
Yes, you still need to learn about gulp and sass, but at least you can run a simple AngularJS app using the old ES5 syntax, and start modifying it.
I'm probably blogging about this. So, I'll update this answer then.
I tend to do this:
app.js:
require('/any/angular/deps');
var subModule = require('/some/sub/module');
var app = angular.module('myApp', []);
// pass the app module in sub modules to allow them to define their own config
subModule.configure(app);
/subModule/module.js:
var someSubDirective = require('./subDir/directive');
export function configure(app) {
someSubDirective.configure(app);
}
/subModule/subDir/directive.js:
export function configure(app) {
app.directive('myDir', myDir);
}
function myDir() {
}
My idea is to let all sub modules handle their own configuration, so declaring config or constant, factories or providers. Letting this then bubble up to the app.js. This means its really easy to delete a folder from your structure, because it is one line removal from it's parent module.
This also makes relevant file paths a lot shorter and easier to handle.

Resources