Vue-Select: Pushing a 2 dimensional array to :options - vuejs2

The plugin Vue-Select.
What I was trying to do is, make a search-select-dropdown input based on database.
So here's my SQL first named Ms_Location.
id_Loc | name_Loc
LOC0001 | Indonesia
LOC0002 | China
LOC0003 | America
My index.php
<!DOCTYPE html>
<html>
<head>
</head
<body>
<div class="form-group">
<label for="lokasi_id" class="control-label required"><strong>Lokasi</strong></label>
<v-select :options="lokasi_list" placeholder='Type location..'></v-select>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script src="https://unpkg.com/vue-select#latest"></script>
Vue.component('v-select', VueSelect.VueSelect);
var app = new Vue ({
el: '#app',
data: {
lokasi_select: '',
lokasi_list: [],
},
// End of data
computed: {
get_lokasi() {
var list_loc = new Array();
list_loc = <?php include('receive_lokasi.php') ?>;
for(var i=0; i<list_loc.length; i++) {
var pushLoc = {
label: list_loc[i][1], value: list_loc[i][0]
}
this.lokasi_list.push(pushLoc);
}
return list_loc[0][1];
}
}
})
});
</script>
</body>
</html>
And this is my receive_lokasi.php
<?php
include ('koneksi.php');
$condition = "1";
if(isset($_GET['userid'])){
$condition = " id=".$_GET['userid'];
}
$sqltran = mysqli_query($con, "SELECT id_Loc, name_Loc FROM ms_location")or die(mysqli_error($con));
$response = array();
while ($rowList = mysqli_fetch_array($sqltran,MYSQLI_NUM)) {
$response[] = $rowList;
}
echo json_encode($response);
mysqli_close($con);
?>
However, I can't seem to get the option shown. This only happens after I make the get_lokasi(). So the mistake is probably there? Or perhaps I was missing something.
I've tried to print the lokasi_list somewhere, and yes, the value is there, but not shown in the dropdown bar.
Also, I'm new to Vue, so any help would be good. Thanks!

Nevermind..
My mistake, I didn't notice my receive_lokasi.php code
Instead of using MYSQLI_NUM
while ($rowList = mysqli_fetch_array($sqltran,MYSQLI_NUM)) {
$response[] = $rowList;
}
I should be using MYSQLI_ASSOC, as documented in here.
while ($rowList = mysqli_fetch_array($sqltran,**MYSQLI_ASSOC**)) {
$response[] = $rowList;
}
After that change this
<v-select :options="lokasi_list" placeholder='Type location..'></v-select>
To this
<v-select label='nama_Location' :options="lokasi_list" placeholder='Type location..'></v-select>
After that, everything loads fine.

Vue's computed properties aren't normally used to populate vue data attributes, they normally take one or more data attributes and combine them into something different for the template to use.
In your code you've tried to populate the vue data attribute 'lokasi_list' in the computed property 'get_lokasi', but you never call 'get_lokasi' anywhere in the template so lokasi_list remains empty.
Another approach to this sort of situation is to use a vue method to fetch data from the php backend via an ajax call with something like axios, and you'd normally use that method in the vue app's created life cycle event to get the data asap.
e.g.
<script>
Vue.component('v-select', VueSelect.VueSelect);
var app = new Vue({
el: '#app',
data: {
lokasi_select: '',
lokasi_list: [],
},
created: function() {
this.fetchLocations();
},
methods: {
fetchLocations: function() {
axios.get('/api/locations-end-point')
.then((response) => {
this.lokasi_list = response.data //might need to change this to match how your php is returning the json
})
.catch((error) => {
//handle the error
})
}
}
});
</script>
Sorry to mention this, but in your php you've got:
if(isset($_GET['userid'])){
$condition = " id=".$_GET['userid'];
}
That looks like you were planning to use it as part of your sql, but it would have been vulnerable to SQL injection attacks, sorry If I'm pointing out something you already knew.

Related

NuxtJs where to declare a computed property

How can i declare a computed property using Nuxt ? or the equivalent ?
I am using NuxtJs and trying to use a category filter.
I want to filter by unique categories, and i am getting this error message:
Cannot read property 'filter' of undefined
I trying to adapt to Nuxtjs the exemple i found in this pen : https://codepen.io/blakewatson/pen/xEXApK
I declare this computed property below, first at pages/index.vue and after into .nuxt/App.js
filteredStore: function() {
var vm = this;
var category = vm.selectedCategory;
if(category=== "All") {
return vm.stores;
} else {
return vm.stores.filter(function(stores) {
return stores.category === category;
});
}
}
And i try to apply the filter into this list of checkboxes :
<div class="columns is-multiline is-mobile">
<div class="column is-one-quarter" v-for="store in filteredStore" :key="store.id" :store="store">
<label class="checkbox">
<input type="checkbox" v-model="selectedCategory" :value="''+store.category">
{{store.category}}
</label>
</div>
</div>
I'm going to do some guessing at your code situation (based on the example you noted), so just let me know where I make an incorrect assumption. I would guess that something like the following could work for you... maybe you could provide additional details where I'm missing them.
With regards to your error Cannot read property 'filter' of undefined, that probably means your array of stores is undefined. I believe if you create the stores array as empty in the data section, you should at least have it available before your async call returns any results.
One possible thing to you can do to test if your filtering logic is working... is to uncomment the manually created data array that I've created below. It's like an inline test for your data structure and logic, removing the asynchronous retrieval of your data. This basically can check if the filter works without your API call. It would narrow down your issue at least.
export default {
data() {
return {
stores: [
// Let's assume you don't have any static stores to start on page load
// I've commented out what I'm guessing a possible data structure is
//
// Example possible stores in pre-created array
// { name: 'Zales', category: 'Jewelry', id: 1 },
// { name: 'Petco', category: 'Pet Shop', id: 2 },
// { name: 'Trip Advisor', category: 'Tourism', id: 3 },
// { name: 'Old Navy', category: 'Clothes', id: 4 }
],
selectedCategory: 'All'
}
},
computed: {
// Going to make some small js tweaks
filteredStores: () {
const vm = this;
const category = vm.selectedCategory;
if (category === "All") {
return vm.stores;
} else {
return vm.stores.filter(store => {
return store.category === category;
});
}
}
},
async asyncData({ $axios }) {
$axios
.$get('https://yourdomain.com/api/stores/some-criteria')
.then(response => {
this.stores = response.data;
})
.catch(err => {
// eslint-disable-next-line no-console
console.error('ERROR', err);
});
}
};
And then your HTML
<div class="columns is-multiline is-mobile">
<div class="column is-one-quarter" v-for="store in filteredStores" :key="store.id" :store="store">
<label class="checkbox">
<input type="checkbox" v-model="selectedCategory" :value="`${store.category || ''}`">
{{store.category}}
</label>
</div>
</div>
ANYWAY This is all just a big guess and what your scenario is, but I figured I'd try to help shape your question some so that you could get a more meaningful response. In general, I'd suggest trying to provide as much detail as you can about your question so that people really can see the bits and pieces where things might have gone astray.
Don't touch anything in .nuxt Someone noted that above in a comment, and it's very important. Essentially that whole directory is generated and any changes you make in it can be easily overwritten.

Vue rendering array of objects

I'm creating a basic app in vue that uses axios to make a get request to grab html data from a blog site and using the cheerio node package to scrape the site for elements such as blog title and the date posted of each blog articles. However, I'm having trouble trying to render the scraped elements into the html. Here's the code:
<template>
<div class="card">
<div
v-for="result in results"
:key="result.id"
class="card-body">
<h5 class="card-title">{{ result.title }}</h5>
<h6 class="card-subtitle mb-2 text-muted">{{ result.datePosted }}</h6>
</div>
</div>
</template>
<script>
const Vue = require('vue')
const axios = require('axios')
const cheerio = require('cheerio')
const URL = 'https://someblogsite.com'
export default {
data() {
return {
results: []
}
},
mounted: function() {
this.loadBlogs()
},
methods: {
loadBlogs: function() {
axios
.get(URL)
.then(({ data }) => {
const $ = cheerio.load(data)
let results = this
$('.post').each((i, element) => {
const title = $(element)
.children('.content-inner')
.children('.post-header')
.children('.post-title')
.children('a')
.text()
const datePosted = $(element)
.children('.content-inner')
.children('.post-header')
.children('.post-meta')
.children('.posted-on')
.children('a')
.children('.published')
.text()
this.results[i] = {
id: i + 1,
title: title,
datePosted: datePosted
}
})
})
.catch(console.error)
}
}
}
</script>
I tried declaring
let results = this
before the axios request to refer to the scope within export default, but still getting the indicator from VS Code that the scope is still within the loadBlogs function. Am I missing something? I greatly appreciate the help! Thanks!
I think your problem is that you're trying to set Property of an results array so Vue can't pick your data update. Instead you should construct new array from your parsed page and set it as this.results = newResultsArray:
loadBlogs: function() {
axios.get(URL).then(({data}) => {
const $ = cheerio.load(data)
const newResults = $('.post').map((i, element) => {
const title = $(element).children('.content-inner .post-header .post-title a').text()
const datePosted = $(element).children('.content-inner .post-header .post-meta .posted-on a .published').text()
return {
id: i + 1,
title: title,
datePosted: datePosted
}
})//.toArray() // this toArray call might be needed, I haven't worked with cheerio for some time and not sure whether it returns array or its own collection type like jQuery does
this.results = newResults;
}).catch(console.error)
}
Also it should be even simpler if you just use this.results.push({...}) instead of property assignment this.results[i] = {...} (but it is usually easier to handle whole arrays instead of inserting and removing parts of them, both are viable solutions in their respective use cases, though).
And please check out this documentation article about how Vue handles reactive updates, it describes the problem you've encountered.

Updating Vue.Js data with secondary ajax request

I have some working code with Vue.js v2.5.16 that updates existing data, but I am not sure if I am following best practices.
What I am doing:
I call a properties.view() method (which I omitted below for brevity) to quickly grab basic data from a redis cache on load. I then call properties.available() to retrieve non-cached inventory data.
Questions:
Is there a efficient way to update my properties objects without having to "manually" do it like via some sort of "magic" data binding? I tried the v-model directive but couldn't get it jiving.
When updating property.rate.low.total.nightly_avg I can only reference the first level attribute "rate" I then have to build out the object I am going to update to get all the way down to the child. Is there a cleaner way of doing this?
JS Code:
var properties = new Vue({
el: '#properties',
data: {
properties: []
},
methods: {
available: function(){
let self = this;
axios
.get(searchJs.getAttribute('webroot') + 'properties/available', {
headers: {
'Content-Type': 'application/json'
}
})
.then(function(response){
for (let i=0; i < response.data.length; i++) {
let p = response.data[i];
let vData = self.properties.filter(function(property) {
if (property.id == p.id) {
return property;
}
});
self.$set(vData[0], 'rate', {
'low':{
'total': {
'nightly_avg': p.rate.low.total.nightly_avg,
'original_nightly_avg': p.rate.low.total.original_nightly_avg
}
}
});
}
});
},
}
});
HTML snippet:
<div class="property-card_price-book">
<span class="strikethrough-price">${{property.rate.low.total.original_nightly_avg}}</span>
<span class="price">${{property.rate.low.total.nightly_avg}}</span>
<div class="offer">
<figure>
<span></span>
<sep></sep>
<span></span>
</figure>
</div>
</div>

Vue JS fire a method based on another method's output unique ID

I'm trying to render a list of notes and in that list I would like to include the note's user name based on the user_id stored in the note's table. I have something like this, but at the moment it is logging an error stating Cannot read property 'user_id' of undefined, which I get why.
My question is, in Vue how can something like this be executed?
Template:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{user.name}}</em>
</div>
Scripts:
methods:{
fetchNotes(id){
return this.$http.get('http://api/notes/' + id )
.then(function(response){
this.notes = response.body;
});
},
fetchUser(id){
return this.$http.get('http://api/user/' + id )
.then(function(response){
this.user = response.body;
});
}
},
created: function(){
this.fetchNotes(this.$route.params.id)
.then( () => {
this.fetchUser(this.note.user_id);
});
}
UPDATE:
I modified my code to look like the below example, and I'm getting better results, but not 100% yet. With this code, it works the first time it renders the view, if I navigate outside this component and then back in, it then fails...same thing if I refresh the page.
The error I am getting is: [Vue warn]: Error in render: "TypeError: Cannot read property 'user_name' of undefined"
Notice the console.log... it the returns the object as expected every time, but as I mentioned if refresh the page or navigate past and then back to this component, I get the error plus the correct log.
Template:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.user_name}}</em>
</div>
Scripts:
methods:{
fetchNotes(id){
return this.$http.get('http://api/notes/' + id )
.then(function(response){
this.notes = response.body;
for( let i = 0; i < response.body.length; i++ ) {
let uId = response.body[i].user_id,
uNote = this.notes[i];
this.$http.get('http://api/users/' + uId)
.then(function(response){
uNote.user = response.body;
console.log(uNote);
});
}
});
},
}
It looks like you're trying to show the username of each note's associated user, while the username comes from a different data source/endpoint than that of the notes.
One way to do that:
Fetch the notes
Fetch the user info based on each note's user ID
Join the two datasets into the notes array that your view is iterating, exposing a user property on each note object in the array.
Example code:
let _notes;
this.fetchNotes()
.then(notes => this.fetchUsers(notes))
.then(notes => _notes = notes)
.then(users => this.joinUserNotes(users, _notes))
.then(result => this.notes = result);
Your view template would look like this:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.name}}</em>
</div>
demo w/axios
UPDATE Based on the code you shared with me, it looks like my original demo code (which uses axios) might've misled you into a bug. The axios library returns the HTTP response in a data field, but the vue-resource library you use returns the HTTP response in a body field. Attempting to copy my demo code without updating to use the correct field would cause the null errors you were seeing.
When I commented that axios made no difference here, I was referring to the logic shown in the example code above, which would apply to either library, given the field names are abstracted in the fetchNotes() and fetchUsers().
Here's the updated demo: demo w/vue-resource.
Specifically, you should update your code as indicated in this snippet:
fetchInvoices(id) {
return this.$http.get('http://localhost/php-api/public/api/invoices/' + id)
// .then(invoices => invoices.data); // DON'T DO THIS!
.then(invoices => invoices.body); // DO THIS: `.data` should be `.body`
},
fetchCustomers(invoices) {
// ...
return Promise.all(
uCustIds.map(id => this.$http.get('http://localhost/php-api/public/api/customers/' + id))
)
// .then(customers => customers.map(customer => customer.data)); // DON'T DO THIS!
.then(customers => customers.map(customer => customer.body)); // DO THIS: `.data` should be `.body`
},
Tony,
Thank you for all your help and effort dude! Ultimately, with the help from someone in the Vue forum, this worked for me. In addition I wanted to learn how to add additional http requests besides the just the user in the fetchNotes method - in this example also the image request. And this works for me.
Template:
<div v-if="notes.length > 0">
<div v-if="loaded === true">
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.user_name}}</em>
<img :src="note.image.url" />
</div>
</div>
<div v-else>Something....</div>
</div>
<div v-else>Something....</div>
Script:
name: 'invoices',
data () {
return {
invoices: [],
loaded: false,
}
},
methods: {
fetchNotes: async function (id){
try{
let notes = (await this.$http.get('http://api/notes/' + id )).body
for (let i = 0; notes.length; i++) {
notes[i].user = (await this.$http.get('http://api/user/' + notes[i].user_id)).body
notes[i].image = (await this.$http.get('http://api/image/' + notes[i].image_id)).body
}
this.notes = this.notes.concat(notes)
}catch (error) {
}finally{
this.loaded = true;
}
}

Unable to print output to the console in dojo

I'm a beginner in dojo, and I'm trying to print the output to console using dojo code. But I don't what's the problem in the following code, and how can I print the output to the console?
<html>
<head>
<script type = "text/javascript" src = "dojo/dojo.js" data-dojo-config = "async: true, isDebug : true" >
</script>
</head>
<body>
<h1 id = "greeting">Hello</h1>
<script>
define(["dojo/dom"],function(dom) {
var Twitter = declare(null, {username:"defaultusername",
say :function(msg)
{
console.log("Hello "+msg);
}
});
var myInstance = new Twitter();
myInstance.say("Dojo");
});
</script>
</body>
</html>
Use require instead of define:
<script>
require(["dojo/dom", "dojo/_base/declare"], function(dom, declare) {
var Twitter = declare(null, {
username: "defaultusername",
say :function(msg) {
console.log("Hello "+msg);
}
});
var myInstance = new Twitter();
myInstance.say("Dojo");
});
</script>
Console works, but your code inside callback function in declare is not being executed until you require it.
You cannot define in inline script code, that is meant to be a class define, put in the topmost line of a class-file, meaning define maps the filename to the returned value of its function.
This means, if you have
dojo_toolkit /
dojo/
dijit/
dojox/
libs/
myWidgets/
foo.js
And foo.js reads
define(["dijit._Widget"], function(adijit) {
return declare("libs.myWidgets.foo", [adijit], function() {
say: function(msg) { console.log(msg); }
});
});
Then a new module is registered, called libs / myWidgets / foo. You should make sure that the returned declare's declaredClass inside each define matches the file hierachy.
That being said, reason why define does not work for you is the explaination above. It is inline and has no src to guess the declaredClass name from. Rewrite your code to define("aTwitterLogger", [":
define("aTwitterLogger", ["dojo/_base/declare", "dojo/dom"],function(declare, dom) {
var Twitter = declare(null, {
username:"defaultusername",
say :function(msg)
{
console.log("Hello "+msg);
}
});
var myInstance = new Twitter();
myInstance.say("Dojo");
});