I'm fairly new to vue.js but I couldn't seem to find a concrete answer for what I'm looking for but I also am new enough that I might have been looking right at the answer and not known it. So.
What I'm trying to do is create a 1 page application that tracks turns basically.
It will:
takes user input on how many users are participating
ask for the names of all the users
use the names which I currently have saved in an array, to build user cards, just showing the name of each user.
You will click your name when your turn is over and the next persons card/button will 'raise' and then they click it, etc.
I'm currently having a hard time with that 3rd part. I need to figure out how I can pass this array of users to my other component's data object so I can use vue.js to loop and just spit the cards out.
initial-start.vue - template
<template lang="html">
<div>
<div>
<h1>How many users?</h1>
<input type="number" id="myNumber" value="0">
<button v-on:click="getUsers">Submit</button>
<app-area v-if="users > 0 && users < 5"></app-area>
</div>
</div>
</template>
initial-start.vue - script
<script>
export default {
methods:{
getUsers(){
var counter = 0;
var users = [];
var x = document.getElementById("myNumber").value;
if (x <= 0){
alert('Please choose a number greater than 0.');
}else if(x > 4){
alert('Maximum 4 users!');
}else{
alert('thank you for that.')
}
while(counter < x){
name = prompt('Please enter your names.');
users.push(name);
counter++;
}
return users;
}
}
}
</script>
app-area.vue - template
<template lang="html">
<div>
<h1>Turn: {{ turn }}</h1>
<userCards></userCards>
</div>
</template>
app-area.vue - script
<script>
export default {
data() {
return{
turn: 0,
users: []
}
},
props: ['users']
}
</script>
The question is, "How do I get the array from the getUsers() function, into the app area where I would be able to loop like
<userCards v-for="user in users"></userCards>
and have a card/button for each person entered ?"
You should save the value of getUser in your data, and then you can pass that to your app-area component.
data: {
users: 0
}
methods:{
getUsers(){
#users = 2;
}
And then in your html:
<app-area v-if="users > 0 && users < 5" v-bind:users="users"></app-area>
Now your app-area template has access to users. So you can pass each user to userCard component
<userCards v-for="user in users" v-bind:user="user"></userCards>
Related
I'm working with BootstrapVue. First I want to explain my code shortly:
I have a v-for with inputs, these I can create as often as I want with a b-button -> addElement. Or I can select an item from my b-dropdown - there I am checking the length of my selected and push the amount of my arrayLength as my inputs.
-> e.g. selected array = ['1111', '2222', '3333'] -> arrayLength = 3 -> 3 inputs
Now I need to get a function fired automatically (marked as ???) when exactly this case happend that I'm selecting something from my dropdown - e.g. is my arrayLength = 3, it should be fired 3 times with correct id, indexParent and indexChild. How can I solve that?
I need this above named parameters in my methods..
Thanks in advance!
<div v-for="(id, indexChild) in inputs" :key="indexChild">
<b-dropdown text="Select" right variant="success">
<b-dropdown-item v-for="(item, indexDropdown) in json" :key="indexDropdown" #click="trySomething(item.Number)">{{item.Name}}</b-dropdown-item>
</b-dropdown>
<b-button v-b-toggle="'newElement'+indexParent+indexChild">Element {{indexChild + 1}}></b-button>
<b-collapse :id="'newElement'+indexParent+indexChild"
<div ???="getValues(id, indexParent, indexChild)"> <!-- how can I fire this when created? -->
<!-- Do some more code -->
</div>
</b-collapse>
</div>
<!-- create new "item"-elements -->
<div>
<b-button #click="addElement()">Add element</b-button>
</div>
methods: {
addProduct() {
this.inputs.push({})
},
trySomething(itemValue) {
var array = [];
const products = this.otherJSON.find((i) => i.Number === itemValue);
for (let key in products.ID) {
array.push(products.ID[key]);
}
this.getLengthArray = array.length;
this.inputs.splice(0) //at first I need to delete all to push exact inputs based on arrayLength
for (let i = 0; i < this.getLengthArray; i++) {
this.inputs.push({});
}
},
getValues(id, indexParent, indexChild) { //Here I need the parameters id, indexParent, indexChild
}
},
data() {
return {
inputs: [{}],
}
},
props: ["indexParent"]
I've just used:
<div v-if="getValues(id, indexParent, indexChild)" v-once></div>
and it worked out for me!
I am working on Angular 7 application using angular primeng and new to Angular. This is the scenario.
a) Fetched user data from external api but i need to display user based on the input search. I tried to read the following documentation "Multiple" select https://www.primefaces.org/primeng/#/autocomplete but not able to display value based on the search.
b) I have added Add button on the right side of search section. Upon clicking the add button, user should be displayed below it.
Please see the code snippet.
home.component.html
<div class="ui-g">
<div class="ui-g-9">
<p-autoComplete [(ngModel)]="patients" [suggestions]="filteredUsersMultiple" (completeMethod)="filterCountryMultiple($event)"
[minLength]="1" placeholder="Users" field="name" [multiple]="true">
</p-autoComplete>
<ul>
<li *ngFor="let c of patients">{{c}}</li>
</ul>
</div>
<div class="ui-g-3">
<button pButton type="button" label="Add" class="ui-button-secondary"></button>
</div>
</div>
country.service.ts
export class CountryService {
constructor(private http: HttpClient) { }
getUsers() {
return this.http.get('https://jsonplaceholder.typicode.com/users');
}
}
home.component.ts
filterCountryMultiple(event) {
this.userService.getUsers().subscribe(
(val: any[]) => {
this.filteredUsersMultiple = val.map(user => user.username);
console.log(this.filteredUsersMultiple);
}
)
}
It would be really helpful if somebody could help me to find on how to display user data based on searching and display it when clicking Add button.
As per the doc https://www.primefaces.org/primeng/#/autocomplete use completeMethod to query for the results. The event contains the query text. Then create a search function and compare both query and the filteredUsersMultiple.
filterCountryMultiple(event) {
let query = event.query;
this.userService.getUsers().subscribe(
(val: any[]) => {
this.filteredUsersMultiple = val.map(user => user.username);
this.checkUsers = this.mySearch(query, this.filteredUsersMultiple);
});
}
mySearch(query, filteredUsersMultiple: any[]):any[] {
let filtered : any[] = [];
for(let i = 0; i < filteredUsersMultiple.length; i++) {
let data = filteredUsersMultiple[i];
if(data.toLowerCase().indexOf(query.toLowerCase()) == 0) {
filtered.push(data);
}
}
I have multiple buttons on one page, "Add to cart" buttons where each button has a unique id attribute.
I want to hide a particular button when the user clicks on it.
The issue:
What's happening currently is that when a user clicks on a button 1 it hides, then clicks on button 2 it hides but on the same time it shows button 1
The expected behavior:
When the user clicks on button 1 it should hide and keep hiding even after clicking on button 2
P.S. the information of the buttons (products) gets added to an array.
Current code:
Html:
<div *ngFor="let product of products; let i = index">
<div *ngIf="hideButton != i" [attr.id]="i" class="addButton" (click)="addToCart(product, i)">ADD</div>
</div>
JS
addToCart(itemDetails, index) {
this.hideButton = index;
}
You need an array of hidden buttons and you need to add the index to that array:
JS:
// at the top
hiddenButtons = [];
addToCart(itemDetails, index) {
this.hiddenButtons.push(index);
}
HTML:
<div *ngFor="let product of products; let i = index">
<div *ngIf="hiddenButton.indexOf(i) === -1" [attr.id]="i" class="addButton" (click)="addToCart(product, i)">ADD</div>
</div>
If you have a cart to which products are being added, you can look in the cart to check whether the product already exists in it, and use that to decide whether to display the ADD button.
If your product objects can have more properties to them, you can do away with indexes completely.
HTML
<div *ngFor="let product of products">
<div *ngIf="productInCart(product)" [attr.id]="product.id" class="addButton" (click)="addToCart(product)">ADD</div>
</div>
JS
productInCart(product) {
return this.products.findIndex(p => p.id==product.id)!=-1;
}
addToCart(product) {
this.products.push(product);
}
<div *ngFor="let product of products; let i = index">
<div *ngIf="!product.isHidden" [attr.id]="i" class="addButton" (click)="addToCart(product, i)">ADD</div>
</div>
In component
addToCart(itemDetails, index) {
itemDetails.isHidden = true;
this.products[index] = itemDetails;
}
Logic behind this is to create a new property in product when it clicked for add to cart. Initially there will be no property with name isHidden. SO, it will return undefined and undefined will treat as false.
I would suggest the following:
<div *ngFor="let product of products; let i = index">
<div *ngIf="!isInCart(product)" [attr.id]="i" class="addButton" (click)="addToCart(product, i)">ADD</div>
</div>
private hiddenProducts = new Set<FooProduct>();
products: FooProduct[] = [];
loadProducts(){
this.products = // some value
hiddenProducts = new Set<FooProduct>();
}
isInCart(product: FooProduct): boolean {
return this.hiddenProducts.has(product);
}
addToCart(product: FooProduct, index: number){
// optional: check if the element is already added?
this.hiddenProducts.add(product);
// rest of your addToCart logic
}
Why using a set instead of a simple array?
Performance: access time is constant.
Why not use the index as identifier?
Weak against list mutations (filter, reorder, etc)
i've built a website i.e. a blog which runs at the moment locally with the full MEAN (MongoDB, Node, Angular and Express) stack. I've already implemented the backend, but have some problems for the frontend. Most of it works. I load the content dynamically i.e. every time the user reached the bottom of the page, i load the next three blog posts. I've also added some voting (up and down). I don't know if i did it right, but it works. I have an array which contains all blog post. Every time the user reached the bottom, i add them via .push to my array. This array is shown via *ngFor, so it contains all current blog post and "future posts". The question arise how i add the voting in an efficient way? I've added an array, every time a user clicks "vote up" i check if the post id is already in that array, if not i add it and increment the vote by one and store the value also in the database by a request. If the user regrets his decision he can anytime press the same button to bring the initial state back, because now the post id is in that array and therefore a click on the same button is not a vote up anymore rather we decrease the vote and remove the post id from the array (and store the new value in the database). The user can now again vote up, because the post id is no more in that array, etc.
Is there any better approach to implement the voting? I think if the user loads 1000 blog articles and votes many articles and afterwards regrets one, then the loading time can eventually very high (finding a value in a big array, etc.).
The point why i've written here is the following. The main problem is that i want to add a commenting box to each blog post. But how do i do that? Should i create each time the browser loads 3 article 3 commenting boxes? "This" is not really a problem, but what happened in the same scenario as already mentioned? What happened if the user reads 20 articles one after the other. Then 20 commenting boxes are created and are binded to some variables, etc. It cant be efficient in that way. I thought i could create one commenting box and every time the user clicks on an anchor tag (named "Reply") the commenting box will be opened. But there exists only one box. It will be shown in the appropriate place. But i don't know how to implement this with angular. :(
I'd appreciate any hint or help. :)
UPDATE 26.11.2017: i share some content from my project, so can probably better understand what i mean.
This is my blog.component.ts
import { Component, OnInit } from '#angular/core';
import { CarouselConfig } from 'ngx-bootstrap/carousel';
import { BlogPost } from '../shared/blogPost';
import { BlogPostFactory } from '../shared/blogPost-factory';
import { DataService } from '../data.service';
#Component({
selector: 'ca-blog',
templateUrl: './blog.component.html',
styleUrls: ['./blog.component.css'],
providers: [{provide: CarouselConfig, useValue: {interval: 1500, noPause: true}}]
})
export class BlogComponent implements OnInit {
private blogPosts: BlogPost[] = [];
private heartsUp: string[] = [];
private likesUp: string[] = [];
constructor(private dataService: DataService) {
window.onscroll = () => {
let windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
let body = document.body;
let html = document.documentElement;
let docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
let windowBottom = windowHeight + window.pageYOffset;
if(windowBottom >= docHeight) {
let urlSearchParams = { last: this.blogPosts.length };
this.dataService.getBlogPosts(urlSearchParams).subscribe( result => {
for(let i=0; i<result.length; i++){
this.blogPosts.push(BlogPostFactory.fromObject(result[i]));
}
});
}
};
}
ngOnInit() {
this.dataService.getBlogPosts().subscribe( result => {
for(let i=0; i<result.length; i++){
this.blogPosts.push(BlogPostFactory.fromObject(result[i]));
}
});
}
incrementHearts(index) : void {
let idIndex : number = this.heartsUp.indexOf(this.blogPosts[index].id);
if(idIndex != -1){
this.blogPosts[index].hearts--;
this.heartsUp.splice(idIndex, 1);
} else {
this.blogPosts[index].hearts++;
this.heartsUp.push(this.blogPosts[index].id);
}
this.dataService.editBlogPost(this.blogPosts[index].id, { hearts: this.blogPosts[index].hearts }).subscribe().unsubscribe();
}
incrementLikes(index) : void {
let idIndex : number = this.likesUp.indexOf(this.blogPosts[index].id);
if(idIndex != -1){
this.blogPosts[index].likes--;
this.likesUp.splice(idIndex, 1);
} else {
this.blogPosts[index].likes++;
this.likesUp.push(this.blogPosts[index].id);
}
this.dataService.editBlogPost(this.blogPosts[index].id, { likes: this.blogPosts[index].likes }).subscribe().unsubscribe();
}
}
and blog.component.html is given by
<div class="container">
<div *ngFor="let blogPost of blogPosts; let i=index">
<div *ngIf="i !== 0">
<hr class="my-5">
</div>
<div class="row">
<div class="col">
<article>
<section>
<header style="" class="mb-4">
<h1 style="display:inline" class="m-0 p-0">{{ blogPost.title }}</h1><small style="opacity:0.5" class="d-block d-sm-inline ml-sm-3">zuletzt bearbeitet am {{ blogPost.lastEdited | date }} von <strong>{{ blogPost.author.username }}</strong></small>
<p class="mt-1 mt-sm-auto"><i class="fa fa-tags mr-2"></i><a *ngFor="let hashtag of blogPost.hashtags" href="#" class="badge badge-secondary mr-2">#{{ hashtag }}</a></p>
</header>
<div class="m-0 p-0" [innerHTML]="blogPost.body">
</div>
<div>
<small class="heartUp"><a (click)="incrementHearts(i)" [ngStyle]="{'color':heartsUp.includes(blogPost.id) ? '#E63946' : 'rgba(0,0,0,0.5)'}"><i class="fa fa-heart mr-1"></i>{{ blogPost.hearts }}</a></small>
<small class="likeUp"><a (click)="incrementLikes(i)" [ngStyle]="{'color':likesUp.includes(blogPost.id) ? '#3b5998' : 'rgba(0,0,0,0.5)'}"><i class="fa fa-thumbs-up mr-1"></i>{{ blogPost.likes }}</a></small>
<small class="reply"><a><i class="fa fa-mail-reply mr-1"></i>Reply</a></small>
</div>
</section>
</article>
</div>
</div>
</div>
</div>
I hope this visualize my problem better.
for your first question it depends how you think about it, you want to disable the vote up? if yes then you need to treat it on the front end side, if not, you can the user let press the vote up and then in the server check for the user id and send a error message back ("you already voted"), for your second question i don't know very well what you mean with there is just 1 box? you mean you have 1 box component? if that makes you confusion, just think on the component as a class that you can instantiate in objects, same for components you can create a lot of since very one is a diferent instance.
I'm trying to show a stock message whether the item has enough quantity in the API object for the user to add the right amount of quantity in the basket. Managed to do it with single item but since the items in the basket is an Array having an issue to show the stock message on the right item index in ng-repeat
HTML template code : --
<div ng-repeat="item in vm.basketList">
...
<button ng-click="vm.update(item)">Update qty</button>
// stock message
<span ng-if="vm.checkStock == false" class="text-warning">Stock is not enough.</span>
</div>
controller update qty: --
vm.update = function (item) {
// Will be needing this var for later
var updateCartItem = {
....
Products: [item]
}
// Date required for HTTP stock check
var xd = {
...
ProductIds: [updateCartItem.Products[0].product_idField]
}
itemService.itemStock(xd)
.then(function (response) {
console.log('Checking stock...');
for (var i = 0; i < response.data.Results.length; i++) {
var stockElement = response.data.Results[i];
if (stockElement.qtyField >= updateCartItem.Products[0].qtyField) {
console.log('Stock is enough go ahead update QTY');
vm.checkStock = true;
} else {
console.log('Stock is not enough');
vm.checkStock = false;
}
}
}
Here's an image of I'm trying to achieve, I marked them as cross and check (cross = not enough stock, check = enough stock so to update). So message not enough stock should only show on the item with cross.
My question is how do I show the message in the correct index for item that doesn't have enough stock, when I click the button to update the qty?
Hope this question make sense to some and would be able to answer.
have checkStock property on item.
vm.checkStock = false;
// make this
item.checkStock = false
In repeater
<span ng-if="vm.checkStock == false" class="text-warning">Stock is not enough.</span>
Change this to
<span ng-if="item.checkStock == false" class="text-warning">Stock is not enough.</span>