First I get data set from database using API and add a custom field called "isShare" and add value "false" in the initial state, then I iterate these data using v-for. Then I wanted to change the value of the "isShare" depending on the click event of another element.
I tired using v-show and v-if both. But in both cases, I couldn't get the result I wanted.
This is the code I used to get data from database and add a custom field called "isShare"
loadLessons() {
axios.get('/api/find/non_auth_get_lessons')
.then(({data}) => {
(this.lessons = data)
this.lesson_images = []
this.lessons.forEach(lesson => {
this.lesson_images.push(`${window.location.protocol + '//'
+ window.location.hostname}/lesson_covers/${lesson.lesson_cover_image}`)
lesson.isShare = false
});
},
(error) => {
this.loadLessons()
}
)
},
This is how I iterate data.
<carousel-3d class="slider_container lesson_container" :disable3d="true"
:space="320" :clickable="false" :controls-visible="true" style="height: 100%">
<slide class="card m-1 lma_view bg-white" v-for="(lesson, index) in lessons" :index="index" :key="index" >
<div class="card_img_wrapper" >
<img :src="lesson_images[index]" class="card-img-top" alt="..." v-
if="lesson.lesson_cover_image !== null" draggable="false">
</div>
<div class="card-body" >
<h5 class="card-title welcome_page_card_title" v-
html="$options.filters.truncate(lesson.lesson_title, 25, '...')"></h5>
<div class=" d-flex flex-column welcome_page_lesson_details">
<small class="card-text">Subject : <span class="text-muted">{{
lesson.subject | truncate(30, '...') }}</span></small>
<small class="card-text">Material Type : <span class="text-muted">{{
lesson.meterial_type === '1' ? 'Text based' : (lesson.meterial_type ===
'2' ? 'Video based' : 'Document based') }}</span></small>
<small class="card-text">Language : <span
class="text-muted">{{ lesson.language === '1' ? 'Sinhala' :
(lesson.language === '2' ? 'English' : 'Tamil') }}</span></small>
</div>
<hr/>
<div class="text-center">
View
<span class=""><i class="fas fa-share-alt share_icon"
#click="sharePopup(lesson, index)"></i></span>
<!--<div class="share_options" v-show="lesson.isShare"></div>-->
<div class="share_options">{{lesson.isShare}}</div>
<span class="badge pull-right" :class="lesson.access_type === '1' ?
'badge-success' :
(lesson.access_type === '2' ? 'badge-warning' : 'badge-info')">
{{ lesson.access_type === '1' ? 'Free'
: (lesson.access_type === '2' ? 'Restricted' : 'Paid') }}
</span>
</div>
</div>
</slide>
</carousel-3d>
When I click on the element witch has "share_icon" class I need to change the value of "isShare"
When I clicked on the "share_icon" it trigger the sharePopup function and changes the value of "isShare" but does not render
sharePopup(lesson, index){
lesson.isShare = !lesson.isShare;
}
You should access your array by the given index which is passed as parameter as follows:
sharePopup(lesson, index){
this.lessons[index].isShare = !lesson.isShare;
this.$set(this.lessons,index, this.lessons[index])
}
learn more about $set function here
Related
I want to filter my checkboxes I search it on internet there was information but I couldn't work it with my code.
This is the webpage
I want when you click on the checkbox it must be same as the category.
This is some code of the checkbox:
<div>
<input class="form-check-input checkboxMargin" type="checkbox" value="All" v-model="selectedCategory">
<p class="form-check-label checkboxMargin">All</p>
</div>
This is my grey box layout:
<div class="col-sm-12 col-md-7">
<div class="card rounded-circle mt-5" v-for="item of items" :key="item['.key']">
<div>
<div class="card-body defaultGrey">
<h5 class="card-title font-weight-bold">{{ item.name }}</h5>
<div class="row mb-2">
<div class="col-sm ">
<div class="row ml-0"><h6 class="font-weight-bold">Job:</h6><h6 class="ml-1">{{ item.job }}</h6></div>
<div class="row ml-0"><h6 class="font-weight-bold">Category:</h6><h6 class="ml-1">{{ item.categories }}</h6></div>
<div class="row ml-0"><h6 class="font-weight-bold">Location:</h6><h6 class="ml-1">{{ item.location }}</h6></div>
<div class="row ml-0"><h6 class="font-weight-bold">Niveau:</h6><h6 class="ml-1">{{ item.niveau }}</h6></div>
<div class="row ml-0"><h6 class="font-weight-bold">Availability:</h6><h6 class="ml-1">{{ item.availability }}</h6></div>
<h6>{{ item.info }}</h6>
<div class="row ml-0"><h6 class="font-weight-bold">Posted:</h6><h6 class="ml-1">{{ item.user }}</h6></div>
</div>
</div>
<div class="row">
<div class="col-xs-1 ml-3" v-if="isLoggedIn">
<router-link :to="{ name: 'InternshipDetails', params: {id: item['.key']} }" class="btn bg-info editbtn">
Details
</router-link>
</div>
<div class="col-xs-1 ml-3 mr-3" v-if="isLoggedIn && item.user == currentUser">
<router-link :to="{ name: 'Edit', params: {id: item['.key']} }" class="btn btn-warning editbtn">
Edit
</router-link>
</div>
<div class="col-xs-1" v-if="isLoggedIn && item.user == currentUser">
<button #click="deleteItem(item['.key'])" class="btn btn-danger dltbtn">Delete</button>
</div>
</div>
</div>
</div>
</div>
</div>
And I have my object here but how can I filter my grey boxes with category:
selectedCategory: []
Use computed properties:
computed: {
filteredItems(){
if(this.selectedCategory.length == 0) return this.items;
return this.items.filter(el => {
for(let i = 0; i < this.selectedCategory.length; i++){
if(el.categories == this.selectedCategory[i]) return el;
}
});
}
}
Then you go for v-for="item of filteredItems"
I didnt tested it. If you provide me more code i could help you more
You need to create one-way binding between your CategoryCheckBox component and your ListCard component.
Because you provided separated code when I can not reproduce it to give you a solution based on your own, I suggest this example to explain my solution.
Step One:
You have many ways to CRUD your items using a Plugins or Vuex or global instance, in my example I used global instance in main.js
Vue.prototype.$myArray = ["Books", "Magazines", "Newspaper"];
I assume you created your items data in the ListCards component
Step Two:
You need to add #change event in your checkbox to handle checked and unchecked states. I used static data (Book) for value.
<label>
<input type="checkbox" value="Books" #change="handleCategory($event)" /> Books
Now, let's implement handleCategory method, but before that, as we know that Checkbox and ListCards are independents which means we need to define a bus to create event communication between them and this is your issue so we define the bus inside the main.js
Vue.prototype.$bus = new Vue({});
Now we define handleCategory like this :
methods: {
handleCategory(e) {
this.$bus.$emit("checkCategory", e);
}
}
Step Three:
How can our ListCards listen to this event ?
By call $bus.$on(..) when the component is created ( Hope you know what Vue lifecycle methods mean )
created() {
this.$bus.$on("checkCategory", e => this.checkCategory(e));
},
When the user click the check box the component runs handleCategory then runs checkCategory inside ListCard.
Inside my ListCard, I have categories and originalCategories as data
data() {
return {
categories: this.$myArray,
originalCategories: this.$myArray
};
},
and a template :
<ul v-for="(category,index) in categories" :key="index">
<CategoryItem :categoryName="category" />
</ul>
and a created method ( lifecycle )
created() {
// $bus is a global object to communicate between components
this.$bus.$on("checkCategory", e => this.checkCategory(e));
},
and our filtering methods :
methods: {
checkCategory(e) {
let target = e.target;
let value = e.target.value;
target.checked
? this.filterCatergories(value)
: (this.categories = this.originalCategories);
},
filterCatergories(value) {
this.categories = this.categories.filter(val => val === value);
}
}
What's important for you is :
this.categories.filter(val => val === value); //will not do nothing
const categories=this.categories.filter(val => val === value); //will update the view.
And you can change the code or make it better and simple.
#Update
You can also use computed properties but because we have a parameter here which is the category name we need get and set.
computed: {
filteredItems: {
get: function() {
if (this.selectedCategory.length == 0) return this.items;
return this.items.filter(value => {
for (const category of this.selectedCategory) {
if (value == category) return value;
}
});
},
set: function(category) {
this.selectedCategory.push(category);
}
}
},
methods: {
checkCategory(e) {
let target = e.target;
let value = e.target.value;
target.checked
? (this.filteredItems = value)
: this.selectedCategory.pop(value);
}
}
I have two lists , user can drag items from list 1 to list 2 and there is a button with text input so user can add his own input to the list 2 which will be automatically updated in my MYSQL database using axios.
This is AddItem script
addItembh(){
var input = document.getElementById('itemFormbh');
if(input.value !== ''){
// this line makes a new article with input value but no attribute :(
this.tasksNotCompletedNew.unshift({
behv_skilldesc:input.value
});
axios.post('../joborder/addAttrib', {
behv_skilldesc: input.value,
type:'behvnew',
joborder_id: this.joborder_id ,
alljobs_id: this.alljobs_id
}).then((response) => {
console.log(response.data);
}).catch((error) => {
console.log(error);
});
input.value='';
}
},
To be clear on the question : I need to assign an attribute to my new article thats getting created so I can find the text of that attrib later on deleteItem method
UPDATE :
<template>
<div class="row">
<div class="col-md-4 col-md-offset-2">
<section class="list">
<header>Drag or Add Row Here</header>
<draggable class="drag-area" :list="tasksNotCompletedNew" :options="{animation:200, group:'status',handle:'disabled'}" :element="'article'" #add="onAdd($event, false)" #change="update">
<article class="card" v-for="(task, index) in tasksNotCompletedNew" :key="task.prof_id" :data-id="task.prof_id" #change="onChange">
<span >
{{ task.prof_skilldesc }}
</span>
<span v-if="task.prof_skilldesc !== 'Drag Here'">
<button class="pull-left" #click="deleteItem(task.prof_id) + spliceit(index)" ><i class="fa fa-times inline"></i></button>
</span>
</article>
<article class="card" v-if="tasksNotCompletedNew == ''">
<span>
Drag Here
</span>
</article>
</draggable>
<div>
<input id='itemForm' />
<button v-on:click='addItem' class="btn btn-theme btn-success" style='margin-top:5px;' >Add a row </button>
</div>
</section>
</div>
<div class="col-md-4">
<section class="list">
<header>List of Skills ( Hold left click )</header>
<draggable class="drag-area" :list="tasksCompletedNew" :options="{animation:200, group:'status'}" :element="'article'" #add="onAdd($event, true)" #change="update">
<article class="card"
v-for="(task, index) in visibleskills"
:key="task.prof_id" :data-id="task.prof_id"
>
{{ task.prof_skilldesc }}
<div v-if="index == 4" style="display:none" >{{index2 = onChange(index)}}</div>
</article>
<pagination
v-bind:tasksCompletedNew ="tasksCompletedNew"
v-on:page:update ="updatePage"
v-bind:currentPage ="currentPage"
v-bind:pageSize="pageSize">
</pagination>
</draggable>
</section>
</div>
</div>
</template>
So on Add a row our method will be called .
Thanks for any help
I'm working on a system where the user can select a product and go to the next page. This product then gets saved in the session using laravel sessions. When the user decides to go to the next page and come back. The chosen product is indeed saved in the session, but the is no way for them to see what product they have chosen because the class isn't applied to the product that was chosen.
The code may clarify it better:
#foreach($themes as $theme)
<div class="col-md-4 mb-4">
<div class="card theme-card card-hover depth-2 border-0" id="theme-id-{{$theme->id}}">
<a href="" class="theme-link" data-toggle="modal" data-target="#theme{{ $theme->id }}">
<div class="card-image" style="height: 200px; background-image: url('/uploads/{{ $theme->thumbnail }}'); background-size: cover; background-position: center center;"></div>
<div class="card-body">
<div class="row">
<div class="col-md-2 vertical-center">
<i class="fab fa-magento fa-lg"></i>
</div>
<div class="col-md-10">
<p class="m-0">{!! str_limit($theme->name, $limit = 32, $end = '...') !!}</p>
<small class="text-muted">{{ $theme->productable_type }}</small>
</div>
</div>
</div>
</a>
<div class="card-footer bg-white border-0 text-right pt-0">
<div class="row">
<div class="col-md-6 text-left">
<input type="hidden" class="theme-name" name="theme[{{$theme->id}}]">
{{--<input type="hidden" value="{{ $theme->composer_package }}">--}}
<button data-card-id="{{$theme->id}}" class="btn btn-orange btn-sm btn-theme-choice">Kiezen</button>
</div>
<div class="col-md-6 text-right">
<span style="font-size: 20px;" >€ {{ $theme->price }} EUR</span>
</div>
</div>
</div>
</div>
</div>
#endforeach
In the above code, I for each trough every so-called "Theme" and I'm giving the ID of the theme as a data target and as ID. Then in my Javascript code, I do the following:
$('.btn-theme-choice').on('click', function (event) {
event.preventDefault();
newSelectedCardId = $(event.target).data('card-id');
if(cardId === null) {
cardId = newSelectedCardId;
} else if (cardId !== newSelectedCardId) {
$('#theme-id-' + cardId).removeClass('theme-chosen').find("input[name='theme["+cardId+"]']").val('');
cardId = null;
}
var card = $('#theme-id-' + cardId );
card.toggleClass('theme-chosen');
selectedCardInput = card.find("input[name='theme["+cardId+"]']");
if( !$('.theme-card').hasClass('theme-chosen') ) {
selectedCardInput.val('');
} else if ( $('.theme-card').hasClass('theme-chosen') ) {
selectedCardInput.val('selected');
}
console.log(selectedCardInput);
});
Here I add the class to the card so the user can See which card they've selected. This choice then gets saved in the session using some PHP code in the controller
if( $theme == null ) {
return redirect('plugins');
} elseif( $theme != null ) {
foreach($this->predefinedArray as $value) {
$request->session()->put('chosen_theme.' . $value, $theme->$value);
}
$request->session()->put('chosen_theme.composer_package', $theme->productable->composer_package);
return redirect('plugins');
}
problem
How can I read the session and add the class to the card with the IDs that were stored in the session so if the person leaves the page and comes back, they can see what cards they've selected?
Thanks in advance
Try this in your view..
<div class="card theme-card card-hover depth-2 border-0 {{ \Session::get('chosen_theme.composer_package') == $theme->id ? 'theme-chosen' : '' }}" id="theme-id-{{$theme->id}}">
Whenever the theme id is in the session and the page loaded the class will be added, and if it is not in the session then the class won't be added.
Let me know the result..
I'm using Vue v2
I'm trying to change only the properties of the selected element. See, when the response is marked after the click, it should change to a red color with a text that says 'Unmark'. And vice versa: if the button is clicked again (which now would say 'Unmark'), it should change to a green color and the text would be 'Mark'. Alas, it works.... Nevertheless, my code applies the change to every element inside the v-for; I only want that to happen to the selected element.
I've thought about using a Component to check if somethings changes, but first I'd like to see if there's a solutions for this. ANy help will be appreciated
Here's my code:
<div class="search-results">
<div class="activity-box-w" v-for="user in users">
<div class="box">
<div class="avatar" :style="{ 'background-image': 'url(' + user.customer.profile_picture + ')' }">
</div>
<div class="info">
<div class="role">
#{{ '#' + user.username }}
</div>
<div>
<div>
<p class="title">#{{ user.customer.name }}
#{{user.customer.lastname}}
</p>
</div>
</div>
</div>
<div class="time">
<input type="button" class="btn btn-sm btn-primary" v-on:click.prevent="markUser(user)" v-model="text"
v-bind:class="[{'green-border':notMarked}, {'red-border':marked}]" v-cloak v-if="!action"
:disabled="action"></input>
</div>
</div>
</div>
Now for the script:
new Vue({
el: '#engage-panel',
data: {
users: [],
mark: {'id' : '', 'marks' : ''},
text: 'Mark', //Migth change to Unmark later on
action: false,
marked: null,
notMarked: null,
},
methods:
{
markUser: function(user){
this.mark.id = user.id;
this.action= true;
Vue.http.headers.common['X-CSRF-TOKEN'] = $('meta[name="csrf-token"]').attr('content');
this.$http.put('/mark/user', this.mark).then(response => {
if(response.body === "marked")
{
this.mark.marks="true";
this.text = 'Unmark';
this.marked= true;
this.notMarked= false;
this.action= false;
}else{
this.mark.marks="false";
this.text = 'Mark';
this.marked= false;
this.notMarked= true;
this.action= false;
}
}).catch(e => {
this.action= false;
});
}
}
You can use $event.target on click if you just need to toggle css class.
fiddle here
But it's true that it's easier if a user has a status like marked = true/false for example, you just need to bind class like :
<input :class="{ 'green-border': user.marked, 'red-border': !user.marked }">
The issue my code applies the change to every element you met is caused by every user in v-for="user in users" uses one same object to indicates it is marked or not.
If your users data has one property like status to save current status (like unmark, mark etc), it is very simple, just change to next status when click mark button.
If your users data doesn't have that property, you need to create one dictionary, then save the users already clicked as key, the status for the user will be the value.
Below is one demo:
app = new Vue({
el: "#app",
data: {
users1: [{'name':'abc', 'status':'none'},
{'name':'xyz', 'status':'none'}],
users2: [{'name':'abc'}, {'name':'xyz'}],
selectedUsers: {}
},
methods: {
getNextStatusBaseOnRoute: function (status) {
if(status ==='marked') return 'marked'
let routes = {'none':'unmark', 'unmark':'marked'}
return routes[status]
},
markUser1: function (item) {
item.status = this.getNextStatusBaseOnRoute(item.status)
},
markUser2: function (item) {
let status = item.name in this.selectedUsers ? this.selectedUsers[item.name] : 'none'
// remember to use vue.$set when adding new property to one object
this.$set(this.selectedUsers, item.name, this.getNextStatusBaseOnRoute(status))
}
}
})
.marked {
background-color:green;
}
.unmark {
background-color:yellow;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Case 1: </h2>
<div v-for="(item, index1) in users1" :key="'1'+index1">
<span>{{item.name}}:</span><span :class="[item.status]">{{item.status}}</span><button #click="markUser1(item)">Mark</button>
</div>
<h2>Case 2: </h2>
<div v-for="(item, index2) in users2" :key="'2'+index2">
<span>{{item.name}}:</span><span :class="[item.name in selectedUsers ? selectedUsers[item.name] : 'none']">{{item.name in selectedUsers ? selectedUsers[item.name] : 'none'}}</span><button #click="markUser2(item)">Mark</button>
</div>
</div>
For Vue3, you can also store the index of the selected element
<ul role="list" class="">
<li class="relative" v-for="(image, index) of images" :class="selectedImage == index? 'border-indigo-500 border-2': 'border-transparent'" >
<div #click="selectedImage = index" class="">
<img :src="image" alt="" class="object-cover pointer-events-none group-hover:opacity-75">
</div>
</li>
</ul>
I'm facing a little problem with order by that i could not underdstand.
When i reoder the array i can see that every thing is working perfectly, but the problem is in the ng-click function of the list.
For example if i have this array:
$scope.ticketsEncour = [
{_id: 1, vendeur: 'John Doe', date_ticket: '18:52', total: 50},
{_id: 2, vendeur: 'Foo Bar', date_ticket: '12:12', total: 20},
{_id: 3, vendeur: 'John Smith', date_ticket: '11:02', total: 10},
{_id: 4, vendeur: 'Test Test', date_ticket: '05:10', total: 6000}
]
And this is ther reoder and click functions:
$scope.reverse = false;
$scope.orderItems = function (item) {
$scope.reverse = !$scope.reverse;
if (item != undefined) {
if (item == $scope.orderItem) {
$scope.orderType = $scope.orderType == '+' ? '-' : '+';
}
else {
$scope.orderItem = item;
$scope.orderType = '+';
}
}
$('.iconOrder').removeClass('up').removeClass('down');
if ($scope.orderType == '+') {
$('.iconOrder[name="' + $scope.orderItem + '"]').addClass('down')
}
else {
$('.iconOrder[name="' + $scope.orderItem + '"]').addClass('up')
}
return $scope.orderType + $scope.orderItem;
}
$scope.goToCaissePage = function (id) {
id = id.replace("app/caisse/", "");
console.log(id);
};
And my Html Code
<ion-list class="list articlesList swipedList" show-delete="showDelete">
<ion-item class="item item-button-right stable-bg">
<span class="row">
<span class="col col-20" ng-click="orderItems('vendeur')">
<strong>Vendeur</strong>
<div class="iconOrder" name="vendeur">
<i class="icon ion-android-arrow-dropup"></i>
<i class="icon ion-android-arrow-dropdown"></i>
</div>
</span>
<span class="col col-20" ng-click="orderItems('date_ticket')">
<strong>Heure</strong>
<div class="iconOrder" name="date_ticket">
<i class="icon ion-android-arrow-dropup"></i>
<i class="icon ion-android-arrow-dropdown"></i>
</div>
</span>
<span class="col col-10" ng-click="orderItems('total')">
<strong>Total</strong>
<div class="iconOrder" name="total">
<i class="icon ion-android-arrow-dropup"></i>
<i class="icon ion-android-arrow-dropdown"></i>
</div>
</span>
</span>
</ion-item>
<ion-item class="item item-button-right pad-item" ng-repeat="ticket in ticketsEncour | orderBy : orderBy : orderItem:reverse track by $index">
<span class="row">
<span class="col col-20 col-pad" ng-click="goToCaissePage('app/caisse/{{ticket._id}}')">
<div>
{{ ticket.vendeur }}
</div>
</span>
<span class="col col-20 col-pad" ng-click="goToCaissePage('app/caisse/{{ticket._id}}')">
<div>
{{ ticket.date_ticket | date:'HH:mm' }}
</div>
</span>
<span class="col col-10 col-pad" ng-click="goToCaissePage('app/caisse/{{ticket._id}}')">
<div>
{{ ticket.total.toFixed(2) }}€
</div>
</span>
</span>
</ion-item>
</ion-list>
When i click on any item of the ng-repeat it shows the right id in the console. But when i use the reorder i can is that the HTML has changed but the ng-click function render always the old one:
click on index 0 => console 1 (perfect)
After reoder with any type:
click on index 0 => console 1 (expected 3 for example)
Thanks
Update: changed the array name (copy cut error)
Updated with #Weijian suggestion
I suggest using the orderBy that uses the scope variables directly. This is also the method suggested by the angular docs.
js
$scope.reverse = false;
$scope.orderItems = function (item) {
if( item == $scope.orderItem ){
$scope.reverse = !$scope.reverse;
}
$scope.orderItem = item;
$('.iconOrder').removeClass('up').removeClass('down');
if( $scope.reverse ){
$('.iconOrder[name="' + $scope.orderItem + '"]').addClass('down');
}
else {
$('.iconOrder[name="' + $scope.orderItem + '"]').addClass('up');
}
}
html
orderBy:orderItem:reverse
https://docs.angularjs.org/api/ng/filter/orderBy
this way is cleaner since you avoid the mixed use of the orderItems function.
I fixed the issue, i was from the track by $index in the ng-repeat loop