Application is being built using .Net 5 MVC & Web API. Application has a plugin system which is organized as below.
[Starting MVC Application]
|__MainLayOut
|__Modules
|__Plugin1
|_Binaries (Web APIs, MVC Controllers, logic, db access local to this plugin)
|_Views (has shared, _viewimports etc.. local to this module)
|_wwwroot (has static files local to this module)
|
|__Plugin2
|_Binaries (Web APIs, MVC Controllers, logic, db access local to this plugin)
|_Views (has shared, _viewimports etc.. local to this module)
|_wwwroot (has static files local to this module)
As you can see plugins are self contained (ex:- if you delete a plugin directory and restart the app) Any features related to that plugin will not work. (Also you can just zip the entire plugin directory and deploy it with some config file changes) Common features like User management/ Permissions etc. are provided to plugins via the main app.
As of now UI is rendered from server and WebAPIs are being called by the MVC Controllers and MVC controllers talk to Web APIs which I don't want to do when I am actually going to develop the final app.
My problem is I can move the UI part and do it as an Angular/React app. but then I can't use the plugin system that I have now. If I can do that, I should be able to break the angular/react components to different physical locations (plugin directories) and then be able to distribute those independently so that upon deployment client app will identify plugin related angular/react code and then load/run as necessary.
I couldn't find such example out there. They all have a separate Angular/React app. The other option I was thinking is to use RequireJs and AMD pattern where I can modularize my javascript code in different plugins. Main UI rendering will happen at server but client will take care of WebAPI calls getting data and binding. However this will not give me MVVM change detection which can be very useful.
So can I use Angular/React in such a way that I don't create a separate app, but use angular code in the existing app, create angualr/react modules in different plugin directories and get them to work together?
I have recently worked in a pure Scala-Play application and later in a pure AngularJS application. I'm very impressed with both and I'm wondering what is the sweet spot of combining the two frameworks together. Since the two frameworks can be complementary but also overlapping in different areas e.g. MVC and page routing, as far as I know these are some of the possibilities:
Single Page design, use AngularJS MVC-only and use Scala-Play as "dull" service layer backend with no full page refreshes.
Allow page reloads and each page reload becomes a different AngularJS root application. This seems quite flexible e.g. the client side is not bloatted with so much data for larger applications but better partitioned for the different use-cases. The downside is that I'd need different AngularJS MVC applications and I'm not even sure how to organize it as a project. Are there examples of this?
Typical server side Web App, use Play MVC-only and AngularJS for handling UI models for each separate page.
My choice of IDE for these types of architecture would of course be WebStorm but unfortunately I can't have all client-side (AngularJS and JavaScripts) and sever-side (Scala-Play) codes in a single project.
I believe that there is no the ultimate optimal architecture for combining Play and Angular. It depends on the specificity of the project, team etc.
The decision to develop UI part with Angular and the server side back-end with Play is very reasonable. Technically it may be done as following:
Development:
Both parts are developed as detached projects with the preferable IDE.
The client should have some entry point HTML page. It is reasonable to name it index.html, but is may be any other name.
For client-server integration do on the Play side as following:
Select a sub-folder under the play application root, which will serve as the "home" for the client files. The default solution is to use the folder public, since all files under it are automatically deployed.
All client files should be copied under the public folder. The files may be organized in any structure.
Add a route for the default URL as a route to the index.html. The argument path in the route should be the full path of the index.html relatively to the application root.
If index.html is directly in the public folder, the route is like this:
GET /defaultUrl controllers.Assets.at(path="/public", file ="index.html")
Add routing to the client files:
GET /*file controllers.Assets.at(path="/public", file)
Now the distribution package will include all the client files.
Putting of the client files into the public folder should be done automatically, for example by organizing the client directory structure and appropriate configuration of the client IDE.
You can find more examples in this post.
I have developed an app in Yii2 that is as a backoffice for a Travel Agency.
I have used the basic start template, and used gii (code generator) to create the CRUDS for the models (Hotels, HotelRooms, HotelImages, etc)
I also want to create two different front-end applications (one for retail, and the other for other agencies), and I thought to separate it from the backend (maybe an angular.js app) and get the info through REST could be a good approach.
So I want to use the API Rest based on the models made with gii.
How can I achieve this?
I have read this:
http://www.yiiframework.com/doc-2.0/guide-rest-quick-start.html
and It says that I have to switch the controllers.
Currently I have (generated with gii)
class HotelController extends Controller
and reading the guide it says that I have to use
class HotelController extends ActiveController
but if I make this change, the backoffice does not work anymore.
What would be the best approach?
Make another APIHotelController that extends ActiveController?
Can I merge (in some sort of way) with the gii generated controller?
Any other way to achieve this separation of layers (back-front) ?
Thanks!
The most elegant solution I know so far to build a REST API Yii2 web app without messing up with the existent code or the routes configurations is by building a REST API as a separate sub-application containing MVC elements by itself, which means in Yii's world : as a module.
You already have a working code, accessible within the web folder, it has it's own Entry Scripts within the index.php file, its own server configurations (.htaccess file if using apache) and its own app configurations within the config folder to which your app will depend before parsing your URL's and rendering your view files within their related controllers and models.
So the idea is to create a new web folder, lets call it api for example, with its own Entry Scripts file, own server configurations, own config folder and own controllers extending the ActiveController class.
Then both, your web app and api service will share the same models files to validate, store or retrieve data.
Your app will move from this basic template based structure :
+ assets
+ config
+ controllers
+ models
+ views
+ web
...
To this new structure (from the tutorial linked below) :
+ web
+ config
+ controllers
...
+ api
+ config
+ modules
+ v1
+ controllers
.htaccess
index.php
You will need then to register your api service as separate module in order to work and being initialized by Yii.
Then you will use different URLs to get to your web app and related views or Rest api and related json/xml outputs respectively within http://[your_path]/web/controller/action or http://[your_path]/api/v1/controller/action (url structure depends on your web and api configs).
In order to implement the necessary code, here is a great step by step tutorial. It uses the basic template structure and follows yii's api versionning approach (the v1 folder).
I have a site that is developed by multiple developers that has multiple pages. Each "page" initializes angular by calling angular.module(etc).
My question is, all pages share some modules, and some pages use specific modules. What is the best practice to achieve this? Do I trust that developers will insert the correct modules that will be needed across the site (i.e. Google Analytics) or do I create one call that is shared my all pages that loads ALL the modules. And is there a way to do both? Such as, initilize the modules that are needed across all the pages and then, load specific modules dynamically on their respective pages.
I would make one global module that is loaded by each individual app, much like modules like 'ngAnimate' are loaded... the global module could then initialize functionality common to all pages, such as Google analytics.
This requires some policing on all developers involved, but this is a good practice via code reviews, etc.
example page:
angular.module('individualPage', [
'globalModule',
'customPageModule'
]).config(
// etc
);
global module:
angular.module('globalModule', [
'googleAnalytics'
]).config(
// etc
);
I have a site that is developed by multiple developers that has multiple pages. Each "page" initializes angular by calling angular.module(etc).
My question is:
All pages share some modules, and some pages use specific modules. What is the best practice to achieve this?
I do not know the best practice when dealing with multiple pages.
IMHO, creating multiple pages is a BAD practice nowadays. I think of
web applications(SPAs) that have different views and states not web
sites with disjointed pages. So if you choose to go the
SPA(single page application) way, you can load all you core/common
modules before the application bootstraps. Views/pages that need
specific modules can lazy load them using something like
oclazyload.
Do I trust that developers will insert the correct modules that will be needed across the site (i.e. Google Analytics) or do I create one call that is shared my all pages that loads ALL the modules.
I can't say much about the question of trust as I do not know your
developers well enough. In general, developers are never to be
trusted, they will do anything that seems to work, high five
themselves and call it a day. The idea is to "Trust but verify", you
don't have to wire tap their phones or read their emails but never
ever take your eyes off the main git or svn repository. Anywhere, If
you were to use oclazyload as I suggested above for a SPA, you would
only need to worry about dynamically loading 'view/page' specific
modules which the developers can configure themselves.
And is there a way to do both? Such as, initilize the modules that are needed across all the pages and then, load specific modules dynamically on their respective pages.
Yes, yes, there is ... SPA approach that I have already outlined
above. I would also recommend using angular-ui-router for the
states and views approach. The idea is to design your application
whilst thinking of it as a desktop or mobile thick client that has
state transitions and so forth.
I have a site that is developed by multiple developers that has multiple pages.
Each "page" initialises angular by calling angular.module(etc).
My question is, all pages share some modules, and some pages use specific modules.
What is the best practice to achieve this?
Do I trust that developers will insert the correct modules that will be needed across the site (i.e. Google Analytics)
or do I create one call that is shared my all pages that loads ALL the modules.
And is there a way to do both?
Such as, initialise the modules that are needed across all the pages and then, load specific modules dynamically on their respective pages.
I would start off by defining what a page is.
Are you talking about a SPA or a more traditional setup a la client transitions between pages with a regular <a href="/page"> and
the server serves the client a piece of HTML?
If the latter is true, then I would urge you to reconsider your underlying approach to your Angular application.
The best (or rather, preferred) way of doing things would be to serve the client a single piece of HTML, and then
transition between pages (from now on I will refer to them as states), by using either ngRoute, angular router (2), or
better yet - ui-router.
For the remainder of my answer I am going to assume that you are in fact working with a SPA.
What is the best practice to achieve this?
As it stands, I would go out on a limb and say that there is no best practice defined for the case you present.
There are a ton of ways to do it, none of which have been officially recommended by the core development team / community standard as far as I'm concerned.
You could go with webpack-angularjs-lazyload, requirejs (angular-requirejs-seed), requirejs (angularAMD), SystemJS among others. Pick your poison of preference!
Do I trust that developers will insert the correct modules that will be needed across the site (i.e. Google Analytics)
or do I create one call that is shared my all pages that loads ALL the modules.
If code contained in an angular.module is required across the site, I would attach it to the main application module.
Such as:
/** Define core functionality that _is_ essential to the application running as expected **/
angular.module('core-module', [ 'route-definitions', 'http-interceptors', 'google-analytics' ]);
/** Inject the core functionality into a bundle module **/
angular.module('main-bundle-module', [ 'core-module' ]);
/** Bootstrap the bundle module as your application **/
angular.bootstrap(/* DOM element */, ['main-bundle-module']);
Now, whenever someone creates a new module for a specific state, they will need to inject said module into the main-bundle-module (barring lazy loaded modules).
As such, the core functionality will always be supplied and available, in your case Google Analytics. In a sense, you just tore down
the trust barrier.
Taking a step back, lets for a moment assume that you are not working with a SPA - and you are in fact re-initialising the
angular application on each page transition (bad move). How would you ensure that the required functionality is always present?
Decorating the angular.module method.
Note: This is not officially supported, be wary of what you are doing. Also it defeats the purpose of modularisation in my opinion, but I'll
showcase the way(s) of doing it.
You could go two ways here I reckon:
Kill the execution of JS if the required module is not a part of the developers module definition.
"Bad cop."
This would catch the 'untrusted developer' in his/her tracks during development, so as to ensure they are following the project standard.
Assist the 'untrusted developer' by automating the task of requiring the module.
"Good cop." (well, sort of...)
This would ensure that the required module is always present, albeit in every module.
"Good Cop"
(function(angular) {
// The always required module(s).
var ALWAYS_REQUIRED = ['cs.core'];
// Keep a reference to the original angular.module function.
var originalFn = angular.module;
// Keep track of registered modules.
var registered = {};
angular.module = function (name, dependencies, configFunction) {
var loaded;
// Ensure that we are always working with an array of dependencies.
dependencies = dependencies || [];
// If the module has not already been registered
if (!registered[name]) {
// Ensure that the required modules are available.
ALWAYS_REQUIRED.forEach(function (required) {
if (dependencies.indexOf(required) === -1) {
dependencies.push(required);
}
});
// Register the module and store it in the registered object for future reference.
loaded = registered[name] = original(name, dependencies, configFunction);
} else {
// Do not re-register the module, simply load it as per 'angular.module('name_of_module')';
loaded = original(name);
}
// Return the loaded module.
return loaded;
};
})(angular);
"Bad Cop"
(function(angular) {
var ALWAYS_REQUIRED = ['cs.core'];
var originalFn = angular.module;
angular.module = function (name, dependencies, configFunction) {
ALWAYS_REQUIRED.forEach(function (required) {
if (dependencies.indexOf(required) === -1) {
throw new Error('You need to add ' + required + ' to ' + name + '\'s module dependencies!');
}
});
return originalFn(name, dependencies, configFunction);
};
})(angular);
That's two ways of killing the trust issue, but in doing so we've introduced code that;
Is not very pretty.
Is definitely not very well tested / battle proven.
Kills modularisation to boot.
And is there a way to do both?
Such as, initialise the modules that are needed across all the pages and then, load specific modules dynamically on their respective pages.
I would say the best way to do so is to:
Take the steps necessary to convert to a SPA.
Write some documentation for all your developers so as to bring them up to speed on the requirements of the project.
Create a standalone module containing the required core functionality and attach it to your ng-app/angular.bootstrap module.
Get ui-router, ui-router-extras and ocLazyLoad to allow for lazy loaded module/state/component definitions.
Have a look at some of the following links for inspiration/ideas on what fits your specific project:
ocLazyLoad-SystemJS-Router by #lookfirst
ng-jspm-seed#futureStateConfig by #kasperlewau
ocLazyLoad#with-your-router
ocLazyLoad+requirejs plunker
webpack vs browserify #stackoverflow
angular + webpack slides
tl;dr
Convert to a SPA.
Bootstrap the application with core functionality supplied.
Write some documentation for untrusted developers.
Better yet, build trust. :)
Lazy load state-specific modules when needed.
You can try browserify to load and build your javascript in single file.
With this you can easy minify the javascript code, and also you can load nodejs modules like events and more, but this is not your question about.
I need to create a module that will be used by different applications.
Each application is using its own set of vars (application name, REST urls, ...).
How do I set inner variable, by the hosting application, in module (or in service)?
Need to init these parameters as soon as possible as the application loads.
Thanks.
In your module have a provider. That way the client application can bootstrap configuration.
Read the "Provider Recipe" section on angular's site for an example of this.
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.