What this code does: If no flavours are selected, "Please select at least one flavour" & "You ordered and undefined is displayed". Then if a flavour is selected, nothing is displayed.
What I'm trying to do: If no flavours are selected, "Please select at least one flavour" is displayed. Then only if something is selected, I want to display "You ordered (then list whatever they ordered)" ie: You ordered Mint choc chip, cookies and cream".
I'm confused because I thought that ${flavours.slice(0, -1).join(', ')}; & You ordered {join(flavours)} would list the flavours selected. This is the code that I'm playing around with. (credit: Svelte example from Svelte website)
<script>
let scoops = 1;
let flavours = ['Mint choc chip'];
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
function join(flavours) {
if (flavours.length === 1) return flavours[0];
return `${flavours.slice(0, -1).join(', ')};
}
</script>
<h2>Flavours</h2>
{#each menu as flavour}
<label>
<input type=checkbox bind:group={flavours} value={flavour}>
{flavour}
</label>
{/each}
{#if flavours.length === 0}
<p>Please select at least one flavour</p>
<p>
You ordered {join(flavours)}
</p>
{/if}
You've got a few mistakes...
You're missing a quote (but that's a syntax error, I guess you would have noticed if you have this in your original code because it wouldn't work at all):
return `${flavours.slice(0, -1).join(', ')};
// fixed:
return `${flavours.slice(0, -1).join(', ')}`;
// you don't need the quotes here, anyway:
return flavours.slice(0, -1).join(', ');
You're also missing a else, that's why your code display nothing when some values are selected:
{#if flavours.length === 0}
<p>Please select at least one flavour</p>
{:else} <-- HERE -->
<p>
You ordered {join(flavours)}
</p>
{/if}
And, finally, why the slice? It drops the last value, is that really what you want?
// fixed
return flavours.join(', ');
Related
I'm quite new with Vue, and webdev in general. Am creating a simple hiking pack list as a school project. Have a component 'PackItem' that consists of input fields for brand, model and weight. In the backend I have a database with some items that the user can pick from. When selecting a brand, the model input will show a list of models for that brand, and when a model is chosen the weight input gets filled automatically.
<template>
<div class="inputRow">
<h1> Brand: {{ this.brand }}</h1>
<input
type="text"
list="brands"
placeholder="Brand"
v-model="this.brand"
/>
<datalist id="brands">
<option
v-for="(suggested_brand, index) in getProductBrands"
:key="index"
:value="suggested_brand"/>
</datalist>
<input
type="text"
list="models"
v-model="this.model"
/>
<datalist id="models"> -->
<option
v-for="(suggested_model, index) in brandModels"
:key="index"
:value="suggested_model"/>
</datalist>
<input
class="product-inputs"
type="number"
name="Weight"
placeholder="Weight"
v-model="this.getProductWeight"
#change="this.updateItemOnStore($event.target.value);"/>
</div>
</template>
This is the component. The datalists and input gets their values from computed properties, that talk to vuex.
export default defineComponent({
name: 'PackItem',
props: {
},
data() {
return {
model: '',
brand: '',
weight: 0,
}
},
...
...
...
computed: {
getProductBrands(){
return this.$store.getters.productsBrandList
},
getProductWeight(){
let weight = this.$store.getters.productWeight(this.brand, this.model)
return weight
},
brandModels(){
if(this.brand === ''){
return []
}
console.debug('Item %d getting models for %s', this.uuid, this.brand)
let models = this.$store.getters.brandModels(this.brand)
return models
},
},
}
This all works great for the first PackItem, but when i spawn a second PackItem from the parent, the models list for the new PackItem will be the same as the first PackItem, regardless of choosing a different brand.
Image: One item spawned
Image: Two items spawned
If I change computed property brandModels() to a method, and save the returning array from the store in local data and print it, I can see that the model list looks like it should, but those values are not showing up in the list for the user?
Image: Two items spawned with printouts
I can also see in the logs that I'm getting the correct models for the selected brand.
Item 0 getting models for Hyperlite Mountain Gear
index.js:209 Hyperlite Mountain Gear models: Windrider 3400,Junction 3400
index.js:221 Got weight 907g for product Hyperlite Mountain Gear Windrider 3400
index.js:64 Creating item 1 in category 0
index.js:199 Brands list: ZPacks,Hyperlite Mountain Gear,The OMM
index.js:225 Did not find weight for product in database!
index.js:199 Brands list: ZPacks,Hyperlite Mountain Gear,The OMM
PackItem.vue:119 Item 1 getting models for ZPacks
index.js:209 ZPacks models: Arc Blast
index.js:225 Did not find weight for product ZPacks Windrider 3400 in database!
So seems to me like I'm fetching all the correct data, and as far as I can see it should be displayed in the browser, but for some reason is not. I have no idea what is going on here...
EDIT:
As suggested by #yoduh I've fetched the whole products table into the component, and do computed values there instead of the supposedly broken computed values from getters in vuex. This has unfortunately not fixed it. Can still see in logs that brandModels() creates a correct list of models, but the datalist still shows the wrong ones.
computed: {
getProductBrands(){
let brands = []
this.dbProducts.forEach(product => {
//var json = JSON.parse(product);
var brand = product.brand;
if(!brands.includes(brand)){
console.debug('Adding brand %s to brands list', brand)
brands.push(brand)
}
} )
console.log('Brands list: %s', brands)
return brands
},
brandModels(){
if(this.brand === '') {return }
let models = []
this.dbProducts.filter(
product => product.brand === this.brand)
.forEach(product => models.push(product.model)
)
console.debug('%s models: %s', this.brand, models)
return models
},
getProductWeight(){
if(this.brand === '' || this.model === ''){ return }
let product = this.dbProducts.find(
product => (
product.brand === this.brand && product.model == this.model
))
if(product){
let weight = product.weightGrams
console.debug('Got weight %dg for product %s %s', weight, this.brand, this.model)
return weight
}
console.debug('Did not find weight for product %s %s in database!', this.brand, this.model)
return 0
},
},
Turns out the actual issue was with the pair of elements <InputText> and <datalist> on every item having the same id and list values. The value for list and id connect the two elements together, but once a second item is added, another pair of <InputText> and <datalist> with the same id and list values is created. Because no two elements should ever share the same id, the connection between inputs and datalists becomes confused and broken.
The solution then is to bind unique values to the id/list attributes. I used the uuid of each item since it's a unique number:
PackItem.vue
<InputText
type="text"
:list="`models${uuid}`"
placeholder="Model"
class="p-inputtext"
v-model="this.model"
/>
<datalist :id="`models${uuid}`">
<option
v-for="(suggested_model, index) in brandModels"
:key="index"
:value="suggested_model"
/>
</datalist>
updated codesandbox
first I'd like to thank you for your time trying to help. I am a designer and I suck at developing stuff, so I have no other option than to scream for help.
So this is the situation:
I was asked to add an image (country flag) that's gonna be dynamic, in an element that fetches info from a JSON file with, among others, Resorts (one of the elements being its country's long name, i.e. Australia) and Countries (by long name and shortcode, i.e. Australia, au).
I need to get the country shortcode printed in the img src, but the resort array is only containing its long name.
The code:
This is the way the JSON file presents the information:
{
"Countries":[
{"name":"Australia",
"code":"au",
"continent_code":"oc",
"slug":"australia"}],
"Continents":[
{"name":"Oceania",
"code":"oc",
"slug":"oceania"}],
"Resorts":[{
"id":"1",
"resort_name":"Resort Name",
"encoded_name":"resort-name",
...
"country":"Australia",
...}]
}
And this is my file bit:
const DesktopResort = ({resort}) => (
<Link href="/resort/[resort]" as={`/resort/${resort.encoded_name}`}>
<a target='_blank' className='resort-item'>
<div className="resort">
<div className="top">
<div className="title">{resort.resort_name}</div>
<img className="logo" src="/assets/img/resort-logo-sample.png" />
<span className="info">{`${resort.ski_network} - ${resort.region}`}</span>
// Down below is the "dynamic" file call
<img className="flag-icon" src={`/assets/img/flags/${resort.country}.svg`} />
</div>
<div className="arrow"><img src="/assets/img/arrow-link.png" /></div>
</div>
</a>
</Link>
)
I know its badly done right now, for this australian resort my image src is /assets/img/flags/Australia.svg and what I would need to print is of course /assets/img/flags/au.svg
How would you do it?
Thanks again!
I'd write a little helper function to look up a country code based on the country name.
Note: you'll need to handle what should happen if the country is not found, or the code is not there. I'm just defaulting to an empty string here.
const countryCode = name => {
const country = yourData.Countries.find(country => country.name === name);
return country && country.code || '';
};
Then use this when you're passing the src to your img.
<img
className="flag-icon"
src={`/assets/img/flags/${countryCode(resort.country)}.svg`}
/>
I have the following code which contains about 12 items but I only need to retrieve the first item. How can I display the first item in my list?
My code is:
<#list analysttest.rss.channel.item as item>
<div>
<h3 class="bstitle">${item.title}</h3>
<span class="bsauthor">${item.author}</span>
<span>${item.pubDate}</span>
<p>${item.description}</p>
</div>
</#list>
analysttest.rss.channel.item[0] gives the fist item, which you can #assign to a shorther name for convenience. Note that at least 1 item must exist, or else you get an error. (Or, you can do something like <#assign item = analysttest.rss.channel.item[0]!someDefault>, where someDefault is like '', [], {}, etc, depending on what you need. There's even a shorter <#assign item = analysttest.rss.channel.item[0]!> form, which uses a multi-typed "generic nothing" value as the default... see in the Manual.)
Listing is also possible, though odd for only one item: <#list analysttest.rss.channel.item[0..*1] as item>, where *1 means at most length of 1 (requires FreeMarker 2.3.21 or later). This works (and outputs nothing) even if you have 0 items.
<#assign item = analysttest.rss.channel.item[0]>
<div>
<h3 class="bstitle">${item.title}</h3>
<span class="bsauthor">${item.author}</span>
<span>${item.pubDate}</span>
<p>${item.description}</p>
</div>
i've a search field and when i search for something and nothing is found, i show a message for user, but when has content to show, the message error appear for 3~4 seconds and after this time the message is disappear and the result of search appear...
my html:
<div>
<h2>Search page</h2>
<div class="container clearfix" ng-controller="restaurantsDataCtrl" group-by="category">
<restaurants-gallery ng-show="restaurants.length" category="{{list.category}}" restaurants="{{list.restaurants}}" ng-repeat="list in restaurantsByCategory">
</restaurants-gallery>
<p ng-show="!restaurants.length">Message nothing found.</p>
</div>
</div>
what i need is set a time for this message appear and when this time is ended angular will know if show or not the message.
One thing you could do is create a flag to indicate whether your data request has started:
$scope.completed = false
then in the callback for your data request set that flag as true:
...
$http.get(...).then(function(response) {
$scope.completed = true;
$scope.restaurants = response.data;
}
if you combine that with your conditional to see if there are in fact no restaurants you can be guaranteed the message will only show up after you've tried to get data and nothing came back:
<p ng-show="!restaurants.length && completed">Message nothing found.</p>
Here's a small working example of that idea: http://plnkr.co/edit/IYtSKMHco52rHGWTT55W?p=preview
I have a typeahead that looks like this:
<input type="text" class='tk-proxima-nova degreeIn candidateProfile' placeholder="School / Institution" ng-model="university" typeahead="university.Service_Provider_Name for university in universitySuggest($viewValue)" />
it returns JSON that looks similar to this:
[{"Service_Provider_ID":133368,"Service_Provider_Name":"Duke University","Service_Provider_Desc":null,"NAICS_Id":1809},{"Service_Provider_ID":196282,"Service_Provider_Name":"Duke University Medical Center","Service_Provider_Desc":null,"NAICS_Id":1809},{"Service_Provider_ID":222220,"Service_Provider_Name":"Duke University Psychology Internship","Service_Provider_Desc":null,"NAICS_Id":1809},{"Service_Provider_ID":223427,"Service_Provider_Name":"Duke University Medical Center Psychology Internship","Service_Provider_Desc":null,"NAICS_Id":1809}]
When I select the option from the typeahead it puts the school name in the field as it's supposed to, but is there a way to also set the id to another hidden input so I can send that with my selected data as well. The ID is what is important here, but the name is needed for viewing.
Yes, you can do something like :
<input type="text"
ng-model="selected"
typeahead-on-select="changeSelect($item)"
typeahead="university as university.Service_Provider_Name for university in universities | filter:{Service_Provider_Name:$viewValue} " />
Here is the Plunkr
I've a little indented your code for more clarity.
In addition to the comments Apercu gave above regarding the format of the typeahead="..." ,you need to have a typeahead-input-formatter like this
typeahead-input-formatter="univChosen($model)"
$scope.univChosen = function(theId) {
var theItem = jQuery.grep($scope.universities,function(p) {
return p.Service_Provider_ID == theId});
return theItem[0].Service_Provider_Name;
};
I'm assuming here that $scope.universities contains the complete list of all the items in the typeahead choices. I've used jQuery here to find the matching item but you can just do a for(...) loop if you want. The function returns the string that will actually get displayed, in this case the name.