I have an AngularJS app combined with some legacy javascript that does DOM manipulation. The messy part - sometimes the legacy javascript inserts dom elements with AngularJS directives. Naturally AngularJS is not aware of this, and these directives are not compiled and linked to a scope. How to compile and link the directives on the new elements that are dynamically inserted?
Please don't tell me to re-write everything in angular, or wrap legacy stuff in angular, I can't touch that. My angularjs app is a plugin that runs on 3P websites to give extra functionality.
Options I have considered:
Just tear down the angular app and bootstrap it again each time - this is actually very difficult to do cleanly because there is no clean method to tear down the app: How to destroy an angularjs app?
Traverse the dom looking for uncompiled directives. I only have a handful of directives to look for (as attributes). Angular automatically places ng-scope class on elements where scopes are attached, so if this is missing I can compile the directive manually use the $compile service and link it to the scope.
At the moment I am working on the second method, but I keep thinking there must be a smarter way. Please does anyone have a better idea?
I should mention that my app receives a callback after any dom manipulation - but it would be better if I didn't need it.
Related
I'm very new to Vue and have been given a task of looking at creating some Vue widgets that could be embedded in a couple of existing non Vue legacy web applications. The idea is that we would create a library of these widgets which could be then embedded in either of the legacy applications and eventually we might migrate the entire apps to Vue.
I've been searching for the best way forward and I am a bit confused. I guess these are my questions:
Do I need to be thinking Web Components here or can the widgets be actual Vue applications that we embed somehow?
If the widgets should be created as Web Components is there any difference between using the Vue/web-component-wrapper or the vue-custom-element library?
Whichever option we choose can we make full use of features that you would use in any normal Vue application - Vue router, Vuex for state management etc (and can state be shared across those widgets)?
Would the widgets need to be fully styled or would it be best practice to leave all the styling of the components to the parent app (or a combination of the two)?
I've never done anything like this before (as you can probably tell!) so any guidance or advice or pointers to examples would be appreciated.
** Update **
I found this article which I think is the direction I need to go in https://itnext.io/vuidget-how-to-create-an-embeddable-vue-js-widget-with-vue-custom-element-674bdcb96b97
There are three distinct (but quite similar) cases:
web components
They are supposed to be an encapsulated web fragment. If you want, it's a smarter alternative to <iframe>s. Its main use case (and what it was designed for) is to display ads in a page and guarantee the host can't mess with its internal logic and rendering.
custom elements
These are, simply put, declared and registered custom HTML tags. The advantage of using them is being able to mark them as off-limits in any outer framework, stating: "this custom element is not one of your custom components, treat it as an HTML tag".
framework components
By default, modern JS frameworks (Angular, React, Vue) use this pattern internally: their internal components look like custom elements (case 2). But they are not. They are just internal conventions, without ever making it into the HTML markup output of the app.
Here's what happens internally: when the template is parsed, if an unknown HTML element is met, the framework assumes it's one of its registered components. If it is, the tag is not rendered. A new instance of that component is created and the tag is replaced with the contents of the component's template (or the result of its render function).
All of the above frameworks, when running into an unknown html tag that is not a registered custom component will issue a warning along the lines of "hey, did you forget to register this component?". Unless it's registered as a custom element (case 2) - in which case they treat it as as such: an HTML tag.
Vue handles all of the above with grace. What you choose for your widgets largely depends on context and desired end result.
Here are the answers to your questions:
Do I need to be thinking Web Components here or can the widgets be actual Vue applications that we embed somehow?
You shouldn't go with Web Components if you want to be able to style them from the context.
If the widgets should be created as Web Components is there any difference between using the #vue/web-component-wrapper or the vue-custom-element library?
Yes, there is. #vue/web-component-wrapper produces web components (encapsulated DOM framents).
vue-custom-elements declares and uses custom elements (custom HTML tags). Their content is HTML markup (not encapsulated). The advantage of using custom elements is being able to inform outer frameworks: don't treat this custom element as one of your own components, it's handled by something else (Vue, in our case). Treat it as HTML markup.
Whichever option we choose can we make full use of features that you would use in any normal Vue application - Vue router, Vuex for state management etc (and can state be shared across those widgets)?
Yes. Whichever option you choose, you're still using JavaScript (every widget/app has unrestricted access to the entire context). You can also inject dependencies into your widgets, allowing them to communicate (by modifying the same external dependency - a router, a state management module, etc...). This is pretty much the standard mode in which every Vue instance normally operates. In simpler words, a Vue (sub-)component can function without a parent component and is, essentially, a Vue app. (or, if you prefer, every Vue app is a Vue instance and all of its sub-components are also Vue instances).
Would the widgets need to be fully styled or would it be best practice to leave all the styling of the components to the parent app (or a combination of the two)?
It's entirely your code design choice. It's easy to scope CSS in Vue. But there are great advantages in styling from above (DRY-er code). Also, having styles coming from context means less CSS rules applying, although that hardly qualifies as a performance issue. Obviously, take into consideration the answer to the first question.
I want to use the Timeline from visjs.org in my AngularJS application. To render the individual cells I want to use an AngularJS template.
On the visjs.org site I can find an example of a Handlebars template, which is working fine.
However I want to use some AngularJS functionality (like i18n filters etc) so I prefer to have a "text/ng-template" template but I don't know how to configure visjs to use AngularJS templates.
Any ideas?
My primary suggestion to use create a directive or component in new angular version for visjs.
or use angular-visjs, a directive module developed by visjs team inoder to support angular projects. But you need to consider the following note from the developers
NOTE: This library is currently being refactored. The intention is make the directives simpler, removing the additional 'non-vis.js' related directives (such as time-board and time-navigation), and bring the DataSet factory in-line with the vis.DataSet such that the documentation for vis is fully (hopefully) applicable and consistent.
-The note was taken from ReadMe File on 4 th March 2017
For using template like handlebars you can refer another post from stack overflow itself
Resolve template in AngularJS similar to Handlebars?
I’m developing web app using Angular2. I use some external GUI libraries like: nanosSroller , qTip2. Problem is that some of them requires initialization like:
$('.panel').nanoScroller();
I’m not sure where to put this code. In my opinion best place would be tag in template file because this code is strictly view related but Angular2 removes all tags from templates so it’s not possible. I’ve ended up with custom service that is used inside ngAfterViewInit:
ngAfterViewInit() {
this.jsInitializer.initializeScroll('#user-panel');
}
I like this approach because I keep all GUI libraries dependencies in single service. What I don’t like about it is that I still have to pass manually css selector. Is there a better way to do this?
I'm not sure whether seeing these directives in plain text poses any particular threat to our app hacking or not. But if nothing else one can clearly see our model structure and functionality. If Angular removed those after bootstrapping it would at least make it a bit harder to access...
Two questions
Would it be possible to remove all custom ng- attributes from markup after Angular app boostrapped? Would the app still work as expected?
Would it be possible to dynamically add ng- attributes on DOM Ready using jQuery, and then remove them as per #1? If this required manual app bootstrapping so be it?
By having the possibility of the #2 somehow we could externalize app configuration and HTML markup would never show any declarative directives (just in the time between configuration-bootstraping-removal). But at least there would be none if user disabled Javascript.
Anything that you expose via Angular will have to be exposed in some way anyway to the client if the client is supposed to show some data. If you want to have an interactive client-side application, then it needs to have some form of client-side model and data structure and code working on those things. Whether you express them purely in procedural code in a .js file or embed part of the structure into the HTML markup hardly makes any difference. It does not make anything any more hackable which wouldn't already be hackable to begin with.
I would like to have JavaScript tightly integrated with my Backbone templates, and as the views reference which template to use, I believe this would be the best location to store the JavaScript, right? But how?
In particular, I have JavaScript which sets up the DOM when the view has rendered and ready (not an event). I originally set this script in the template but this was only triggered when the application was first loaded, when you navigate back to the view through Backbone's routers, the JavaScript wasn't triggered.
Templates are not meant to store your JavaScript - they are supposed to be dumb html templates to be rendered by your views.
When you load a template from a View, this view needs to bind with all DOM Elements that will interact with your application.
It is good practice if your view has only bindings to the DOM then, so it can trigger internal events.
Then you can create "controllers" that will listen to your view instance and interact with it, triggering methods to do some change in the DOM, or preparing collections and models to be used by your view.
However, you can use lots of other libraries to organize your code logic loading.
I like requirejs, but commonjs has a lot of useful options.
Putting the JavaScript inside a template is like putting it mixed with your HTML. It's not a good idea, because you are mixing logic with structure and your application will get hard to maintain once it grows.
I organize my code with MVC style, which makes it is simple to grow and test large applications. For example, if I want models/collections, they are in the MODEL/COLLECTION folders, the CONTROLLER folder will keep the logical parts of my application, the VIEW folder will keep the files that interact with the DOM and like Django I added a Template folder for the templates, which are as simple as possible.
Just to explain your problem:
When you load a template with a script, you will execute the script. After this, your template will be stored in the memory, it means that your script inside will never be triggered again. You can fix it, but you will do it wrongly. Your script can be triggered after the render by binding your view render method in Backbone.