Angular Accordian with short description - html

I am trying to use accordian in angular which can show a shor description of the content. And when you click on it, it should show the full description. So basically i need to slice the content and show it in the header. But i am unable to do so. Below is my code, as you can see I have announcement.title in teh heading. However i need to add part of announcement.content in the heading. So i need to slice the content for upto a few characters and show it in the heading. Then when someone click on it, i show the full content. I am not sure how to achieve this. Can someone help ?
HTML code :
<accordion>
<div *ngFor="let announcement of announcements; let i = index">
<accordion-group [heading]="announcement.title">
<div class="announcement-body" [innerHTML]="announcement.content"> {{ announcement?.content }}
</div>
</accordion-group>
</div>
</accordion>
TS file:
announcements: any[] = [];
ngOnInit() {
this.showAnnouncements();
}
showAnnouncements() {
this.configService.getAnnouncements().then(
(data: any) => {
for (const key in data) {
this.announcements.push({
title: data[key].title,
content: data[key].content
});
}
}
).catch((error) => {
console.log(error);
});
}
enter image description here
I also need the title and the sliced content to appear in the heading on different lines with different font.

You can append some portion of your content to the title using the string slice method. For example:
this.announcements.push({
title: data[key].title + data[key].content.slice(0,10),
content: data[key].content
});
This would add the first 10 characters of your content string to your title.
If you wanted some fixed length, you could do something like:
title: data[key].title + data[key].content.slice(0,MaxLength - data[key].title.length)
For the last case, you'd have to ensure that your title is always below max length or protect the slice from going negative.

Related

Show entity definitions inside title of divs

Having this HTML block in a React app:
<div className="my-class" title={myValue}>
<div>{myValue}</div>
</div>
The content of myValue is MyCompany™.
The inner div is showing the content correctly.
But the title, which appears when hover, replaces ™ with ™ and shows it like this: MyCompany™.
Why is this happening for the title? Is there a way to fix it?
You can use dangerouslySetInnerHTML, which is not ideal, but it is fine. So, the code should look like that:
let title = ["MyCompany", <span>™</span>]
<div className="my-class" dangerouslySetInnerHTML={{__html: title }}>
<div>{title }</div>
</div>
If your entity is dynamic, like you told me in the comments, then we can do the following trick:
const parseASCII = (string) => {
const htmlTextArea = document.createElement('textarea');
textarea.innerHTML = string;
return htmlTextArea.value;
}

Why are my <br> tags showing in my browser display for angular project

I have text that is being saved from a textarea. I save it to the database. I now want to display this text onto another page after submit. I would like to keep the same format as the original text input.
This is what shows up on the browser after redirecting to the other page.
Im replacing br with --- or else it will create actual breaks in this text.
--- equals br
"M <---><---/> O <---/><---/>C<---/>Hub<---/>"
How do I display onto the page without the br tags? I have seen this work in other circumstances but unsure what I am doing wrong.
I have been able to use the .replace method to replace the enter's with . The only thing though, the tag is showing up in the display.
x.component.ts
getMessages() {
this._messageBoardService.getMessages().subscribe(
(data: any) => {
this.msgList = data
this.msgList[0].message = this.msgList[0].message.replace(/\n/g, "<br />")
})
}
x.component.html
<h3>{{ msgList[0].title }}</h3>
<p> {{ msgList[0].message }}</p>
The expected output without the breakpoints is
M
O
C
Hub
Because this is how angular works. It prevents from injection "bad html".
To print html you will need to use DomSanitizer
Change your code
this.msgList[0].message = this.msgList[0].message.replace(/\n/g, "<br />")
to
this.msgList[0].message = sanitizer.bypassSecurityTrustHtml(this.msgList[0].message.replace(/\n/g, "<br />"))
then template
<p [innerHTML]="msgList[0].message"></p>
BUT for security reason I would not do it I'd better do it with css
p.break-newline{
white-space: pre-wrap
}
add class
<p class="break-newline"> {{ msgList[0].message }}</p>
And now you dont need to modify your message in code.
If it is just for display purposes add a css property that displays your line breaks
<p class="message">{{ msgList[0].message }}</p>
.message { white-space: pre-line; }
in which case your api fetching has no operations
getMessages() {
this._messageBoardService.getMessages().subscribe(
(data: any) => {
this.msgList = data
})
}
OR
else you can do in angular is use innerHTML property
<p [innerHTML]="msgList[0].message"></p>

How to make links clickable in a chat

I have a chat on my website that reads from a JSON file and grabs each message and then displays it using Vue.js. However, my problem is that when a user posts a link, it is not contained in an anchor tag <a href=""/>. Therefore it is not clickable.
I saw this post, and I think something like this would work, however, I am not allowed to add any more dependencies to the site. Would there be a way for me to do something similar to this without adding more dependencies?
Code for displaying the message.
<p v-for="msg in messages">
<em class="plebe">
<b> [ {{msg.platform.toUpperCase()}} ]
<span style="color: red" v-if="msg.isadmin">{{msg.user.toUpperCase()}}</span>
<span style="color: #afd6f8" v-else="">{{msg.user.toUpperCase()}}</span>
</b>
</em>:
{{msg.message}}
</p>
In a situation like this, its preferred to write a custom functional component.
The reason for this is the fact that we are required to emit a complex html structure, but we have to make sure to properly protect against xss attacks (so v-html + http regex is out of the picture)
We are also going to use render functions, because render functions have the advantage to allow for javascript that generates the html, having more freedom.
<!-- chatLine.vue -->
<script>
export default {
functional: true,
render: function (createElement, context) {
// ...
},
props: {
line: {
type: String,
required: true,
},
},
};
</script>
<style>
</style>
We now need to think about how to parse the actual chat message, for this purpose, I'm going to use a regex that splits on any length of whitespace (requiring our chat urls to be surrounded with spaces, or that they are at the start or end of line).
I'm now going to make the code in the following way:
Make a list for child componenets
Use a regex to find url's inside the target string
For every url found, do:
If the match isn't at the start, place the text leading from the previous match/start inside the children
place the url inside the list of children as an <a> tag, with the proper href attribute
At the end, if we still have characters left, at them to the list of children too
return our list wrapped inside a P element
Vue.component('chat-line', {
functional: true,
// To compensate for the lack of an instance,
// we are now provided a 2nd context argument.
// https://vuejs.org/v2/guide/render-function.html#createElement-Arguments
render: function (createElement, context) {
const children = [];
let lastMatchEnd = 0;
// Todo, maybe use a better url regex, this one is made up from my head
const urlRegex = /https?:\/\/([a-zA-Z0-9.-]+(?:\/[a-zA-Z0-9.%:_()+=-]*)*(?:\?[a-zA-Z0-9.%:_+&/()=-]*)?(?:#[a-zA-Z0-9.%:()_+=-]*)?)/g;
const line = context.props.line;
let match;
while(match = urlRegex.exec(line)) {
if(match.index - lastMatchEnd > 0) {
children.push(line.substring(lastMatchEnd, match.index));
}
children.push(createElement('a', {
attrs:{
href: match[0],
}
}, match[1])); // Using capture group 1 instead of 0 to demonstrate that we can alter the text
lastMatchEnd = urlRegex.lastIndex;
}
if(lastMatchEnd < line.length) {
// line.length - lastMatchEnd
children.push(line.substring(lastMatchEnd, line.length));
}
return createElement('p', {class: 'chat-line'}, children)
},
// Props are optional
props: {
line: {
required: true,
type: String,
},
},
});
var app = new Vue({
el: '#app',
data: {
message: 'Hello <script>, visit me at http://stackoverflow.com! Also see http://example.com/?celebrate=true'
},
});
.chat-line {
/* Support enters in our demo, propably not needed in production */
white-space: pre;
}
<script src="https://unpkg.com/vue#2.0.1/dist/vue.js"></script>
<div id="app">
<p>Message:</p>
<textarea v-model="message" style="display: block; min-width: 100%;"></textarea>
<p>Output:</p>
<chat-line :line="message"></chat-line>
</div>
You can watch or write computed method for the variable having url and manupulate it to html content and then use v-html to show html content on the page
v-html

Show text inside div but treat it as a background image

I'm trying to create a div which has the initial text loading... inside of it, and can later be overlapped by new elements that are loaded into it. Here's what I'm dealing with:
<div class="results" style="position:relative;" *ngIf="showResults">
<!-- show 'loading...' before the results load, which would then overlap this text -->
<div stlye="position: absolute; z-index: -1;">Loading ...</div>
<!-- angular2 loop to asynchronously load results -->
<div *ngFor="let loc of searchLocations | async" (click)="selectLocation(loc)" class="search-result">
{{ loc.name }}, {{ loc.adminName1 }}, {{ loc.countryCode }}
</div>
</div>
But when I run this, here's what I get something like
so my 'loading...' text has it's own boundry, when i want those proceeding elements to overlap ontop of that text. How can I accomplish something like this?
You could create a boolean for the loader.
export class YourClass(){
isLoading:boolean = true;
constructor(){
// Not sure how you are implementing your GET Request
// So no point in trying to replicate that, just where ever
// you assign searchLocations to an object
// fetch data logic ends with => {
isLoading = false;
}
}
}
Then in your html you just need to add an *ngIf
<div style="position: absolute; z-index: -1;"
*ngIf="isLoading">Loading ...</div>
You could even even make the loader a component to use anywhere, or you could hook into the showResults variable, as long as you place the loading div outside the showResults div with an attribute of *ngIf="!showResults" , which might mean a bit of a style tweek.

AngularJs - how to support a textbox with hyperlinks

I'm new to Angular but I'm trying to implement a textbox that allows users to enter in links. I only want to support links, and otherwise I want to block all html from being presented as such. I could theoretically use something other than a textarea, but my requirements are that it must be bound to a variable in my scope (right now with ng-model) and I cannot accept html tags other than '< a >'
Here is my example plnkr
In the example, I would like the second seeded item to display as a link, blue and underlined. However, the third item should display as it is currently shown (without interpreting it as html).
HTML:
<textarea maxlength="160" ng-model="val.text"></textarea>
<div class="btn" ng-click="submit()">Submit</div>
<br><br>
<div ng-repeat="item in items">
{{display(item)}}
</div>
JS:
$scope.submit = function() {
if (!$scope.val.text) return
$scope.items.push($scope.val.text);
}
$scope.display = function(txt) {
return txt;
// something here? if txt contains <a> and </a> indicate
// that we should display as html
}