I have an array of data which is gotten from an api and is displayed using Vuejs like this
<div class="col-sm-9">
<Select class="form-control" name="building" v-model="allBuild.id" #change="showBuilding()">
<option v-for="b in allBuilding" :key="b.id" :value="b.id">
{{ b.description }}
</option>
</Select>
</div>
data(){
return {
allBuilding: [],
allBuild:{
id: null
},
}
}
async created(){
let self = this;
await axios.get( self.$apiAdress + '/api/electricity-building?token=' + localStorage.getItem("api_token") )
.then(response => {
self.allBuilding = response.data
// for(index in Object.keys(self.allBuilding)){
// self.allBuild = self.allBuilding[0].fields.id
// }
if(response.data.length){
this.allBuild = response.data.id
}
})
}
showBuilding()
{
console.log(this.allBuild)
},
My Question is that how do i get the ID of each data when the onchange is fired. if i click the building from the list of all building displayed i get the id of the first element in the array which isn't what i want.
What i want is that any building i click i should get an id of each.
For example click building 1 get id 1 click building 2 get id 2 click building 3 get id 3
etc
try this:
showBuilding(evt){
console.log(evt.target.value)
}
Related
I'm dealing with something that seems to be a bit beyond my ken here ...
It's about select value binding. Here below, it's a simple peace of code that is working perfectly in a classic svelte SPA.
<script>
let countrySelected = {
code: 'BE',
name: 'Belgium',
};
const countries = [
{
code: 'FR',
name: 'France',
},
{
code: 'BE',
name: 'Belgium',
},
{
code: 'GA',
name: 'Gabon',
},
];
</script>
<select bind:value={countrySelected}>
{#each countries as country}
<option value={country} selected={country.code === countrySelected.code}>
{country.name}
</option>
{/each}
</select>
But for whatever reason, it doesn't work anymore when it's working in a sveltekit app which uses the svelte-i18n npm package.
/src/routes/__layout.svelte
<script>
import { setupI18n, isLocaleLoaded } from '$lib/services/i18n.js';
$: if (!$isLocaleLoaded) {
setupI18n({ withLocale: 'fr-FR' });
}
</script>
{#if !$isLocaleLoaded}
Please wait...
{:else}
<main>
<slot />
</main>
{/if}
/src/lib/services/i18n.js
The code of /src/lib/services/i18n.js comes from:
https://phrase.com/blog/posts/how-to-localize-a-svelte-app-with-svelte-i18n/
&
https://lokalise.com/blog/svelte-i18n/
import { derived } from 'svelte/store';
import { dictionary, locale, _, date, time, number } from 'svelte-i18n';
const MESSAGE_FILE_URL_TEMPLATE = 'http://localhost:3000/lang/{locale}.json';
let cachedLocale;
async function setupI18n({ withLocale: _locale } = { withLocale: 'en-GB' }) {
const messsagesFileUrl = MESSAGE_FILE_URL_TEMPLATE.replace(
'{locale}',
_locale
);
const res = await fetch(messsagesFileUrl);
const messages = await res.json();
dictionary.set({ [_locale]: messages });
cachedLocale = _locale;
locale.set(_locale);
}
// Before any locale is set, svelte-i18n will give locale an object type.
// Once it is correctly set, the libray will set locale
// to the code of the active locale, e.g. "en", a string type.
// We check for this in our devired store, and make sure that isLocaleLoadedās value
// is true only after i18n initialization is successful.
const isLocaleLoaded = derived(locale, $locale => typeof $locale === 'string');
export { _, locale, setupI18n, isLocaleLoaded, date, time, number };
Problem:
The <select> element in the index.svelte file should show "Belgium" because:
countrySelected = { code: 'BE', name: 'Belgium', }
The problem is that in __layout.svelte, svelte-i18n makes a kind of refresh just after the item has been selected so it looks nothing is selected.
It's probably because of the way I've integrated svelte-i18n in my project as I only understood the broad strokes but once again ... the devil is in the detail :D
Thank you so much for your help. You can clone this repos, it gonna be easier to understand:
git clone https://github.com/BigBoulard/sveltekit-sveltei18n
npm i
npm run dev
With your example, if you inspect the select element in a browser, the selected attribute is not set on any option.
Svelte has a very easy to use and straight forward way to set the initial selected value.
It automatically handles the required attribute on the option element when binding a value to the select element.
The issue in your example is, that countrySelected looks the same as the object/dictionary inside your countries array, but is actually a new object and therefore Svelte can not select it.
This example should work:
<script>
const countries = [
{
code: 'FR',
name: 'France',
},
{
code: 'BE',
name: 'Belgium',
},
{
code: 'GA',
name: 'Gabon',
},
];
let countrySelected = countries.find(x => x.code === 'BE');
</script>
<select bind:value={countrySelected}>
{#each countries as country}
<option value={country}>
{country.name}
</option>
{/each}
</select>
I'm using Alpine to display a list of items that will change. But I can't figure out how to tell Alpine to refresh the list of items once a new one comes back from the server:
<div x-data=" items() ">
<template x-for=" item in items " :key=" item ">
<div x-text=" item.name "></div>
</template>
</div>
The first "batch" of items is fine, because they're hard-coded in the items() function:
function items(){
return {
items: [
{ name: 'aaron' },
{ name: 'becky' },
{ name: 'claude' },
{ name: 'david' }
]
};
}
Some code outside of Alpine fetches and receives a completely new list of items, that I want to display instead of the original set. I can't figure out how, or if it's even currently possible. Thanks for any pointer.
There are 3 ways to solve this.
Move the fetch into the Alpine.js context so that it can update this.items
function items(){
return {
items: [
{ name: 'aaron' },
{ name: 'becky' },
{ name: 'claude' },
{ name: 'david' }
],
updateItems() {
// something, likely using fetch('/your-data-url').then((res) => )
this.items = newItems;
}
};
}
(Not recommended) From your JavaScript code, access rootElement.__x.$data and set __x.$data.items = someValue
<script>
// some other script on the page
// using querySelector assumes there's only 1 Alpine component
document.querySelector('[x-data]').__x.$data.items = [];
</script>
Trigger an event from your JavaScript and listen to it from your Alpine.js component.
Update to the Alpine.js component, note x-on:items-load.window="items = $event.detail.items":
<div x-data=" items() " x-on:items-load.window="items = $event.detail.items">
<template x-for=" item in items " :key=" item ">
<div x-text=" item.name "></div>
</template>
</div>
Code to trigger a custom event, you'll need to fill in the payload.
<script>
let event = new CustomEvent("items-load", {
detail: {
items: []
}
});
window.dispatchEvent(event);
</script>
Expanding on Hugo's great answer I've implemented a simple patch method that lets you update your app's state from the outside while keeping it reactive:
<div x-data="app()" x-on:patch.window="patch">
<h1 x-text="headline"></h1>
</div>
function app(){
window.model = {
headline: "some initial value",
patch(payloadOrEvent){
if(payloadOrEvent instanceof CustomEvent){
for(const key in payloadOrEvent.detail){
this[key] = payloadOrEvent.detail[key];
}
}else{
window.dispatchEvent(new CustomEvent("patch", {
detail: payloadOrEvent
}));
}
}
};
return window.model;
}
In your other, non-related script you can then call
window.model.patch({headline : 'a new value!'});
or, if you don't want assign alpine's data model to the window, you can simply fire the event, as in Hugo's answer above:
window.dispatchEvent(new CustomEvent("patch", {
detail: {headline : 'headline directly set by event!'}
}));
I am using jhipster to generate CRUD for my web application.
by configuring jdl generator I expect to see search panel in UI for each entity. But it just generates EntityQueryService classes in backend,it works fine
and it is reachable in swagger-ui in the API docs page
Is there any UI library thing to help me pass parameters as expected format or any predicate filter panel ?
Thanks.
Finally I addded manual search panel as below :
<div class="container-fluid">
<div class="row">
<jhi-alert-error></jhi-alert-error>
<div class="col-sm-4">
<label for="field_billOrgType">organization</label>
<select id="field_billOrgType" ng-model="vm.searchModel.billOrgType">
<option ng-repeat="x in vm.Utilities" value="{{x.key}}">{{x.name}} - {{x.key}}</option>
</select>
</div>
</div>
</div>
My controller:
( function () {
'use strict';
angular
.module('ebppApp')
.factory('TmpBill', TmpBill);
TmpBill.$inject = ['$resource'];
function TmpBill($resource) {
var resourceUrl = 'api/tmp-bills/:id';
return $resource(resourceUrl, {}, {
'search': {
method: 'GET'
, isArray: true
, url: 'api/tmp-bills?:billOrgType',
params: {
billOrgType: '#billOrgType'
}
}
}
});
} })();
function search() {
TmpBill.search({
billOrgType: vm.searchModel.billOrgType ? "billOrgType.equals=" + vm.searchModel.billOrgType : ''
page: vm.page - 1,
size: vm.itemsPerPage,
sort: sort()
}, onSuccess, onError);
function sort() {
var result = [vm.predicate + ',' + (vm.reverse ? 'asc' : 'desc')];
if (vm.predicate !== 'id') {
result.push('id');
}
return result;
}
function onSuccess(data, headers) {
vm.links = ParseLinks.parse(headers('link'));
vm.totalItems = headers('X-Total-Count');
vm.queryCount = vm.totalItems;
vm.tmpBills = data;
// vm.page = pagingParams.page;
}
function onError(error) {
AlertService.error(error.data.message);
}
}
After much Googling and finding the Vue.js forum down, I am ready to give up.
I'm creating a Postcode Lookup component, and everything was working well until I tried to combine it with Laravel's form validation - particularly when there's an error, and the form re-fills the old values.
Hopefully I cover everything here. I have a form input partial that I use which generates every form input. It also uses Laravel's old(...) value if present.
The issue is because there's a default value (in this case for postcode and address) of an empty string, this overrides the value attribute of Postcode input, and the content of the Address textarea.
In made up land, the ideal would be:
data : function() {
return {
postcode : old('postcode'),
address : old('address'),
addresses : [],
hasResponse : false,
selectedAddress : ''
};
},
So that's what I'm trying to replicate.
I can probably replace validation with Ajax validation, but my form partial changes the appearance of fields with an error slightly, so this would be messy
From my understanding:
I can't set an initial data value, as this will override the input value.
I can set a prop, but this is immutable
Any help I can find suggests 'using a computed property which determines its value from the prop' but if you literally do that, it doesn't update.
Here's what I have so far:
<so-postcode-lookup initial-postcode="{{ old('postcode') }}" initial-address="{{ old('address') }}"></so-postcode-lookup>
/**
* Allow user to select an address from those found in the postcode database
*/
Vue.component('so-postcode-lookup', {
name : 'so-postcode-lookup',
template : '#so-postcode-lookup-template',
props : ['initialPostcode', 'initialAddress'],
data : function() {
return {
postcode : '',
address : '',
addresses : [],
hasResponse : false,
selectedAddress : ''
};
},
computed : {
currentAddress : function() {
if (this.address !== '') {
return this.address;
} else {
return this.initialAddress;
}
},
currentPostcode : function() {
if (this.postcode !== '') {
return this.postcode;
} else {
return this.initialPostcode;
}
},
hasAddresses : function() {
return this.addresses.length;
},
isValidPostcode : function() {
return this.postcode !== '' && this.postcode.length > 4;
},
isInvalidPostcode : function() {
return !this.isValidPostcode;
}
},
methods : {
fetchAddresses : function() {
var resource = this.$resource(lang.ajax.apiPath + '/postcode-lookup{/postcode}');
var $vm = this;
var element = event.currentTarget;
// Fetch addresses from API
resource.get({ postcode : this.postcode }).then(function(response) {
response = response.body;
if (response.status == 'success') {
// Update addresses property, allowing select to be displayed
$vm.addresses = response.data;
} else {
$vm.addresses = [];
}
this.hasResponse = true;
});
},
setAddress : function() {
this.address = this.selectedAddress;
}
}
});
<template id="so-postcode-lookup-template">
<div class="row">
#include('partials.input', [
'label' => trans('register.form.postcode'),
'sub_type' => 'postcode',
'input_id' => 'postcode',
'autocorrect' => false,
'input_attributes' => 'v-model="currentPostcode"',
'suffix_button' => true,
'suffix_button_reactive' => trans('register.form.postcode_button_reactive'),
'suffix_text' => trans('register.form.postcode_button'),
'required' => true,
'columns' => 'col-med-50',
'wrapper' => 'postcode-wrapper'
])
<div class="col-med-50 form__item" v-show="hasResponse">
<label for="address-selector" class="form__label" v-show="hasAddresses">{{ trans('forms.select_address') }}</label>
<select id="address-selector" class="form__select" v-show="hasAddresses" v-model="selectedAddress" #change="setAddress">
<template v-for="address in addresses">
<option :value="address.value">#{{ address.text }}</option>
</template>
</select>
<so-alert type="error" allow-close="false" v-show="!hasAddresses">{{ trans('forms.no_addresses') }}</so-alert>
</div>
#include('partials.input', [
'label' => trans('register.form.address'),
'input_id' => 'address',
'type' => 'textarea',
'input_attributes' => 'v-model="currentAddress"',
'required' => true
])
</div>
</template>
If I try this, and set the model of the inputs to currentPostcode and currentAddress respectively, I seem to get an infinite loop.
I think I'm overthinking this somehow.
You can't bind directly to a prop but you can set an initial value using the prop and then bind to that, which is the way to go if you need a two way binding:
Vue.component('my-input', {
props: {
'init-postcode': {
default: ""
}
},
created() {
// copy postcode to data
this.postcode = this.initPostcode;
},
data() {
return {
postcode: ""
}
},
template: '<span><input type="text" v-model="postcode"> {{ postcode }}</span>'
});
Then just do:
<div id="app">
<my-input init-postcode="{{ old('postcode') }}"></my-input>
</div>
Here's the fiddle: https://jsfiddle.net/vL5nw95x/
If you are just trying to set the initial values, but don't need a two way binding, then you can reference the prop directly - as you won't be applying any changes - using v-bind:value:
Vue.component('my-input', {
props: {
'init-postcode': {
default: ""
}
},
template: '<span><input type="text" :value="initPostcode"> {{ postcode }}</span>'
});
And the markup:
Here's the fiddle: https://jsfiddle.net/pfdgq724/
Im working in a easy way to do that using laravel 5.4 controller to send the data directly
In Laravel view:
<input class="form-control" id="ciudad" name="ciudad" type="text" v-model="documento.ciudad" value="{{ old('ciudad', isset($documento->ciudad) ? $documento->ciudad : null) }}" >
in vue.js 2.0
data: {
documento: {
ciudad: $('#ciudad').val(),
},
},
In Laravel Controller
$documento = ReduJornada::where("id_documento",$id)->first();
return view('documentos.redujornada')->with(compact('documento'));
I'm working on a project that is using Vue.js and Vue Router as the frontend javascript framework that will need to use a select box of users many places throughout the app. I would like to use select2 for the select box. To try to make my code the cleanest I can, I've implemented a custom filter to format the data the way select2 will accept it, and then I've implemented a custom directive similar to the one found on the Vue.js website.
When the app starts up, it queries the api for the list of users and then stores the list for later use. I can then reference the users list throughout the rest of the application and from any route without querying the backend again. I can successfully retrieve the list of users, pass it through the user list filter to format it the way that select2 wants, and then create a select2 with the list of users set as the options.
But this works only if the route that has the select2 is not the first page to load with the app. For example, if I got to the Home page (without any select2 list of users) and then go to the Users page (with a select2), it works great. But if I go directly to the Users page, the select2 will not have any options. I imagine this is because as Vue is loading up, it sends a GET request back to the server for the list of users and before it gets a response back, it will continues with its async execution and creates the select2 without any options, but then once the list of users comes back from the server, Vue doesn't know how to update the select2 with the list of options.
Here is my question: How can I retrieve the options from an AJAX call (which should be made only once for the entire app, no matter how many times a user select box is shown) and then load them into the select2 even if the one goes directly to the page with the select2 on it?
Thank you in advance! If you notice anything else I should be doing, please tell me as I would like this code to use best practices.
Here is what I have so far:
Simplified app.js
var App = Vue.extend({
ready: function() {
this.fetchUsers();
},
data: function() {
return {
globals: {
users: {
data: []
},
}
};
},
methods: {
fetchUsers: function() {
this.$http.get('./api/v1/users/list', function(data, status, response) {
this.globals.users = data;
});
},
}
});
Sample response from API
{
"data": [
{
"id": 1,
"first_name": "John",
"last_name": "Smith",
"active": 1
},
{
"id": 2,
"first_name": "Emily",
"last_name": "Johnson",
"active": 1
}
]
}
User List Filter
Vue.filter('userList', function (users) {
if (users.length == 0) {
return [];
}
var userList = [
{
text : "Active Users",
children : [
// { id : 0, text : "Item One" }, // example
]
},
{
text : "Inactive Users",
children : []
}
];
$.each( users, function( key, user ) {
var option = { id : user.id, text : user.first_name + ' ' + user.last_name };
if (user.active == 1) {
userList[0].children.push(option);
}
else {
userList[1].children.push(option);
}
});
return userList;
});
Custom Select2 Directive (Similar to this)
Vue.directive('select', {
twoWay: true,
bind: function () {
},
update: function (value) {
var optionsData
// retrive the value of the options attribute
var optionsExpression = this.el.getAttribute('options')
if (optionsExpression) {
// if the value is present, evaluate the dynamic data
// using vm.$eval here so that it supports filters too
optionsData = this.vm.$eval(optionsExpression)
}
var self = this
var select2 = $(this.el)
.select2({
data: optionsData
})
.on('change', function () {
// sync the data to the vm on change.
// `self` is the directive instance
// `this` points to the <select> element
self.set(select2.val());
console.log('emitting "select2-change"');
self.vm.$emit('select2-change');
})
// sync vm data change to select2
$(this.el).val(value).trigger('change')
},
unbind: function () {
// don't forget to teardown listeners and stuff.
$(this.el).off().select2('destroy')
}
})
Sample Implementation of Select2 From Template
<select
multiple="multiple"
style="width: 100%"
v-select="criteria.user_ids"
options="globals.users.data | userList"
>
</select>
I may have found something that works alright, although I'm not sure it's the best way to go about it. Here is my updated code:
Implementation of Select2 From Template
<select
multiple="multiple"
style="width: 100%"
v-select="criteria.reporting_type_ids"
options="globals.types.data | typeList 'reporttoauthorities'"
class="select2-users"
>
</select>
Excerpt from app.js
fetchUsers: function() {
this.$http.get('./api/v1/users/list', function(data, status, response) {
this.globals.users = data;
this.$nextTick(function () {
var optionsData = this.$eval('globals.users.data | userList');
console.log('optionsData', optionsData);
$('.select2-users').select2({
data: optionsData
});
});
});
},
This way works for me, but it still kinda feels hackish. If anybody has any other advice on how to do this, I would greatly appreciate it!
Thanks but I'm working on company legacy project, due to low version of select2, I encountered this issue. And I am not sure about the v-select syntax is from vue standard or not(maybe from the vue-select libaray?). So here's my implementation based on yours. Using input tag instead of select tag, and v-model for v-select. It works like a charm, thanks again #bakerstreetsystems
<input type="text"
multiple="multiple"
style="width: 300px"
v-model="supplier_id"
options="suppliers"
id="select2-suppliers"
>
</input>
<script>
$('#app').ready(function() {
var app = new Vue({
el: "#app",
data: {
supplier_id: '<%= #supplier_id %>', // We are using server rendering(ruby on rails)
suppliers: [],
},
ready: function() {
this.fetchSuppliers();
},
methods: {
fetchSuppliers: function() {
var self = this;
$.ajax({
url: '/admin_sales/suppliers',
method: 'GET',
success: function(res) {
self.suppliers = res.data;
self.$nextTick(function () {
var optionsData = self.suppliers;
$('#select2-suppliers').select2({
placeholder: "Select a supplier",
allowClear: true,
data: optionsData,
});
});
}
});
},
},
});
})
</script>