I'm new to ionic, we're trying to build a reader app that downloads documents in html from a service and then displays them. I know how to modify html that is part of the ionic application itself, but the documents we download are displayed inside the ionic app. We want to add a search function that finds and highlights all occurrences of the entered words. We find them and highlight them by wrapping them in a span that has a css class that sets a yellow background. But it doesn't reflect the changes in the app.
The document is downloaded from the service and then wrapped in a div, here's what we have do far, this is a snippet from the document.html for the document page in the app and is where the downloaded content lives:
<ion-content id="content">
<div [ngClass]="isNight ? 'night' : 'day'">
<div [ngClass]="isSingle ? 'single' : 'double'">
<div id="inputText" class="document" [innerHtml]="document | keepHtml" [ngStyle]="{'font-size': fontSize+'em' }"></div>
</div>
</div>
</ioncontent>
The javascript that highlights the hits is, I've left out the search box stuff since it's really just boilerplate, the highlight method is where the problem lies:
highlight(keyword) {
var inputText = document.getElementById("inputText");
var innerHTML = inputText.innerHTML;
inputText.innerHTML = innerHTML.replace(new RegExp(keyword, 'g'), "<span class=\"keyword\">" + keyword + "</span>");
}
If the user searched for "the", for example, after the highlight() method runs we should see every "the" in the document highlighted in yellow. But we don't. If we remove the "| keepHtml" from the div for the document, search works.
If we display the document html using an alert from the typescript method we see our changes, but if we run the javascript console in the browser and look at the html in the Dom of the browser, the changes we made are not there.
I know I'm missing something obvious or fundamental to ionic/angular but so far I can't see what it is. Maybe I'm so far off that nobody can help me but I thought I'd take a shot. Thanks for understanding.
Adding the keepHtml code:
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
/**
* Generated class for the KeepHtmlPipe pipe.
*
* See https://angular.io/api/core/Pipe for more info on Angular Pipes.
*/
#Pipe({ name: 'keepHtml', pure: false })
export class KeepHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {
}
transform(content) {
return this.sanitizer.bypassSecurityTrustHtml(content);
}
}
We discovered the problem; ionic/angular thought our html was unsafe. We started sanitizing it before we added it back in and it works.
We actually found two solutions, one was to run a sanitizer in the page class (excuse me if my angular-speak is not quite right, I've lived through so many "Waves of the Future" that at this point in my career they're all starting to run together) with bypassSecurityTrustHtml and the other, surprisingly was to add the modified HTML back into this.document instead of element.innerHTML.
The second solution seems like black magic, I'm not sure I understand why it works. I actually prefer the first solution.
Related
I'm working on a VScode extension for my school open source project.
I was wondering there was a way to detect text input to the activeTextEditor window?
For example if someone were to paste a string in, could I grab detect that string similar to an OnInput in JavaScript? A setup would be spell checking or doing a replacement for commands, similar to Visual Studios' intellisense you type prop +tab +tab it auto generates code.
Let me know if you've heard of something that might work. Thanks
The api you are looking for is vscode.workspace.onDidChangeTextDocument. This event is fired whenever a document that VS Code knows about changes. It is fired with a TextDocumentChangeEvent object which includes all the text changes:
import * as vscode from 'vscode'
export function activate() {
vscode.workspace.onDidChangeTextDocument(changeEvent => {
console.log(`Did change: ${changeEvent.document.uri}`);
for (const change of changeEvent.contentChanges) {
console.log(change.range); // range of text being replaced
console.log(change.text); // text replacement
}
});
}
If you only care about changes to the active editor's text, just check to see if changeEvent.document matches the active editor's document.
I'm turning here as a last resort. I've scoured google and I'm having troubles coming to a solution. I have a form with a textarea element that allows you to type html in the area and it will render the HTML markup live as you type if you have the preview mode active. Not too different from the way StackOverflow shows the preview below a new post.
However, I have recently discovered that my functionality has a vulnerability. All I got to do is type something like:
</textarea>
<script>alert("Hello World!");</script>
<textarea style="display: none;">
And not only does this run from within the textarea live, if you save the form and reload said data on a different page this code still executes within the textarea on said different page but unbeknownst to the user; to them all the see is a textarea (if there is no alert obviously).
I found this post; Live preview of textarea input with javascript html, and attempted to refactor my JS to the accepted answer there, because I noticed I couldn't write a script tag in the JSFiddle example, though maybe that's some JSFiddle blocking that behaviour, but I couldn't get it working within my JS file.
These few lines is what I use to live render HTML markup:
$(".main").on("keyup", "#actualTextArea", function () {
$('#previewTextArea').html($('#actualTextArea').val());
});
$(".main").on("keydown", "#actualTextArea", function () {
$('#previewTextArea').html($('#actualTextArea').val());
});
Is there a way this can be refactored so it's safe? My only idea at the moment is to wipe the live preview and use a toggle on/off and encode it, but I really think this is a cool feature and would like to keep it live instead of toggle. Is there a way to "live encode" it or escape certain tags or something?
In order to sanitise your text area preview simply replace all the < and > with their html character code equivalents:
function showPreview()
{
var value = $('#writer').val().trim();
value = value.replace("<", "<");
value = value.replace(">", ">");
$('#preview').html(value);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="writer" onInput="showPreview();">
</textarea>
<br/>
<hr/>
<div id="preview">
</div>
Edit: Actually, I think this solution is a little cleaner, and makes the below code unnecessary. In the velocity page all that is needed is to take advantage of the Spring framework. So I replace the textarea with this like so:
#springBindEscaped("myJavaObj.textAreaText" true)
<textarea id="actualTextArea" name="${status.expression}" class="myClass" rows="10" cols="120">$!status.value</textarea>
This paired with some backend Java validation and it ends up being a much cleaner solution.
But if you want a non-spring/ velocity solution, then this below works just fine
I cobbled together a quick fix as my main purpose is to eliminate the ability for others to execute scripts easily. It's not ideal, and I"m not claiming it to be the best answer, so if someone finds a better solution, please do share. I created a "sanitize" function like so:
function sanitize(text){
var sanitized = text.replace("<script>", "");
sanitized = sanitized.replace("</script>", "");
return sanitized;
}
Then the previous two event handlers now look like:
$(".main").on("keyup", "#actualTextArea", function () {
var textAreaMarkup = $('#actualTextArea').val();
var sanitizedMarkup = sanitize(textAreaMarkup );
$('#actualTextArea').val(sanitizedMarkup);
$('#previewTextArea').html(sanitizedMarkup);
});
// This one can remain unchanged and infact needs to be
// If it's the same as above it will wipe the text area
// on a highlight-backspace
$(".main").on("keydown", "#actualTextArea", function () {
$('#previewTextArea').html($('#actualTextArea').val());
});
Along with Java side sanitation to prevent anything harmful being stored in the DB, this serves my purpose, but I'm very open to a better solution if it exists.
I have some html for an alert message box. Using interpolation, I can re-use the same html to show multiple different alert messages. It looks like this: <p>{{ myAlertMessage }}</p>
Now I want to show a longer alert message containing line breaks. However, I cannot seem to modify the interpolated component property (which is a string) in any way which will introduce line breaks.
For example, using </br>, or spacing the text across several lines in the component code (contained in either parentheses or back ticks), or the new line code (
). None of these work to produce a line break in the text when the alert message is shown.
I would prefer not to have to add further property bindings just to cater for this particular use case.
Thanks in advance.
Just use
<span [innerHTML]="myAlertMessage"></span>
the innerHTML property will interpret your html code.
The solution, for Angular v2.1.1 at least, is to use [innerHTML]="myAlertMessage". It isn't necessary to use "bypassSecurityTrustHtml" for line breaks or lists to work.
You can use a pipe like
import { Pipe, Sanitizer } from '#angular/core';
#Pipe({name: 'safe'})
export class SafeHtml {
constructor(private sanitizer:Sanitizer){}
transform(html) {
return this.sanitizer.bypassSecurityTrustHtml(html);
}
}
and use it like
<span [innerHTML]="myAlertMessage | safe"></span>
where myAlertMessage can contain <br> or <p>
See also In RC.1 some styles can't be added using binding syntax
Try to use \n in your myAlertMessage text.
Something like that: "\n Some alert text \n Newline of the alert text";
And use a <pre> html tag in your component HTML file like that:
<div>
<p><pre>{{ myAlertMessage }}</pre></p>
</div>
I'm running into a bit of a brick wall with how to render content inside some dynamic HTML content which I don't control, with Ember v1.13.1. In this particular scenario, I'm getting a navigation head, and navigation foot from a service call and putting them in my component:
export default Ember.Component.extend({
// I don't control these. They come from somewhere else
bodyStart: '<div class="header">...</div><div class="contentBody">',
bodyEnd: '</div><footer>...</footer>',
});
So, in my component template, I'm trying to do something like:
{{{bodyStart}}}
{{yield}}
{{{bodyEnd}}}
I would expect the yield content to be placed inside a <div class="contentBody"> element, but it's not. Instead content body is closed before the {{yield}}, and the bodyEnd closing div is ignored.
It's possible I'm just missing something obvious. Any ideas on how to solve this would be greatly appreciated.
Cheers
I believe what happens is that each {{{variable}}} is constructed independently and independently inserted into the DOM, which leads to unclosed DOM nodes that gets closed. The only way I can think of is to include the template compiler and recompile the template with bodyStart and bodyStop.
App.ContentWrappedComponent = Ember.Component.extend({
startBody: '',
endBody: '',
layout: function(){
return Ember.HTMLBars.compile(
this.get('bodyStart') +
'{{yield}}' +
this.get('bodyEnd')
);
}.property('bodyStart', 'bodyEnd')
});
You also need to add to your Brocfile.js:
app.import('bower_components/ember/ember-template-compiler.js');
JSBin: http://emberjs.jsbin.com/ticituxapa/3/edit?html,css,js,output
I'm new to ember and am struggeling with the typical "how would one do that"-Problem. What I've got is fairly simple and I know how to do it, but my way is so complicated that I do not think it's correct.
The case:
<ul>
<li>{{link-to top-level}}</li>
<li>{{link-to another-top-level</li>
<ul class="submenu">
<li>{{link-to submenu</li>
</ul>
</ul>
What should happen is:
When a route is clicked, the corresponding list element should become active.
When a submenu is clicked the corresponding upper ul-element should get the class open
It's a fairly simple case with jQuery, but I understand that this is not scalable and abstracted and stuff.
Therefore I started with this approach:
Create a controller / template construct for the entire navigation to handle it's state (there are some other things I need to check as well, so it came in handy).
since ember adds the active class to the anchor tag I created a component to observe that:
Like:
export default Ember.Component.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews').anyBy('active');
}.property('childViews.#each.active')
});
Replacing the li elements with {{linked-list}} does indeed work.
But what next? Do I need to add another component to watch the component to watch the build in behaviour of active links? Do I have to write dedicated MVC-Classes for all the DOM Elements?
There has to be a simpler way, I think. I already created a whole lotta files for such a simple behaviour that I'm thinking I'm totally on the wrong track.
My gut feeling is: That is view logic and the view should just observe a few states in the template and that's it.
What's the leanest approach to the problem?
I don't know if I understand your question right, but why you want to add the class open to the corresponding upper element? It automatically get active assigned. And with correct CSS it should work as expected.
I have created a small example demonstrating what I mean. Please have a look and let me know, if that's the solution for you or what's your problem with this solution.
http://emberjs.jsbin.com/wifusosadega/7/edit
EDIT
Here is a Bootstrap flavored version: http://emberjs.jsbin.com/wifusosadega/9/edit .