how to reduce time to display a list (json object fetched from database) in a table using *ngFor structural directive? - angular5

I am fetching a json object using web API, and displaying it in a table using *ngFor Structural Directive. However the problem is, though the object gets fetched instantly and display in console, it takes time to display it in table. I want the object to be displayed in table instantly as soon as it comes into console.
component.html file;
<tr *ngFor="let data of orgData" id="{{data.Id}}">
<td hidden><input type="number" id="1" value="{{ data.Id }}"></td>
<td>{{ data.OrganisationName }}</td>
<td>{{ data.ContactPerson }}</td>
<td>{{ data.ContactPersonHPNo }}</td>
<td>{{ data.ContactPersonEmailId }}</td>
<td>{{ data.SubscriptionStatus }}</td></tr>
component.ts file;
ngOnInit() {
// making use of web API
this.httpService.get('http://url/StudyExAPI/GetOrganisations?Id=').subscribe(
data => {
this.orgData = data as string[];
// console.log(this.orgData);
},
(err: HttpErrorResponse) => {
console.log(err.message);
}
);}

If you have a lot of objects inside your array, you can use the virtual scroll viewport provided by package #angular/cdk which render only visible elements.

Related

using v-for for rendering

i am build a laravel vue appliction and i want to render my categories from my categoriescontroller
heres my categorycontroller
public function index()
{
// geeting all the categories
$categories = Category::all();
return response([ 'categories' => CategoryResource::collection($categories), 'message' => 'Retrieved successfully'], 200);
}
heres my category.vue file
<tbody>
<tr v-for="category in categories.data" :key="category.id">
<td>{{ category.id }}</td>
<td>{{ category.name }}</td>
<td>11-7-2014</td>
<td><span class="tag tag-success">Approved</span></td>
</tr>
</tbody>
here also is my method in my category.vue file for loading my categorys
loadCategory(){
axios.get("api/category").then(({ data }) => (this.categories = data));
},
now i dont have any errorrs but its not rendering my categories either. please in need of assistance
since you're destructing the response inside the callback parameters .then(({ data }) you should directly render categories without .data field :
<tr v-for="category in categories" :key="category.id">
as you sending the data from laravel using api resource the actual data is loaded inside a data property. so you should extract it like this in the axios call
loadCategory(){
axios.get("api/category").then(({ data }) => (this.categories = data.categories.data));
},
yes i just quickly want to clearify , just incase someones sees this issue both answers from #Boussadjra Brahim and #sazzad was helpfull . so what i did was
first i took #Boussadjra Brahim suggestion and changed this
<tr v-for="category in categories" :key="category.id">
<td>{{ category.id }}</td>
<td>{{ category.name }}</td>
<td>11-7-2014</td>
<td><span class="tag tag-success">Approved</span></td>
</tr>
then i still got errors until i tried #sazzad suggestion but took out the .data attribute giving me this
loadCategory(){
axios.get("api/category").then(({ data }) => (this.categories = data.categories));
},
hope this helps someone also thanks again

Vue Reactivity issue, setting array element property

I'm having a reactivity issue in following example. I can't find what I'm doing wrong. Am I setting the vue data correctly or do I need to do something else?
I have an object model as follows;
export default {
data () {
return {
filteredSkillTiers: [{
name: '',
categories: [{
name: '',
recipes: [{ name: '', profit: '' }]
}]
}],
recipeFilterText: ''
}
}
In created() method, I fill this filteredSkillTiers with real data. When I check as console.log(this.FilteredSkillTiers), it seems fine.
And, in my template, I have a button with #click="CalculateRecipe(i, j, k) which seems to be working perfect.
Here is my template;
<div
v-for="(skilltier,i) in filteredSkillTiers"
:key="i"
>
<div
v-if="isThereAtLeastOneFilteredRecipeInSkillTier(skilltier)"
>
<h3> {{ skilltier.name }} </h3>
<div
v-for="(category,j) in skilltier.categories"
:key="j"
>
<div
v-if="isThereAtLeastOneFilteredRecipeInCategory(category)"
>
<v-simple-table
dense
class="mt-3"
>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">{{ category.name }}</th>
<th class="text-left">Click to Calculate</th>
<th class="text-left">Estimated Profit</th>
</tr>
</thead>
<tbody>
<tr v-for="(recipe,k) in category.recipes" :key="k">
<template
v-if="recipe.name.toLowerCase().includes(recipeFilterText.toLowerCase())"
>
<td>{{ recipe.name }}</td>
<td>
<v-btn
dense
small
#click="CalculateRecipe(i, j, k)"
>
Calculate
</v-btn>
</td>
<td>{{ filteredSkillTiers[i].categories[j].recipes[k].profit }}</td>
</template>
</tr>
</tbody>
</template>
</v-simple-table>
</div>
</div>
</div>
</div>
And here is my method;
CalculateRecipe (skilltierIndex, categoryIndex, recipeIndex) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('profitResult')
}, 50)
}).then((profit) => {
this.filteredSkillTiers[skilltierIndex].categories[categoryIndex].recipes[recipeIndex].profit = 'new Profit'
console.log(this.filteredSkillTiers[skilltierIndex].categories[categoryIndex].recipes[recipeIndex].profit)
})
},
When I log to console, I can see that I'm modifying the object correctly. But my updated value is not reflected in the rendered page.
There is this thing I suspect, if I update an irrevelant component in this page (an overlaying loading image component), I can see that rendered table gets updated. I want to avoid that because updating a component returns me to top of the page.
<td>{{ filteredSkillTiers[i].categories[j].recipes[k].profit }}</td>
This profit property seems not reactive. I'm really confused and sorry that I couldn't clear the code more, Thanks.
Here's the article describing how exactly reactivity works in Vue 2.x. And yes, (in that version) you should never update a property of tracked object directly (unless you actually want the changes not to be tracked immediately).
One way (mentioned in that article) is using Vue.set() helper function. For example:
Vue.set(this.filteredSkillTiers[skilltierIndex]
.categories[categoryIndex].recipes[recipeIndex], 'profit', 'new Profit');
You might consider making this code far less verbose by passing recipe object inside CalculateRecipe function as a parameter (instead of those indexes), then just using this line:
Vue.set(recipe, 'profit', promiseResolutionValue);

Vue checkbox not updating with data change

Having problems with my data in Vue updating the UI, but only checkboxes seem to have this problem.
I'm trying to allow only one checkbox to be checked at one time, therefore I'm setting all the checkboxes to false upon clicking one of them. However, this works in the data but isn't rendered correctly in the checkboxes.
HTML:
<table class="buildings-modify--table table-full table-spacing">
<thead>
<tr>
<th>Name</th>
<th>Primary</th>
<th>Address</th>
<th>City/Town</th>
<th>Province</th>
<th>Postal Code</th>
<th>Phone</th>
<th>Active</th>
</tr>
</thead>
<tbody>
<tr v-for="landlord in selectedLandlords" :key="landlord.id">
<td>{{ landlord.name }}</td>
<td>
<input type="checkbox" :value="landlord.is_primary" #change.prevent="makePrimaryLandlord(landlord)">
</td>
<td>{{ landlord.address }}</td>
<td>{{ landlord.city }}</td>
<td>{{ landlord.province}}</td>
<td>{{ landlord.postal_code}}</td>
<td>{{ landlord.phone }}</td>
<td>{{ landlord.is_active ? "Yes" : "No" }}</td>
</tr>
</tbody>
Vue Code:
export default {
data: function() {
return {
selectedLandlords: []
}
},
methods: {
makePrimaryLandlord: function(landlord) {
this.selectedLandlords = this.selectedLandlords.map(item => {
item.is_primary = false; return item});
}
}
}
}
Only the checkbox appears to have an issue. If I change say the name, or a text value with a filtered array setting them all to a specific value they change but the checkboxes data change doesn't reflect in the UI, however the data in Vue is correct.
From Official docs
text and textarea elements use value property and input event;
checkboxes and radiobuttons use checked property and change event;
select fields use value as a prop and change as an event.
Use :input-value instead of :value
I can't exactly make it up from your code, but are you sure the property is_primary is available in the objects on load of the viewmodel? So for example this.selectedLandlords.push({ Name:'John', is_primary: false, ...}) should contain the is_primary field to make it reactive.
If not, you should use Vue.set(item, 'is_primary', false) to make it reactive.
try using the key attributes to re-render the checkbox.
reference: https://michaelnthiessen.com/force-re-render/

Using v-for and v-show to hide/show additional text inside an p element

I have this html code
<tr v-for="(help, index) in helps">
<td scope="row">{{ help.ID }}</td>
<td scope="row">{{ help.Date | formatDateWithTime }}</td>
<td>
<p #click="fullTextFun(index)" v-show="help.FullText">{{ help.Text.substring(0,16) + '...' }}</p>
<p #click="fullTextFun(index)" v-show="!help.FullText">{{ help.Text }}</p>
</td>
</tr>
I want to be able to show the full text when someone click on the current p element. This is my vue function
fullTextFun: function(index) {
this.helps[index].FullText = !this.helps[index].FullText;
},
It doesn't work. I also tried to do it using this code
<span #click="fullTextFun(help)" v-show="help.FullText">{{ help.Text.substring(0,16) + '...' }}</span>
fullTextFun: function(item) {
item.FullText = !item.FullText;
},
But again without any luck. It seems the v-show function don't care about the status of help.FullText
When I load the data I don't have FullText variable in my helps array. I don't know if this is the problem
This is what it is inside my helps variable when first loaded
[{"ID":"2","Date":"2019-05-15
17:27:29","Text":"randomText"},{"ID":"4","Date":"2019-05-17
09:53:59","Text":"some text"}]
It might be Vue reactivity issue.
fullTextFun: function(index) {
this.helps[index].FullText = !this.helps[index].FullText;
this.helps = JSON.parse(JSON.stringify(this.helps))
}
Basically Vue only updates if you change the reference.
When you update this.helps[index].FullText, this.helps still points to old object reference, and Vue can't recognize the change.
Another solution is using Vue.set
Vue.set(this.helps[index], 'FullText', !this.helps[index].FullText)
You can read more at Vue document

Vue.js 'if - else' not working with font awesome

Here is my code:
<tr v-for="(i, index) in items.data">
<td>{{ index }}</td>
<td>{{ i.name }}</td>
<td>{{ i.producer }}</td>
<td><font-awesome-icon v-if="i.recieved" icon="check" /><font-awesome-icon v-else icon="times" /></td>
</tr>
I received data from server, where i.recieved can be true or false, but when I compile this, I always see only icon times. It is very strange because I have 4 received fields with true on my server.
So what do you think I should do?
In console I see only one error:
error: Elements in iteration expect to have 'v-bind:key' directives (vue/require-v-for-key) at src/views/Home.vue:15:7:
> 15 | <tr v-for="(i, index) in items.data">
| ^
As the warning says, you need to add a key. https://v2.vuejs.org/v2/guide/list.html#key
Try this.
<tr v-for="(i, index) in items.data" :key="index">
<td>{{ index }}</td>
<td>{{ i.name }}</td>
<td>{{ i.producer }}</td>
<td><font-awesome-icon v-if="i.recieved" icon="check" /><font-awesome-icon v-else icon="times" /></td>
</tr>
Your console errors comes because your v-for has no key defined.
It is recommended to provide a key with v-for whenever possible, unless the iterated DOM content is simple, or you are intentionally relying on the default behavior for performance gains.
Since it’s a generic mechanism for Vue to identify nodes, the key also has other uses that are not specifically tied to v-for, as we will see later in the guide.
More Info about the Key