Simple Blog post display with Firestore - javascript

I'm creating a bare-bones blog with Angular 5 and a Firestore database. I'm able to save to the database and retrieve the data for display. The problem is when I save the text area value to the database. It doesn't retain the line breaks and spacing I would want and therefore upon retrieval, it's just a single line string. I'm keeping it super simple until I work out these types of issues I'm unfamiliar with.
Any suggestions a simple and effective way of fixing this issue?
This is how I'm currently displaying and saving the data.
ngOnInit() {
this.createForm();
this.postCollectionRef = this.afs.collection('posts');
this.postCollectionList = this.postCollectionRef.valueChanges();
}
addPost(val) {
this.postCollectionRef.add({ title: val.title, body: val.body });
}
<div>
<ul>
<li *ngFor="let post of postCollectionList | async">
{{ post.title }}
<br> {{ post.body }}
</li>
</ul>
</div>

To me this looks like an HTML/CSS issue -- <li>, like most of HTML elements, collapse whitespace sequences by default. Try wrapping {{ post.body }} inside a <pre> tag, or add style="white-space: pre;" to <li> elements.

Related

Vuejs seems to struggle with v-model when many items on the page

I have a simle page containing a form at the top to submit some data and a list below that form as in the image:
The list is filled by data from an api and each object has 4 properties (not many for a simple list!). Currently the list has 130 elements in total. Now if I try to write something in the textarea, it happens to be very slow (2-10frames/sec). The more items added to the list, the slower it gets. The form at the top has a simple data object called form like below:
form: {
description: '',
expired: '',
applicationId: ''
}
The funny part it that there is no single data shared between the list and the form at the top, so, they should operate independently. When I commend out the list section the textarea is very fast again.
Below is the code to render out the list:
<b-card no-body class="box-shadowed">
<b-card-body v-if="appKeys.length === 0">
<h5>Seems there is no key available.</h5>
</b-card-body>
<b-list-group v-else flush>
<b-list-group-item
v-for="(key, index) in appKeys"
:key="key.id"
>
<div class="d-flex w-100 justify-content-between">
<p class="text-danger h6"><span class="text-dark">{{ index + 1 }} - </span> <i>{{ key.id }}</i></p>
<div>
<b-button variant="primary" title="Edit" #click="openEditModal(key.id, key.description, key.expired)">
<b-icon :icon="'pencil'"/>
</b-button>
<b-button variant="danger" title="Delete" #click="deleteKey(index, key.id)">
<b-icon :icon="'trash'"/>
</b-button>
</div>
</div>
<template v-if="key.expired">
<b-badge variant="info">Expires on: {{ key.expired | formatDate }}</b-badge>
<br/>
</template>
<small>
<b>
<i>Created by: {{ key.createdBy }}</i> on {{ key.created | formatDateTime }}
</b>
<br/>
{{ key.description }}
</small>
</b-list-group-item>
</b-list-group>
If I remove the v-model="form.description" from the textarea the problem goes away again.
I thought that the problem might be the <b-form-textarea> component from bootstrap-vue but the same problem happens with a simple textarea input as well.
I tried checking the vue dev-tools panel and I can see that frames are dropping every time I have many items in the list but I dont know what else to check.
Does anybody have any idea why this might happen ? It is a vuejs limitation on how many items it can handle or I have some bottleneck code somewhere ?
EDIt
I Can use v-once and the page would be fast again but I need that reactivity when I add new elements to update the list below..
There is something what you have faced. Here says:
Any change to a template’s dependency will result of re-render of the
whole virtual dom of that component.
While the list becomes larger and larger, there will be more components to be re-rendered. This results slowness. One solution is that you can create a child component for the html part where the list is. So, any input change in <b-form-textarea> in the parent component will not trigger the child component re-render.

Angularfire: Display key instead of value in HTML for a Firebase Object

I am using AngularFire for a project. My firebase Object is of the form
{
mainKey: {
key1:value1,
key2:value2
},
mainkey2: {
key3:value3
}
}
I have entered the data in such a way that I need to display both the value and the key in different columns of a table row.
I am able to display value using {{key}} expression but what should I do if I have to display the key itself?
Is storing data in this way a wrong practice?
You can use the $id property.
<ul>
<li ng-repeat="message in messages">
Message data located at node /messages/{{ message.$id }}
Message text is {{ message.text }}
</li>
</ul>
I've found the way to do so in angularJS docs
<ul>
<li ng-repeat="(key,value) in object"> {{key}}> </li>
</ul>
This would give me all the keys listed.

AngularJS: how can I pass data to a tooltip without having to put in a character with the data?

I am using Angular Tooltips by 702kb - GitHub and I am trying to pass some data to my tooltip like this:
<div class="ngrs-handle ngrs-handle-min" ng-controller="HRAController">
<a tooltips tooltip-html="{{ data.pressureHigh }} / {{ data.pressureLow }}" tooltip-show-trigger="mouseover click" tooltip-hide-trigger="click" tooltip-side="top" tooltip-scroll="true" class="custom-tooltip"> </a>
</div>
<div class="ngrs-handle ngrs-handle-max" ng-controller="HRAController">
<a tooltips tooltip-html="{{ data.cholesterol }}" tooltip-show-trigger="mouseover click" tooltip-hide-trigger="click" tooltip-side="top" tooltip-scroll="true" class="custom-tooltip"> </a>
</div>
The first one, {{ data.pressureHigh }} / {{ data.pressureLow }}, works fine because of the "/". This is simply coincidence because I actually need the "/" in there.
The second one, {{ data.cholesterol }}, gives an empty tooltip (tooltip doesn't show up at all because it thinks theres nothing in it), but if I were to add a character, such as adding a percentage after the data, like {{ data.cholesterol }}%, then the tooltip would show up and show the data now with a trailing "%". The problem is there shouldn't be any trailing characters so this is giving me trouble.
Is there any way I can put in just the data and not be required to have a character in there with it for the data to be properly outputted?
I tried using both tooltip-html="" and tooltip-content="" (both can be seen on the GitHub link above) but both have the same functionality as far as my problem goes.

AngularJs - Holding value of for loop string to use elsewhere

I've come a bit stuck in my angularjs project.
I run a for loop to query some nested JSON data and output it in 3 different variables inside an ng-repeat. So it makes up a title where i have the control over the elements that make up the title {{ number }} {{ shots }} {{ goals }}.
However, my knowledge of angularjs is stretched here because when I click on one of the events (from the ng-repeat list) it gives me a new tab, but I want to bring the title of that event to the new tab.
I can't call it as a scope variable as the last variable is still being held in there. I thought about assigning it as a new variable.. but was unsure how to actually do that in angularjs.
Here is the code i'm working with:
<li ng-repeat="event in events.events">
<div ng-if="actionType(event)" >
{{number}}
{{shots}}
{{goals}}
</div>
<a class="showPlayer" ng-click="showPlayer(event)">
View more stats
</a>
</li>
my angularjs is just a standard for loop which looks for values inside the js and assigns them as variables.
Any advice is very much appreciated.
EDIT: 24 hours later and I still can't crack this (very frustrating).
I'm not sure if there is a way to grab the string from the ng-click and clone that?
I don't want to have to run another check for the title when I already have the information, surely there is an 'angular' way to do this??
Please try this I am not sure but it might help you
<li ng-repeat="event in events.events">
<div ng-if="actionType(event)" >
{{event.number}}
{{event.shots}}
{{event.goals}}
</div>
<a class="showPlayer" ng-click="showPlayer(event.number,event.shots,event.goals)">
View more stats
</a>
</li>
Try using $rootScope.
Define a rootscope variable where event is handled and write your title in html like you did. {{title}}
$rootScope.title = "example";

Select different child JSON element with select input in AngularJS

I am printing a list of food types by making an API call per category. The food in the categories have this JSON structure:
{"uid":"56",
"title":"Nussbrot",
"code":"X 39 2000000",
"final_factor":"0",
"sorting":"0",
"unit":[
{"title":"Scheiben",
"gram":"45",
"max":"100"
},
{"title":"Messerspitzen",
"gram":"250",
"max":"12"}
]
}
I am looping through & printing the values out into a template. No problem. I am printing the "unit" values into a select box:
<option ng-repeat="title in food.unit">{{ title.title }}</option>
And I am currently printing out the grams & title of the first unit in each food like this:
<div class="max">Max is: {{ food.unit[0].max }}</div>
<div class="grams">Grams is: {{ food.unit[0].gram }} </div>
How can I make this dynamic, so that I am printing out the max & grams of the currently selected unit?
Here's my Plunkr.
Angular makes dealing with options and selected options very easy. You should stop thinking in terms of indexes or value. With angular you can bind the entire object, so there's no need to look it up. For example you could do the following for your select:
<select ng-model='selectedUnit' ng-options="unit as unit.title for unit in food.unit"></select>
Let me briefly explain the expression for ng-options
unit in food.unit means we will iterate over the food.unit array storing each value in unit as we go along.
unit as unit.title means what we are putting in the ng-model whenever the user selects an item is the entire unit object itself. The as unit.title tells angular to use the title of the unit as a display for the option.
What this ends up doing is that whenever the user selects one of the options, the entire unit object will be stored in the ng-model variable (in this case selectedUnit). This makes it really easy to bind it elsewhere. For example you can just do:
<div class="unit">Unit is: {{ selectedUnit.title }}</div>
<div class="max">Max is: {{ selectedUnit.max }}</div>
<div class="grams">Grams is: {{ selectedUnit.gram }} </div>
In angular, if you find yourself dealing with indexes or ids and then looking things up by id or index then you are typically doing it wrong. One of the biggest advantages of using angular is how easy it is to deal with objects, and you should really take advantage of it.
For example, I often see newbies doing something like
<li ng-repeat="person in persons">{{person.name} <a ng-click="savePerson(person.id)">Save</a></li>
And then in their code they use the id to look up the person from an array:
$scope.savePerson = function(id){
var person = persons[id];
$http.post('/persons/'+id, person);
};
This kind of lookup is almost always unecessary with angular. You can almost alway just pass the person right away:
<li ng-repeat="person in persons">{{person.name} <a ng-click="savePerson(person)">Save</a></li>
And then have the click handler take the person:
$scope.savePerson = function(person){
$http.post('/persons/'+person.id, person);
};
I know I strayed a bit from your original question. But hopefully this makes sense and helps you write things more simply using the "angular way"
Her is the plunkr for your example:
http://plnkr.co/edit/lEaLPBZNn0ombUe3GPa9?p=preview
you can fist of all handle the selected item with the ng-selected:
http://docs.angularjs.org/api/ng.directive:ngSelected
<select>
<option ng-repeat="title in food.unit" ng-selected="selectedIndex=$index">{{ title.title }}</option>
</select>
<div class="max">Max is: {{ food.unit[selectedIndex].max }}</div>
<div class="grams">Grams is: {{ food.unit[selectedIndex].gram }} </div>
This should propably work ;) Havn't tryed it yet!

Categories