The problem is that I have lots of HTML templates that include < script > tags with AngularJS code in them. My current project is using Angular 8, and one critical part of it consists basically in usign all the AngularJS templates that I have inside this new project doing as little work as possible (due to the fact that in the future other users will have to be able to add new templates to the project).
I have to be able to add this HTML AngularJS templates to my current project. For that I am using ngUpgrade, but it has been quite misleading and I have not been able o achieve this.
Here is one of the templates, simplified as much as possible for this question:
HTML
<body>
<script>
angular.module('myApp', [])
.controller('myController', function ($scope, $timeout) {
$scope.local = 'es';
BigFunction($scope, $timeout);
});
var texts = {
title: {
en: 'A simple title',
};
// other constanst go here
function BigFunction(scope, timeout) {
scope.texts = texts;
scope.$watch('content', function(end, ini){
// some logic when 'content' changes
}, true);
// more logic
}
<script>
<div>
<!-- Lots of tags using ng-bind and other AngularJS tools -->
</div>
</body>
I have to integrate it touching it as little as possible. I don't know if I should separate the logic of the script from the HTML, or if I could import it as it is right now and work. Take into account that I have no previous experience working with AngularJS, I am an Angular 2+ developer.
If I have to separate the logic from the template, where do I put the angular.module? And the constants?
I would suggest you using Web Components
Basically you would have to wrap the AngularJS application as a Web Component and then import it to your Angular 8 app. You would end up with something like this
<my-angularjs-app></my-angularjs-app>
It could be imported in any component in your Angular 8 application.
There is no official solution on how to do it, but there are good examples there. I would suggest you following this one:
UPGRADING WITH WEB COMPONENTS: FROM ANGULARJS TO ANGULAR
Once you are there scroll down until you see
Exposing AngularJS Components As Custom Elements
After you manage to bundle up the AngularJS application as a web component, having the whole application in one script (main.js), you would have to copy it to your Angular 8 app.
In your Angular 8 app, create folder assets/web-components/my-angularjs-app
Copy the file you generated from AngularJS (main.js) to a folder you just created
Import the file in angular.json - Add this to scripts: assets/web-components/my-angularjs-app/main.js
Your AngularJS app is now imported but there is one more thing we have to do before you can use it in your components. You have to let Angular know that there will be some selectors which Angular might not be aware of. To do this, go to your app.module.ts and add schemas: [CUSTOM_ELEMENTS_SCHEMA]
You can now use your AngularJS app anywhere you prefer by using the selector you declared (This part is in the link I posted). <my-angularjs-app></my-angularjs-app>
I recommend the ng-book, it has a full chapter about hybrid apps. Note the UpgradeAdapter in the book is deprecated. But many concepts are still valid.
NgUpgrade is the official package to do what you want.
The first thing you must do is to put the script in a proper file. And not in the HTML. Then you can do serious coding.
You must follow the NgUpgrade doc to make the 2 apps run.
https://angular.io/guide/upgrade#bootstrapping-hybrid-applications
you need one bootstrap for AngularJS:
angular.bootstrap(document.body, ['heroApp'], { strictDi: true });
And one bootstrap for Angular 8:
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule);
You will notice the NgModule has a reference line towards AngularJs.
The code they give is for JIT compilation.
A production build in AOT compilation will look like this:
import { enableProdMode } from '#angular/core';
import { platformBrowser } from '#angular/platform-browser';
const { AppModuleNgFactory } = require('../aot/src/app/AppModule.ngfactory');
enableProdMode();
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
--
You must know a directive in AngularJS corresponds to what is meant by component in Angular 8.
You have 2 options.
inject Angular 8 components in ANgular JS directives
inject Angular JS directives in ANgular 8 components.
I recommend to upgrade ANgularJs Components inside Angular 8 components. Like that in the future you converge towards moving to Angular 8 only.
It will require some work to copy each AngularJS component in an Angular 8 component.
To reduce work, run Angular JS as it is. And put Angular 8 directives outside of the templates of AngularJS.
<div id="angularjs-root"></div>
<div my-unaltereted-angular8-component></div>
It may be I am wrong and you cannot put them deparately. But it is worth a try.
Myself, I inject DOM elements dynamically from Angular8 to a JQuery application. But you cannot do that in AngularJS since it has lifecycle for all nodes. HOwever, with some carefulness, removing change detection rendering for the node, a huge tree of nodes from Angular 8 could be injected in the template of an ANgularJs template, and this wihtout downgrading.
This is what I use.
addComponentToElement({ component, element }: { component: any, element: any }) {
// Create a component reference from the component
const componentRef = this.componentFactoryResolver
.resolveComponentFactory(component)
.create(this.injector);
// Attach component to the appRef so that it's inside the ng component tree
this.appRef.attachView(componentRef.hostView);
// Get DOM element from component
const domElem = (componentRef.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement;
// Append DOM element to the body
element.appendChild(domElem);
// detect changes
componentRef.changeDetectorRef.detectChanges();
}
The AppModule will need to add each component inside entryComponents:
entryComponents: [ InjectedCOmponent1m InjectedCOmponent2]
Related
Hi I have an existing project which uses alot of knockoutjs.
Im attempting to add vuejs to the project and slowly move everything over to vuejs in time.
Ive added vue to a wrapper element which contains the whole page and created a single vue component.
My aim is to use that component anywhere on the page but not break my knockout code.
The problem im having is my knockout bindings are not being defined when I apply the vue el tag to the main element that wraps the page and the ko.applyBindings scripts. (vue elements work ok but all ko scripts throw an error )
I am using complied templates and the vue-cli with webpack-simple.
Im pretty new to vuejs but I think this is happening because vue is trying to parse the knockoutjs code inside the page html
console error:
ReferenceError: Unable to process binding "slideVisible: function(){return !isShowBillingForm() }"
Message: isShowBillingForm is not defined
at slideVisible (eval at parseBindingsString (knockout-3.3.0.js:61), <anonymous>:3:65)
at init (slidevisible.js:15)
at knockout-3.3.0.js:65
at Object.u (knockout-3.3.0.js:35)
at knockout-3.3.0.js:65
at Object.o (knockout-3.3.0.js:10)
at g (knockout-3.3.0.js:65)
at h (knockout-3.3.0.js:63)
at k (knockout-3.3.0.js:63)
at h (knockout-3.3.0.js:63)
If anyone could help me that would be amazing
(Ive also added the vue cdn to the header and added in vue markup into the page directly and their are no conflicts) so I guess its something to do with the compiled templates?
I've added Vue to a wrapper element which contains the whole page and created a single Vue component.
I believe that's the issue. When migrating to VueJS from legacy Knockout you will have to pick and choose where you want to use Vue.
The way Vue parses the DOM is very different compared to Knockout. It uses a concept of virtual DOM, so the final DOM output is only valid HTML and no framework specific markup. That inherently will break any KO code bound to it.
So the approach I'd recommend is finding pieces of the app you want to convert to Vue and then instantiate Vue inside a Knockout VM (or with plain JS) like this:
import Vue from 'vue';
import MyVueComponent from 'libs/components/my-component.vue';
const propsData = {my: props};
const myComponent = Vue.extend(MyVueComponent);
new myComponent({el: '#some_element', propsData: propsData})
Also important, make sure you tell KO not to process #some_element that Vue is bound to by using the stopBinding handler as described here.
Hope that helps!
I'm using angular 4 with bootstrap 4. I need to create a multi-level sign-up page. Now I'm facing issue to include the JavaScript file into angular.
import '../../../assets/js/js/multiform.js'
used this to import the file into my #Component ;
I need to call a function when the user click "NEXT / BACK"; But my function not calling
Forget about importing pure javascript plugins. It's not a good practice in Angular 2+. You can create your own component for it, or use the open source examples that are already angularized. I think something like this is what you are looking for: https://github.com/cwun/angular-multi-step-wizard.
Good luck.
We are looking at writing a new app using React but will need some existing Angular Directive that are specific for our business, for example a modified Angular Date Picker for example. There are many components so we won't be able to rewrite them all.
I am wondering if anyone has experience or knows the effort or feasibility of this?
The only article I've managed to find on this so far has been. Most resources I find mention going the other way from an Angular App with added React. http://softeng.oicr.on.ca/chang_wang/2017/04/17/Using-AngularJS-components-directives-in-React/
There's a library called angular2react that makes possible reuse angularjs code inside react components. You can see if it fits your needs. :)
I have written a simple demo app showcasing how to achieve it: react-angularjs-bridge
The basic steps are as follows:
Create a react component that is going to host the angular component in the DOM. This component should always render a simple html element (eg. a HTMLDivElement) so that react does not get confused when reconciling the virtual DOM with the real DOM.
After the react component was mounted componentDidMount, create a shadow root under the rendered HTMLDivElement and initialise the angular application under it. This will ensure that the angularJS component is encapsulated and is not affected by the react application and vice-versa
Click here for a demo. Check how the react application styles do not impact the styles of the angular component.
Independently of Angular, ref is a way to go: it gives to access to an underlying DOM node and the ability to manipulate the DOM inside that node in any way you like, including AngularJS. Your changes will stay - at least until react clears the component altogether.
You can create a ref in react by
<divOrAnyOtherDom ref={((el) => {this._elem=el;}).bind(this)}></...
(for the full description and alternate ways to the same end see here: https://reactjs.org/docs/refs-and-the-dom.html#callback-refs)
Anyway, as you now have a DOM node, you can start working with it, i.e. adding angular to the mix inside componentDidMount. Sadly, this is not really obvious - here's a way that has worked for me in the past (moduleName needs to be a module you have put in place already with your logic):
let baseElement = angular.element(this._elem);
let templateElement = angular.element(templateSource);
angular.injector([ 'ng', moduleName ]).invoke(function($compile, $rootScope) {
let cmp = $compile(templateElement);
scpe = $rootScope.$new(false);
scpe.varname = whateverYouHave; # point is: you can set scope variables
cmp(scpe);
baseElement.append(templateElement);
scpe.$digest();
});
Now you have angular inside the DOM node react gave you.
(I've taken this from a slightly different integration: Vaadin to AngularJS, but the principle is the same: Vaadin, just like react, gives you a DOM node. When you're interested in the full code: https://github.com/akquinet/vaangular/blob/master/vaangular/src/main/resources/de/akquinet/engineering/vaadin/vaangular/angular/NgTemplate.js)
Plus: For any callbacks from the angular component, you'll likely have to use setState et al.
I've been looking to develop a method for loading modules and/or components into an AOT-compiled Angular 4 application and been stymied by a variety of solutions that never quite seem to get me where I want to be.
My requirements are as such:
My main application is AOT compiled, and has no knowledge of what it is loading until runtime, so I cannot specifically identify my dynamic module as an entry component at compile time (which is explicitly necessary for the 'dynamic' component loading example presented on Angular.io)
I'd ideally love to be able to pull the code from a back end database via a GET request, but I can survive it simply living in a folder alongside the compiled site.
I'm using Webpack to compile my main application, breaking it into chunks - and so a lot of the SystemJS based solutions seem like dead ends - based on my current research, I could be wrong about this.
I don't need to know or have access to any components of my main application directly - in essence, I'd be loading one angular app into another, with the dynamically loaded module only perhaps having a few tightly controlled explicit interface points with the parent application.
I've explored using tools like SystemJsNgModuleLoader - which seems to require that I have the Angular compiler present, which I'm happy to do if AOT somehow allowed me to include it even if I'm not using it elsewhere. I've also looked into directly compiling my dynamic module using ngc and loading the resulting ngfactory and compiled component/module, but I'm not clear if this is at all possible or if so - what tools Angular makes available to do so. I have also seen references to ANALYZE_FOR_ENTRY_COMPONENTS - but can't clearly dig up what the limitations of this are, as first analysis indicates its not quite what I'm looking for either.
I had assumed I might be able to define a common interface and then simply make a get request to bring my dynamic component into my application - but Angular seems painfully allergic to anything I try to do short of stepping outside of it alltogether and trying to attach non-angular code to the DOM directly.
Is what I'm trying to do even possible? Does Angular 2+ simply despise this kind of on the fly modification of its internal application architecture?
I think I found an article that describes exactly what you are trying to do. In short you need to take over the bootstrap lifecycle.
The magic is in this snippet here.
import {AComponentNgFactory, BComponentNgFactory} from './components.ngfactory.ts';
#NgModule({
imports: [BrowserModule],
declarations: [AComponent, BComponent]
})
export class AppModule {
ngDoBootstrap(app) {
fetch('url/to/fetch/component/name')
.then((name)=>{ this.bootstrapRootComponent(app, name)});
}
bootstrapRootComponent(app, name) {
const options = {
'a-comp': AComponentNgFactory,
'b-comp': BComponentNgFactory
};
https://blog.angularindepth.com/how-to-manually-bootstrap-an-angular-application-9a36ccf86429
I have recently started working on ANGULARJS in which I was encountered a case where I need some guidance to go through. Am implementing a message section in my application(ASP NET MVC - ANGULARJS).Currently I have implemented the message section for a specific module under a particular ng-app and under particular ng-controller. Now I need the same functionality to be used inside another module. It's like duplicating the same code again that ng-app under that ng-controller which was not a good approach. I just wanted like and plug and play kind of approach for my ANGULARJS code.
I have used 2 service,1 directive under that particular ng-app and some functions inside a particular controller. All I want is to make these one a common code and to be used inside under any ng-app and ng-controller.
Is this possible? If so how can I achieve.
Let me know if the query was unclear
You said you used 2 service, 1 directive, and controller etc for your messaging feature. If you want to re-use it across various applications, you need to bundle all of this as a module. for example:
angular.module('customMessaging', [])
.controller('messagingCtrl', function(messenger, messageManager) {
....
})
.directive('messagingDir', function() {
return {
controller: 'messagingCtrl'
...
}
})
.service('messenger', function() {
...
})
.service('messageManager', function() {
...
})
Now you can specify this as a dependency of any of your angular applications as shown below to access the directive, services and controller anywhere in the app:
angular.module('myfirstApp', ['customMessaging']);
angular.module('mySecondApp', ['customMessaging']);
Thanks for the suggestions. I have moved the message related functions such as services, directive and controller related functions into separate JavaScript file . And invoked this JavaScript file where I need message related functionalities.
Let us say that JS as Message.JS . In Message.JS I have used the app variable(Instantiated app from the JS Specific to the page globally).
JS specific to that page
var app = angular.module('appname',[]);
app.controller(...)
Message.JS
I have used the same app in my message.JS since message controller falls under that app.
app.service(...)
app.controller('messagecontroller',[]...)
When ever I need to invoke a function inside MessageController I will use Broadcast in angular to achieve this.For more info http://www.dotnet-tricks.com/Tutorial/angularjs/HM0L291214-Understanding-$emit,-$broadcast-and-$on-in-AngularJS.html
Regards,
Selvam.M