How to display specific row in vuejs api - javascript

I just want to ask how to display/fetch data from API to my textbox
when you click the edit button in a specific row table. It will display it's own id and other details. I'm so sorry to post my code like this, I don't know how to do it because it gives me errors.
Raw Code :
data : {
students : []
}
methods: {
async editStudents(edit) {
let id = "621ecc95817b5aeb5783aebe"
let a = await
this.$axios.get(`https://api.qa.sampleapi.com/students/${id}`)
console.log(a.data.data)
}
It will give me that specific item but how to do it with a for loop.
Sample code :
editStudent(edit) {
let studentid = id
let a = await
this.$axios.get(`https://api.qa.sampleapi.com/students/${studentid}`)
for(let i = 0; i < this.students.length; i++) {
if(edit.studentid === this.students[i].studentid) {
this.textbox1 = this.students[i].studentid;
}
}
}

As per my understanding I came up with below solution. Please let me know if it works as per your requirement or not.
Demo :
new Vue({
el:"#app",
data:{
students: [{
id: 1,
name: 'Student 1'
}, {
id: 2,
name: 'Student 2'
}, {
id: 3,
name: 'Student 3'
}]
},
methods: {
editStudent(id) {
console.log(id); // You will get the student ID here
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul class="list">
<li v-for="student in students" :key="student.id">
{{ student.name }}
<button #click="editStudent(student.id)">Edit</button>
</li>
</ul>
</div>

Related

How to have parent data be updated by child component with multiple values

Code below.
I think I'm missing a crucial piece here. I've been through the docs and watched the entire vue2 step by step. Everything is making sense so far but I'm stuck on what seems to be a core piece. Any help would be appreciated. If this is totally wrong, please let me know, I'm not married to any of this stuff.
Desired functionality: There is an order Vue instance and it has line items.
On order.mounted() we hit an api endpoint for the order's data, including possible existing line items. If there are existing line items, we set that order data (this.lineitems = request.body.lineitems or similar). This part works fine and I can get the order total since the orders' line items are up to date at this point.
Each line item is an editable form with a quantity and a product . If I change the quantity or product of any line item, I want the child line-item component to notify the parent component that it changed, then the parent will update its own lineitems data array with the new value, and preform a POST request with all current line item data so the server side can calculate the new line item totals (many specials, discounts, etc). This will return a full replacement array for the order's line item data, which in turn would passed down to the line items to re-render.
Problems:
The line-items components "update..." methods are feeling obviously wrong, but my biggest issue is understanding how to get the parent to update its own line items data array with the new data. for instance
​
lineitems = [
{id: 1000, quantity: 3, product: 555, total: 30.00},
{id: 1001, quantity: 2, product: 777, total: 10.00}
]
If the second line item is changed to quantity 1, how do I get the parent's lineitems data to change to this? My main problem is that I don't know how the parent is suppose to know which of its own lineitems data array need to be modified, and how to grab the data from the changed child. I assume it came in via an event, via emit, but do I now need to pass around the primary key everywhere so I can do loops and compare? What if its a new line item and there is no primary key yet?
Mentioned above, I'm using the existing line item's DB primary key as the v-for key. What if I need a "new lineitem" that appends a blank lineitem below the existing ones, or if its a new order with no primary keys. How is this normally handled.
Is there a best practice to use for props instead of my "initial..." style? I assume just using $emit directly on the v-on, but I'm not sure how to get the relevant information to get passed that way.
This seems like the exact task that VueJS is suited for and I just feel like I keep chasing my tail in the wrong direction. Thanks for the help!
LineItem
Vue.component('line-item', {
props: ["initialQuantity", "initialProduct", "total"],
data () {
return {
// There are more but limiting for example
quantity: initialQuantity,
product: initialProduct,
productOptions = [
{ id: 333, text: "Product A"},
{ id: 555, text: "Product B"},
{ id: 777, text: "Product C"},
]
}
},
updateQuantity(event) {
item = {
quantity: event.target.value,
product: this.product
}
this.$emit('update-item', item)
},
updateProduct(event) {
item = {
quantity: this.quantity,
product: event.target.value
}
this.$emit('update-item', item)
}
template: `
<input :value="quantity" type="number" #input="updateQuantity">
<select :value="product" #input="updateProduct">
<option v-for="option in productOptions" v-bind:value="option.id"> {{ option.text }} </option>
</select>
Line Item Price: {{ total }}
<hr />
`
})
Order/App
var order = new Vue({
el: '#app',
data: {
orderPK: orderPK,
lineitems: []
},
mounted() {
this.fetchLineItems()
},
computed: {
total() {
// This should sum the line items, like (li.total for li in this.lineitems)
return 0.0
},
methods: {
updateOrder(item) {
// First, somehow update this.lineitems with the passed in item, then
fetch(`domain.com/orders/${this.orderPK}/calculate`, this.lineitems)
.then(resp => resp.json())
.then(data => {
this.lineitems = data.lineitems;
})
},
fetchLineItems() {
fetch(`domain.com/api/orders/${this.orderPK}`)
.then(resp => resp.json())
.then(data => {
this.lineitems = data.lineitems;
})
},
},
template: `
<div>
<h2 id="total">Order total: {{ total }}</h2>
<line-item v-for="item in lineitems"
#update-item="updateOrder"
:key="item.id"
:quantity="item.quantity"
:product="item.product"
:total="item.total"
></line-item>
</div>
`
})
Here's a list of problems in your attempt that would prevent it from displaying anything at all i.e.
quantity: initialQuantity, - surely you meant quantity: this.initialQuantity, ... etc for all the other such data
missing } for computed total
your line-item template is invalid - you have multiple "root" elements
And then there's some minor issues:
you want the #change handler for the select, not #input, if your code ran, you'd see the difference,
Similarly you want #change for input otherwise you'll be making fetch requests to change the items every keystroke, probably not what you'd want
So, despite all that, I've produced some working code that does all you need - mainly for my own "learning" though, to be fair :p
// ******** some dummy data and functions to emulate fetches
const products = [
{ id: 333, text: "Product A", unitPrice: 10},
{ id: 555, text: "Product B", unitPrice: 11},
{ id: 777, text: "Product C", unitPrice: 12},
];
let dummy = [
{id: 1, quantity:2, product: 333, total: 20},
{id: 2, quantity:3, product: 777, total: 36},
];
const getLineItems = () => new Promise(resolve => setTimeout(resolve, 1000, JSON.stringify({lineitems: dummy})));
const update = items => {
return new Promise(resolve => setTimeout(() => {
dummy = JSON.parse(items);
dummy.forEach(item =>
item.total = parseFloat(
(
item.quantity *
(products.find(p => p.id === item.product) || {unitPrice: 0}).unitPrice *
(item.quantity > 4 ? 0.9 : 1.0)
).toFixed(2)
)
);
let res = JSON.stringify({lineitems: dummy});
resolve(res);
}, 50));
}
//********* lineItem component
Vue.component('line-item', {
props: ["value"],
data () {
return {
productOptions: [
{ id: 333, text: "Product A"},
{ id: 555, text: "Product B"},
{ id: 777, text: "Product C"},
]
}
},
methods: {
doupdate() {
this.$emit('update-item', this.value.product);
}
},
template: `
<p>
<input v-model="value.quantity" type="number" #change="doupdate()"/>
<select v-model="value.product" #change="doupdate()">
<option v-for="option in productOptions" v-bind:value="option.id"> {{ option.text }} </option>
</select>
Line Item Price: {{ '$' + value.total.toFixed(2) }}
</p>
`
})
//********* Order/App
const orderPK = '';
var order = new Vue({
el: '#app',
data: {
orderPK: orderPK,
lineitems: []
},
mounted() {
// initial load
this.fetchLineItems();
},
computed: {
carttotal() {
return this.lineitems.reduce((a, {total}) => a + total, 0)
}
},
methods: {
updateOrder(productCode) {
// only call update if the updated item has a product code
if (productCode) {
// real code would be
// fetch(`domain.com/orders/${this.orderPK}/calculate`, this.lineitems).then(resp => resp.json())
// dummy code is
update(JSON.stringify(this.lineitems)).then(data => JSON.parse(data))
.then(data => this.lineitems = data.lineitems);
}
},
fetchLineItems() {
// real code would be
//fetch(`domain.com/api/orders/${this.orderPK}`).then(resp => resp.json())
// dummy code is
getLineItems().then(data => JSON.parse(data))
.then(data => this.lineitems = data.lineitems);
},
addLine() {
this.lineitems.push({
id: Math.max([this.lineitems.map(({id}) => id)]) + 1,
quantity:0,
product: 0,
total: 0
});
}
},
template: `
<div>
<h2 id="total">Order: {{lineitems.length}} items, total: {{'$'+carttotal.toFixed(2)}}</h2>
<line-item v-for="(item, index) in lineitems"
:key="item.id"
v-model="lineitems[index]"
#update-item="updateOrder"
/>
<button #click="addLine()">
Add item
</button>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
</div>
note: there may be some inefficient code in there, please don't judge too harshly, I've only been using vuejs for a week

How to remove image url on Autocomplete in Materialize CSS?

I have this JavaScript method that get all my data from a department table using API:
<script type="text/javascript">
$(document).ready(function() {
//Autocomplete
$(function() {
$.ajax({
type: 'GET',
url: 'http://127.0.0.1/EnrollmentSystem/api/department/read.php',
success: function(response) {
var departmentArray = response;
var dataDepartment = {};
//console.log(departmentArray['records']['0'].name);
console.log(departmentArray['records'].length);
for (var i = 0; i < departmentArray['records'].length; i++) {
console.log(departmentArray['records'][i]);
dataDepartment[departmentArray['records'][i].name] = departmentArray['records'][i].name; //departmentArray[i].flag or null
}
$('input.autocomplete_department').autocomplete({
data: dataDepartment,
});
}
});
});
});
</script>
and i am calling it on my page using this one:
<div class="row lt-row-content input-field">
<div class="col s12 m3 l3 lt-input-field">Department</div>
<div class="col s12 m8 l8 lt-input-field"><input type="text" name="" id="autocomplete-input" class="autocomplete_department lt-input-field"></div>
</div>
My concern is how can i remove the image shown on the Autocomplete?
As for the my Object only the id and name which i include on my models
class Department{
private $conn;
private $table_name = "department";
public $id;
public $name;
public function __construct($db){
$this->conn = $db;
}
...
This is the output of console.log(departmentArray['records'][i]);
const departmentArray = {
records: [
{ id: 1, name: 'DEPARTMENT 1' },
{ id: 2, name: 'DEPARTMENT 2' },
{ id: 3, name: 'DEPARTMENT 3' },
{ id: 4, name: 'DEPARTMENT 4' },
]
}
const dataDepartment = departmentArray.records.map(record => record.name);
$('#input').autocomplete({
source: dataDepartment
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<input id="input" type="text">
The autocomplete documentation accepts a simple array of strings, so lets create that from your departmentArray response using Array map.
var dataDepartment = departmentArray.records.map(record => record.name);
Also, autocomplete expects the options attribute source instead of data.
$('input.autocomplete_department').autocomplete({
source: dataDepartment
});
Looking for a similar answer as the original post. The answer given by camaulay would be right if the original post was about Jquery autocomplete. It is not. The original post is about Materializecss autocomplete and it should be data and NOT source for the list of autocomplete elements. `
// copy/pasted from https://materializecss.com/autocomplete.html
$(document).ready(function(){
$('input.autocomplete').autocomplete({
data: {
"Apple": null,
"Microsoft": null,
"Google": 'https://placehold.it/250x250'
},
});
});`[codepen example.][1]
See "Apple": null - the null value is the way to suppress the image in the Materializecss autocomplete.

Javascript load more images of the array onclick. Using Firebase

I'm trying to make my website a little bit faster, and for that, I'm trying to make a button that on each click presents more images. For example: a user can see 5 images, and if the user wants to see 5 more he can, by clicking on the button.
So for now only got this, and i really think it's not the right way.
HTML ->
<ion-card *ngFor="let pic of photoList">
<h1>{{pic?.username}}</h1>
<h2>{{pic?.name}}</h2>
<img src={{pic?.picture}}>
</ion-card>
<button ion-button (click)="load()">Load More Images</button>
Js ->
load() {
firebase.database().ref('HomeList').limitToLast(5).on('value', snapshot => {
this.photoList = [];
snapshot.forEach(snap => {
this.photoList.push({
id: snap.key,
name: snap.val().name,
username: snap.val().username,
picture: snap.val().picture,
email: snap.val().email,
uid: snap.val().uid,
rating: snap.val().rating
});
console.log(this.photoList);
return false
});
return this.photoList.reverse();
});
}
so you need a pagination try to use .startAfter(number) and .limit(number); assuming this.current = 0; sets in constructor();
load() {
firebase.database().ref('HomeList').startAfter(this.current).limit(5).on('value', snapshot => {
this.photoList = [];
snapshot.forEach(snap => {
this.photoList.push({
id: snap.key,
name: snap.val().name,
username: snap.val().username,
picture: snap.val().picture,
email: snap.val().email,
uid: snap.val().uid,
rating: snap.val().rating
});
console.log(this.photoList);
this.current = this.current + photoList.length;
return false
});
return this.photoList.reverse();
});
}

Checkbox statuses not persisted in simple vue.js questionnaire app

I wrote this simple questionnaire app example: https://jsfiddle.net/neydmo34/ but I have problem with checkboxes loosing its state when user clicks buttons "Next" and "Back".
For example, if user performs this actions:
answer with "Lisp" on first question,
click "Next",
answer with "Bill Gates" on second question,
click "Back",
click "Next",
then you'll see that the "Bill Gates" checkbox will not be checked anymore, despite the fact that backing array userAnswers is correctly updated.
I cannot understand why that's happen and what I should change in my code to made it work correctly.
Here's the code:
<html>
<head>
<title>Questionnaire</title>
<script src="vue.js"></script>
</head>
<body>
<h1>Questionnaire</h1>
<div id="app">
<p><b>Question {{ currQuestionIndex + 1 }})</b> {{ currQuestion.text }}</p>
<div v-for="ans in currQuestion.answers">
<input type="radio"
:name="currQuestionIndex"
:value="ans"
v-model="userAnswers[currQuestionIndex]" />
<label :for="ans">{{ ans }}</label><br>
</div>
<p>
<button #click="goBack">Back</button>
<button #click="goNext">Next</button>
</p>
userAnswers = {{ userAnswers }}
</div>
</body>
<script>
var app = new Vue({
el: '#app',
data: {
currQuestionIndex: 0,
questions: [
{text: "What's the name of most powerful programming language?",
answers: ['Java', 'C#', 'Lisp', 'Haskell']
},
{text: 'Who is Microsoft founder?',
answers: ['Bill Gates', 'Richard Stallman', 'Steve Jobs']
},
{text: 'What type of software do you like most?',
answers: ['open source', 'closed source', 'public domain']
},
{text: 'The best computing company is:',
answers: ['IBM', 'Microsoft', 'Google']
},
],
userAnswers: [null, null, null, null]
},
computed: {
currQuestion: function () {
return this.questions[this.currQuestionIndex];
}
},
methods: {
goNext: function(e) {
var next = this.currQuestionIndex + 1;
if (next >= this.questions.length) {
alert("Ok, your answers are: " + this.userAnswers);
} else {
this.currQuestionIndex = next;
}
},
goBack: function(e) {
var previous = this.currQuestionIndex - 1;
if (previous >= 0) {
this.currQuestionIndex = previous;
}
}
}
});
</script>
</html>
var app = new Vue({
el: '#app',
data: {
currQuestionIndex: 0,
questions: [
{text: "What's the most powerful programming language?",
answers: ['Java', 'Scheme', 'Lisp', 'Haskell']
},
{text: 'Who is Microsoft founder?',
answers: ['Bill Gates', 'Richard Stallman', 'Steve Jobs']
},
{text: 'What type of software do you like most?',
answers: ['open source', 'closed source', 'public domain']
},
{text: 'The best computing company is:',
answers: ['IBM', 'Microsoft', 'Google']
},
],
userAnswers: [null, null, null, null]
},
computed: {
currQuestion: function () {
return this.questions[this.currQuestionIndex];
}
},
methods: {
goNext: function(e) {
var next = this.currQuestionIndex + 1;
if (next >= this.questions.length) {
alert("OK, your answers are: " + this.userAnswers);
} else {
this.currQuestionIndex = next;
}
},
goBack: function(e) {
var previous = this.currQuestionIndex - 1;
if (previous >= 0) {
this.currQuestionIndex = previous;
}
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<h1>Questionnaire</h1>
<div id="app">
<p><b>Question {{ currQuestionIndex + 1 }})</b> {{ currQuestion.text }}</p>
<div v-for="ans in currQuestion.answers" :key="ans">
<input type="radio" :name="currQuestionIndex" :value="ans" v-model="userAnswers[currQuestionIndex]" />
<label :for="ans">{{ ans }}</label><br>
</div>
<p>
<button #click="goBack">Back</button>
<button #click="goNext">Next</button>
</p>
userAnswers = {{ userAnswers }}
</div>
You need a key.
When Vue is updating a list of elements rendered with v-for, it by
default uses an “in-place patch” strategy. If the order of the data
items has changed, instead of moving the DOM elements to match the
order of the items, Vue will simply patch each element in-place and
make sure it reflects what should be rendered at that particular
index. This is similar to the behavior of track-by="$index" in Vue
1.x.
This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM
state (e.g. form input values).
To give Vue a hint so that it can track each node’s identity, and thus
reuse and reorder existing elements, you need to provide a unique key
attribute for each item.
Since each answer is unique in your form, you can use :key="ans".
Update: added snippet copied in from Cristy's fiddle.

I need to filter an array in AngularJS

I need to filter an array that shown below by "all permission value" to send it on the server. It is an example. Thanks for any help.
<!DOCTYPE html>
<html ng-app>
<head>
<script src="./angular.js"></script>
<script>
function MainCtrl($scope) {
$scope.accounts = [{ name: 'KL', company: 'Alpha', permission: 'all'},
{ name: 'Jem', company: 'Altes', permission: 'no' },
{ name: 'Osama', company: 'Solar', permission: 'no' },
{ name: 'Victor', company: 'Osteriks', permission: 'all' }];
// I'd like to get here a filtered array by "all permission" from the View block filter
$scope.filteredAccounts = // [ { name: 'KL', company: 'Alpha', permission: 'all'},
// { name: 'Victor', company: 'Osteriks', permission: 'all' }];
}
</script>
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="account in accounts | filter: { permission: 'all' }">
<p>{{ account.name }}</p>
</div>
</body>
</html>
I didn't feel like including underscore or lodash in my project just because i wanted the findWhere function so i added it to angular myself:
simply include this outside of angular and change the "angular.element(document)" to the element you booted your app on.
angular.findWhere = function(collection,search) {
var $filter = angular.element(document).injector().get("$filter"),
refined = $filter('filter')(collection,search);
return refined[0];
}
then you just use it like this:
angular.findWhere($scope.accounts,{permission: 'all'});
Most simple way is use lodash and write
$scope.filteredAccounts = _.where($scope.accounts,{permission: 'all'});
You can write custom filters in AngularJS. Here is a link to a document that talks about how to create one: https://docs.angularjs.org/tutorial/step_09
Your custom filter might look something like this:
myApp.filter('permissionFilter', function () {
return function (accounts, permission) {
var filtered = [];
angular.forEach(accounts, function (account) {
if (account.permission === permission) {
filtered.push(account);
}
});
return filtered;
};
});
And the usage would be something like this:
<div ng-controller="MainController">
<div ng-repeat="account in accounts | permissionFilter:'all'">
<p>{{ account.name }}</p>
</div>
</div>
This would allow you to use this filter to filter any kind of permission. In other words, you can change 'all' to 'no' and it will return Jem and Osama. You could take that a step further and bind to the filter variable, so you could let the user decide what they wanted to filter based on.
Here is a Plunker with a working example.

Categories