I have a service which is called after events which trigger page content changes. This service currently inspects all the viewable HTML on the rendered page for key words and then creates links to a glossary where those key words are used. The page content comes from many sources, including various components and external textual data. Initially this was done by finding all the elements and then searching and modifying the nativeElement.innerHTML which works fine on events that trigger a complete page refresh; in components where the text is based on template bindings, those bindings won't update after the innerHTML changes. I know modifying the innerHTML is bad...
I've tried using the root ViewContainerRef, and ViewRef as starting points but don't see way to access all the page content including content in multi-level child components. Additionally some of the content is added via router-outlet. I was hoping to either dynamically modify the templates, or the rendered content while allowing the component to still render the content when data changes and my service to post process again. Some components are from imported libraries, or receive their data directly, so modifying the component source code doesn't seem like the best option.
I found that by temporarily disabling, forcing change detection, then enabling the component on a data change, it would cause the component to be removed and recreated with working bindings. This is not an optimal solution, but did work as an initial fix. I'll attempt to try the above solution.
Using a MutationObserver you can subscribe to changes of text in the document, then you can manipulate the DOM in a way that doesn't break Angular, i.e. don't change the innerHTML of a node, but rather, insert a sibling with the new HTML and hide (don't remove) the original node.
I've written a stackblitz that demonstrates this.
https://stackblitz.com/edit/angular-highlighter2
EDIT I created an Angular library with all the code, see https://www.npmjs.com/package/ngx-html-highlighter
Related
I know the question looks like some others I could read but it's not the exact same issue in my opinion.
I have a "loading screen" (small piece of HTML) in my Angular application. This loading screen is present in three places :
When the application is not loaded yet (so inside the tag of the root component in the index.html : <app-root>my loading html</app-root>
When the router inside the root component is not yet ready to display the "final" component. (See answer here for more detail)
In the "final" component itself waiting for some data to be loaded from an HTTP service.
In the second and the third cases the "loading screen" could be in another component. But it's not possible for the first one since another component will only be displayed after the app is fully loaded and we want the first loading screen to be visible as soon the user get the index.html.
So for the moment I have this short "loading screen" HTML duplicated in multiple places.
I don't care if it's duplicated once built and delivered to the user but from a code point of view I want it to exist only once... (You know how it is, when someone will have to change the message it will be forgotten in the other places...)
I could use iframe (or object but W3C advise to use iframe instead) but people here want to avoid it at all cost so I think the code duplication will be preferred to this solution.
I could also have a small JS to do it (like this answer) but it feel wrong to add a "wild js" in an Angular app...
My question is : Do I have a way to include HTML file into another HTML file (like the "include()" in PHP) with some markup (like in this answer about Service Side Include) that could be resolved during the Angular compilation?
I mean the AOT compilation is already checking the HTML template so it could be quite easy...
Thanks in advance!
It's not in the compilation time, but a way to do something similar to what you are asking, is this:
You could have your "loading screen" html code as a component (for instance, app-loading-component), declared and exported inside a Shared Module.
Then, in the component 'X' in which you want to use it, you have to import the Shared Module in the section imports:[] of the module of that 'X' component, and used it in your HTML in the usual way:
<app-loading-component></app-loading-component>
I am trying to have node red go to my router IP and search through the HTML code to see whether a certain device is on the list. When I right click - inspect I can hover over the list I am interested in and see the HTML information I am looking for. When I use the HTML node it seems to only look through the view page source information, which does not have what I am looking for. I there a way to point the HTML node at a more specific element instead of the page source as a whole?
It sounds like the data in the page on your router might be dynamically generated using JavaScript.
This means that when the page is loaded it only has the outline and the rest is filled in by the code using XHResquests to a different URL that supplies the information.
In order for Node-RED to be able to extract the information from the page it would need to load the outline, then effectively run all the JavaScript. Libraries like PhantomJS
There is a contrib node that might be able to help node-red-contrib-nbrowser but the better approach would probably be to work out what URL the JavaScript is calling and calling that directly as the data is most likely to be in a format that is easier to process (e.g. JSON)
I am trying to have a single GAS project that changes its UI by serving up different HTML pages based on what the user clicks. I cannot figure out how to serve up different HTML from the script, replace the current browser page and retain state. Any help appreciated. Thank you.
I use two options:
Have a main page which has buttons or text areas with onchange set to a function which calls back to the server side and gets new page data, then replace the current page or a portion of the page, with the new page.
Pass parameters in the URL and have the server side doGet() parse the parameters and branch to load a given page based on these values.
I have used a combination of both of these effectively. Basically I have a div which has my "menu" and a div which is the section to be replaced. My menu changes and then data is sent back to the server to get the dynamic body. The HTML is returned and then I replace using innerHTML.
In the same code I offer the ability to pass menu values via the published URL. This allows me to go directly to some values if I so choose as I have a Google Site where we embed the script into pages and the menu selections may be specific to that page. It allows us to use an iFrame to show the web app and go directly to the pertinent interface.
With google.script.run you can run any script on the server from the html page. By communicating with the server you have access to PropertiesService which gives you the capability to store information between pages. Personally I like the HTML Service createHtmlOutput(html) because I can edit the html without having to edit a separate page.
I decided to answer your question here so that I could use the code section.
Question:
I am actually looking to avoid manipulating the HTML and serve up a
completely different HTML file stored in the project. How do I make
the page call the script again and replace itself with the new
content?
We I'm guessing that completely replacing the page is not really what you want because the user will suffer a page refresh. But you could create divs like this:
<style>#R01{display:none;}</style>
<div class="replaceable" id="R01"></div>
If you put all your replaceable content in divs like that then you can request content from the server via calls like this:
google.script.run
.withSuccessHandler(updateConversation)
.withFailureHandler(showStatus)
.getConversation();
and put the new content into the appropriate divs and then change the css with another pair and turn the old content off and the new content on. Thereby avoiding a page refresh. Don't forget to save the old data into the PropertiesService first. So I don't think changing the entire page is the way to go but I could be wrong. I think just changing some of the internal content will avoid the need for a total page refresh. If you want to change images you can avoid another download by using CSS Sprites
Ok, so I'm asking this to document my solution and will answer it myself but any improvements on my method are welcome.
The problem:
I need to render a report to both an html page embedded in an iframe (so it needs its own url) and to pdf.
The report includes an out of the ordinary graph which is not supported by any graph api I've seen.
My site uses the Joomla! CMS (V. 2.5.*) which means my backend is php and mysql.
I need the report to render without interference from the default template set on Joomla.
I'm running on a linux host, (basic LAMP).
Joomla uses the base tag in their html which screws up svg's url().
My solution involves the following: wkhtml2pdf, js, url parameters, and writing a joomla component.
I wrote a basic Joomla component with two site views, one that generated the report in html and one that displayed the report view in an iframe. So the report has a url to access it at, and I have a way to embed it. The component also had the js and css for generating the report extracted out to separate files for including in the media folder (but that was just for code cleanliness not functionality).
The report view had to do the following modifications to the page header to get the report to render right (save for the graph) otherwise the resources were loaded at the wrong times:
$document = &JFactory::getDocument();
$document->setBase('');//base tag screws up svg urls so get rid of it
$document->addscript('http://www.google.com/jsapi');
$document->addscript(JURI::root(true).'/media/com_metahealthzone/javascript/chart.js');
$document->addStyleSheet(JURI::root(true).'/media/com_metahealthzone/css/chart.css');
The url used in the iframe had to have ?template=system appended, tmpl=component didn't work as the css from the template was still applied.
To get the Google visualization to look right I added javascript to the end of the OnLoadCallback to grab the svg and add a linearGradient as a child of the defs tag with id="svg_grad" (why Google doesn't support gradients, I don't know) then grab all the elements in the graph that needed the gradient and set their fill to 'url(#svg_grad)'. Remember to use setAttributeNS because of the svg namespace, and make the gradient stop tags be children of the gradient not of defs.
I still have to do the tweaks for VML (stupid IE).
There were some issues with wkhtml2pdf that I had to overcome too. When you add an element to the DOM using js it doesn't behave like other browsers. You must keep the returned handle because you can't get the new element using document.getElement* type functions. Also you have to instruct wkhtml2pdf to wait for the js to run using --no-stop-slow-scripts or the graph won't show up (it's slow). I also had to impersonate the user's session by setting the cookie for wkhtml2pdf using the --cookie <name> <value> argument.
I am relatively new to Web Development, and I am messing with a Joomla template installation.
I managed to get firebug to make changes and Firediff to save them as a new CSS, and replaced the CSS file on the server.
What I would now like to do is edit the template elements, (mostly deleting unused template elements), on screen using firebug, and then save the changes to a new HTML document.
My trouble is that I cannot work out where the correct html is to replace on the server.
Only by using Dreamweaver I can ascertain that the file name is index.html, but there are many files called this on the server.
With the CSS file I was able to identify the server path and filename from the blue text in the right panel of firebug, in order to replace the CSS changes with the output from firediff.
Can I identify the location, (and ideally filename), of hte HTML document in the same sort of way from firebug?
You might want to read the template tutorials before removing anything from the template index.php. In general, you want the template to have provisions for every possibility that you might want. A well designed template will have the ability to display multiple columns in multiple positions as well as other positions to place banners, login boxes, weather widgets, or anything else you can think of. Then you simply code the template so that any position that is not being used does not get added to the final code of the page being displayed.
Official documentation - http://docs.joomla.org/Joomla!_1.5_Template_Tutorial
Once you have a good handle on how the template is supposed to work, the file you will be editing is in JOOMLA/templates/YOUR TEMPLATE/index.php
Be sure you get the whole module positions and collapsible module position thing before stripping anything out. A well thought out template will very rarely need any code editing, virtually everything should be done in the admin and with CSS page class and module class suffixes.