There is one component "TableFields.vue", and two view files "Home.vue" and "Statistics.vue".
In component, I have changes: [] variable under data() object.
data () {
return {
startStopA: true,
startStopB: true,
initialValueA: 3,
initialValueB: 3,
randomNumbersArray: [],
randomSignA: '+',
randomSignB: '+',
signsArray: ['+', '-'],
intervalA: null,
intervalB: null,
changes: []
}
},
That changes array dynamically gets object from calculationsA() function.
calculationsA () {
this.randomSignA = this.signsArray[
Math.floor(Math.random() * this.signsArray.length)
]
this.randomSignA === '+'
? (this.initialValueA += this.randomNumbersArray[0])
: (this.initialValueA -= this.randomNumbersArray[0])
const d = new Date()
// console.log(d.toLocaleTimeString())
// console.log(this.randomNumbersArray[0])
// this.changes.push(this.randomNumbersArray[0])
// this.changes.push(d.toLocaleTimeString())
// console.log(this.changes)
const newChange = {}
newChange.field = 'A'
newChange.value = this.randomNumbersArray[0]
newChange.time = d.toLocaleTimeString()
this.changes.push(newChange)
},
How do I pass changes: [] from TableField.vue component to Statistics.vuepage, in order to code dynamic table with changes array objects data. I am not sure, do I need to create new component or this can be done without it. Basically, this is the working code from the TableField.vue component that is implemented for testing purposes and can be seen from Home.vue which is root url.
<div class="statistics">
<table>
<tr>
<th>Field</th>
<th>Value</th>
<th>Time</th>
</tr>
<tr v-for="item in changes" :key="item.value">
<td>{{ item.field }}</td>
<td>{{ item.value }}</td>
<td>{{ item.time }}</td>
</tr>
</table>
</div>
</div>
I need that code to work at Statistics.vue page.
Here is the link to gitlab repo for better convenience.
You can import TableField.vue in Statistics.vue and pass changes: [] as props.
User "async" from discord vue chat has solved this for me.
Solution was a bit complicated and required a changes to few files.
Refer to gitlab repo if you are curious to find out more about it, because I can't document it here due to its complexity.
According to me, the better solution should be using Vuex (https://vuex.vuejs.org/) that permits you to store data in sharing point called store.
Alternatively you can use events as documented here: https://v2.vuejs.org/v2/guide/components-custom-events.html#Binding-Native-Events-to-Components
Related
In my Angular project I am trying to access the value present in <td [innerHTML]='rupee.replaceHTML' #term></td> and assign it to vaiable newdata.
More on the functionality here, I have 2 buttons change and concourse. The value present in below HTML will change when I press concourse button. I was to save that changed value in variable newdata using localStorage so that I can access it later.
How can I access that html value and store it in new_data?
.html file
<mat-list-item>New Life</mat-list-item>
<mat-list class="textFields">
<table>
<tr>
<td [innerHTML]='rupee.replaceHTML' #term></td> ---> Access whatever value is here
</tr>
</table>
</mat-list>
//button
<button mat-raised-button type='button' (click)='concourse(kada, term)'>Confirm
component.ts
export class Pinky {
#ViewChild('term') editElem!: ElementRef<HTMLTableDataCellElement>;
//button function
concourse(rec: FillData, nada: HTMLTableDataCellElement) {
const { a,b,c,d } = rec;
let maan= nada.innerText;
localStorage.setItem('FillData', JSON.stringify(rec));
var new_data = JSON.parse(localStorage.getItem('FillData') || '{}') as FillData
-------------------//Can I access #term value here?-------------------------
new_data.{{?}}= maan; ----------> Assign the value here ?
localStorage.setItem('FillData', JSON.stringify(new_data));
}
Interface
export interface DraneMan {
maan:string;
}
You already have a #ViewChild('term')
so you can just get it from this.editElem.innerHTML.
What's more, you have a binding [innerHTML]='rupee.replaceHTML', so, reading from the DOM something that is bound seems like the wrong data flow.
I would just use ruppee.replaceHTML as source of truth.
I am using Angular 5 in my project. I did pagination with use ngx-pagination . I need to do this pagination top and bottom table. I need to make pagination at the top and bottom of the table. I did it this way
<div class="admin-panel__nav-block navigation-block">
<app-table-filter-size (valueChange)="valueChange($event)" [size]="size"></app-table-filter-size>
<pagination-controls class="table-pagination pagination-block" previousLabel="Previous" nextLabel="Next" id="listing_pagination" (pageChange)="pageChange(p = $event)"></pagination-controls>
</div>
<!--Table -->
...
<!--Table End -->
<div class="admin-panel__nav-block navigation-block">
<app-table-filter-size (valueChange)="valueChange($event)" [size]="size"></app-table-filter-size>
<pagination-controls class="table-pagination pagination-block" previousLabel="Previous" nextLabel="Next" id="listing_pagination" (pageChange)="pageChange(p = $event)"></pagination-controls>
</div>
it's working , but it's probably not a good practice, since the same id is used in different places. how can I get rid of duplicate code? What should I do in my situation?
You need to have an id to uniquely identify dynamically assigned instead of having hardcoded id="listing_pagination"
Have a paginationConfig Array and push configs for each set of data
ts file implementation
private paginationConfigs: Array; // define the variable
this.paginationConfigs = []; //initialize in the ngOnInit lifecycle function
Have the below configuration for each dataset probably inside the loop
const pagerConfig = {
id: `pager-${value}`, // value could be any unique identifer
itemsPerPage: 10,
currentPage: 1
};
this.paginationConfigs.push(pagerConfig);
html file implementation
<tr *ngFor="let item of mf.data| paginate: paginationConfigs[i]" >// data display </tr>
<pagination-controls previousLabel="Previous" nextLabel="Next [id]='paginationConfigs[i].id'(pageChange)="pageChange(paginationConfigs[i].currentPage= $event)"></pagination-controls>
I'm trying to create a table where the user can hide rows, and keep them hidden as they delete other rows.
In my html I use vuejs to bind a class when rendering the table:
<tr v-for="item in mylist" :class="{'noFruits': (item.fruits.length == 0)}">
There is a user checkbox to hide rows with that class:
<label><input type="checkbox" v-model="showBlankFruits" #change="setBlankDisplay">Show Blank Fruits</label>
In my Vue instance, the checkbox executes a method to hide/show rows with that class via jquery to attach the css display property:
methods: {
setBlankDisplay: function() {
if (this.showBlankFruits) {
$('.noFruits').css('display', '');
} else {
$('.noFruits').css('display', 'none');
}
},
In my jsfiddle, when a user deletes a row, the hidden row reappears. I see that attaching styles with jquery in this instance is not good... does anyone have a suggestion for a better method?
Mixing Vue and jQuery is not recommended, as you can do pretty much everything just using Vue and you don't get any conflicting operations that don't know what the other library/framework is doing.
The following will show the row if either the fruits array length is truthy, in other words not 0, or if showBlankFruits is true:
<tr v-for="item in mylist" v-show="item.fruits.length || showBlankFruits">
The following will toggle showBlankFruits when clicking the checkbox:
<label><input type="checkbox" v-model="showBlankFruits">Show Blank Fruits</label>
Full code example:
JSFiddle
You can also write something like this.
I've used computed and removed the jQuery part completely.
You must declare data as a function instead of an data object (https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function)
You do not need to call the mounted method to set the initial state. It's already set with your data object.
In your code, you have to call mounted, because jQuery can only hide the results, when the DOM is loaded.
new Vue({
el: '#app',
data() {
return {
showBlankFruits: true,
mylist: [
{'user': 'Alice', 'fruits': [{'name': 'apple'}]},
{'user': 'Bob', 'fruits': [{'name': 'orange'}]},
{'user': 'Charlie', 'fruits': []},
{'user': 'Denise', 'fruits': [{'name': 'apple'}, {'name': 'orange'}]},
]
}
},
computed: {
list() {
return this.mylist.filter(item => (item.fruits.length > 0 && !this.showBlankFruits) || (item.fruits.length === 0 && this.showBlankFruits))
},
},
methods: {
delItem(item) {
let index = this.mylist.indexOf(item);
if (index > -1) {
this.mylist.splice(index, 1);
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div id="app">
<label><input type="checkbox" v-model="showBlankFruits">Show Blank Fruits</label>
<br> <br>
<table>
<tr> <th class="col">User</th> <th class="col">Fruits</th> <th class="col"></th> </tr>
<tr v-for="item in list">
<td>{{ item.user }}</td>
<td> <span v-for="f in item.fruits"> {{ f.name }} </span> </td>
<td> <button #click="delItem(item)">Delete</button> </td>
</tr>
</table>
</div>
This is a great example of the power for declarative rendering rather than using DOM manipulation in components. The problem you are running into is when the Vue engine re-renders your list it doesn't know about the manual manipulation of elemets you did in the setBlankDisplay method. The way to get around that is to use component logic in the definition of the view itself, sort of like you did to set the noFruits class in the first place.
So, I propose you get rid of setBlankDisplay and replace it with the method:
itemDisplay(item) {
if (item.fruits.length === 0 && !this.showBlankFruits) {
return 'none';
}
return '';
},
Then you can reference it in the definition of your tr elements linked to a css display property, like so:
<tr v-for="item in mylist" :class="{'noFruits': (item.fruits.length == 0)}" :style="{display: itemDisplay(item)}">
I've updated the jsfiddle with this modification, showing that the state of hidden fruits remains when other items are deleted.
Take this as a general example of the dangers of using jquery to change the state of the view. Every effort should be taken to define the entire view in terms of component logic.
I am loading some data via rest-endport to safe it in an array. In an *ngFor directive i am currently iterating through all that data. So everthing is working.
Is there any possible way in Angular to e.g. set a boolean whenever the iteration of the array is finished (so that i can fire another function on complete)?
-- Edit--
On the ngOnInit lifecycle method i am retreiving the data:
ngOnInit() {
this.restService.getSomeBooks(5000).subscribe(buch => {
this.buecher = buch;
this.fetched = true;
})
}
After that - in HTML - i am iterating over that data:
<table>
<thead>some table-heads</thead>
<tbody>
<tr style="text-align: center" *ngFor="let buchforTable of buecher">
<td>{{buchforTable.author}}</td>
<td>{{buchforTable.erscheinungsdatum}}</td>
<td>{{buchforTable.isbn.toString()}}</td>
<td>{{buchforTable.verlag}}</td>
<td>{{buchforTable.uuid}}</td>
</tr>
</tbody>
</table>
The *ngFor-iteration should set a boolean variable whenever it is done.
As Alex Po already mentioned, trackBy is working. As you can see in the following snippet, with trackBy it is possible to handle events based on the current index of the iteration.
<tbody>
<tr style="text-align: center" *ngFor="let buchforTable of buecher; trackBy: trackByFn">
<td>{{buchforTable.author}}</td>
<td>{{buchforTable.erscheinungsdatum}}</td>
<td>{{buchforTable.isbn.toString()}}</td>
<td>{{buchforTable.verlag}}</td>
<td>{{buchforTable.uuid}}</td>
</tr>
</tbody>
To measure the time of the rendering process of all array-objects the trackBy-function would look like this (array contains 5000 objects here -> index 0 to 4999):
trackByFn(index){
if(index == 0)
this.renderStart = performance.now();
if(index == 4999) {
var renderStopp = performance.now();
var timeToRender = renderStopp - this.renderStart;
}
}
I have, what I thought was a fairly straightforward knockout situation. I have a model that comes in from WebApi that has an array of things with a Success element. I need the value of success to determine what of the properties render. I've validated that all the data is coming down from WebApi ok but nothing but the table shell renders. There are no errors in the dev console.
The HTML
<div id="model1Wrapper">
<table class = "table">
<thead >
<tr >
<th >Stuff</th><th>Things</th>
</tr>
</thead>
<tbody data-bind = "foreach: $data.historyArray" >
<!--ko if: Success -->
<tr class = "success" >
<td data-bind = "text: $data.ThingA" > </td>
<td data-bind = "text: ThingB" > </td>
</tr>
<!-- /ko -->
<!--ko ifnot: Success -->
<tr class = "danger" >
<td colspan="3" data-bind = "text: ThingC" > </td>
</tr>
<!-- /ko -->
</tbody>
</table>
</div>
Example Model Data
[{
"ThingA": "A",
"ThingB": "B",
"ThingC": "C",
"Success": false
}, {
"ThingA": "A",
"ThingB": "B",
"ThingC": "C",
"Success": true
}]
This is monitoring a process that has feeds from several endpoints so I have multiple ViewModels on the page. So I framed up a rough example of how that is working elsewhere on the page.
That business
<script>
var sampleModelData = [{
"ThingA": "A",
"ThingB": "B",
"ThingC": "C",
"Success": false
}, {
"ThingA": "A",
"ThingB": "B",
"ThingC": "C",
"Success": true
}]
var viewModel1 = {
historyArray: ko.observableArray()
};
function onNewHistory(data) {
viewModel1.historyArray(data);
}
$(document).ready(function(){
ko.applyBindings(viewModel1, document.getElementById("model1Wrapper"));
onNewHistory(sampleModelData);
})
</script>
I had to mask of some of the speciffics but the gist is, the ajax call returns an array in the example. There is a function that is called to update the new data into the observable and I would expect the table to rerender, it does not.
Other deets
Sometimes there is no model data in the table so I load it and wait
for an update. All the other Viewmodels are loaded like this but this
is the only one with an array and the only one I'm having trouble
with.
I have tried taking out the if/ifnot business and that does not work.
Fiddler hates me and I have not been able to set up a clean version of this to try.
I leafed though some of the related questions and nothing seems to fit my issue. Or the example is much more complicated to apply.
Thanks!
The problem is in this code:
var viewModel1 = {
historyArray = ko.observableArray();
}
You're mixing the syntax for declaring objects with the syntax for code inside functions. When declaring an object, don't use = and ;. Instead use : and ,.
If you change the declaration to something like below, it should work.
var viewModel1 = {
historyArray: ko.observableArray()
}
Just adding another answer to this question in case someone comes across it in future. I had this issue and it was a result of initialising my observable array within the method. I didn't mean to do this (copy paste error) and it didn't produce any errors in the console so was difficult to trace.
For example:
LoadJSArrayIntoObservable(results) {
vm.validationResults = ko.observableArray(); <---- THIS IS INVALID.
vm.validationResults([]); <---- THIS IS WHAT I MEANT TO DO!!
$.each(results, function () {
try {
vm.validationResults.push(new ValidationResult(this));
}
catch (err) {
alert(err.message);
}
});