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
Related
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
I am writing an app with Ionic and Firebase and my users have the choice to add something to their "Favorites".
Now, I found a way to have the user clicking the button and changing the text once clicked and saving the data in the database, but if the user moves to another page and come back, the button comes back to "Add to favorites".
How to display the text on the button regarding what the user did with it before ?
The DOM :
<div class="list card" id="topLetters-card21" ng-repeat="letter in letters">
<ion-item class="item-avatar item-icon-right" id="userId{{$index}}" alt="{{letter.uid}}" ui-sref="menu.user">
<img src="{{letter.userpic}}" alt="">
<h2>{{letter.username}} </h2>
<p>{{letter.location}}</p>
<i class="icon ion-android-arrow-dropright"></i>
</ion-item>
<h2 style="color:#000000;" class="padding-left" id="letterId{{$index}}" alt="{{letter.letter}}">{{letter.title}}</h2>
<div style="{{letter.font}}background: url(img/{{letter.background}}.jpg) no-repeat center;background-size:cover;">
<h5 style="{{letter.font}}text-align:right;"id="topLetters-heading15" style="color:#000000;" class="padding">{{letter.date}}</h5>
<h5 style="{{letter.font}}"id="topLetters-heading4" style="color:#000000;" class="padding">{{letter.opening}} {{letter.to}}, </h5>
<div id="topLetters-markdown3" style="text-align:justify;" class="show-list-numbers-and-dots padding">
<p style="color:#000000;">{{letter.message}}</p>
</div>
<h5 style="{{letter.font}}"id="topLetters-heading5" style="color:#000000;text-align:right;" class="padding">{{letter.conclusion}}, {{letter.username}}.</h5>
</div>
<button id="addletter{{$index}}" ng-click="addToFavorites($index)" class="button button-dark button-small button-block icon ion-ios-heart-outline" alt={{letter.letter}}> Add to Favorites</button>
<ion-item class="item-icon-left item-icon-right" id="topLetters-list-item29">
<i class="icon ion-ios-heart-outline"></i>{{letter.likes}}
<i class="icon ion-ios-flag"></i>
</ion-item>
<div class="spacer" style="width: 300px; height: 29px;"></div>
</div>
The Controller :
var ref = firebase.database().ref('/letters');
//retrieve the items in the cart of the user
ref.orderByChild("likes").on("value", function(snapshot1) {
$timeout(function(){
$scope.letters = snapshot1.val();
})
})
$scope.addToFavorites = function(ind){
var letterId = document.getElementById('letterId'+ind).getAttribute('alt');
var userId2 = document.getElementById('userId'+ind).getAttribute('alt');
var userId = firebase.auth().currentUser.uid;
//Add the letterUid and ID to our favorites to retrieve it later
//Add our uid to the letter to count the number of Favorites
if(document.getElementById('addletter'+ind).textContent == " Add to Favorites"){
$timeout(function(){
firebase.database().ref('accounts/' + userId + '/favorites/' + letterId).update({
letterId: letterId,
userId: userId2,
});
firebase.database().ref('letters/' + letterId + '/favorites/' + userId).update({
userId: userId,
});
document.getElementById('addletter'+ind).textContent = " Remove from Favorites";
})
} else if (document.getElementById('addletter'+ind).textContent == " Remove from Favorites")
$timeout(function(){
document.getElementById('addletter'+ind).textContent = " Add to Favorites";
firebase.database().ref('/accounts/' + userId + "/favorites/" + letterId).remove();
firebase.database().ref('/letters/' + letterId + "/favorites/" + userId).remove();
})
}
I believe you should do something like this:
JS
$scope.accountsFavorites = [];
$scope.lettersFavorites = [];
$scope.getFavorites = function(){
$scope.accountsFavorites = firebase.database().ref('/accounts/' + userId + "/favorites/")
$scope.lettersFavorites = firebase.database().ref('/letters/' + letterId + "/favorites/")
$scope.syncFavorites();
}
$scope.syncFavorites() {
$scope.letters.forEach(function(letter){
$scope.lettersFavorites.forEach(function(favoriteLetter){
if(letter.id == favoriteLetter.letter_id) { // or something
letter.isFavorite = true;
}
})
})
}
$scope.getFavorites();
HTML
<button
id="addletter{{$index}}"
ng-click="addToFavorites($index)"
class="button button-dark button-small button-block icon ion-ios-heart-outline"
alt={{letter.letter}}>
<span data-ng-if="letter.isFavorite">Remove from favorites</span>
<span data-ng-if="!letter.isFavorite">Add to favorites</span>
</button>
I have articles listed on a page in my app, where I have an iframe and overlay over that iframe. I want to have it that when a user clicks on an iframe overlay, that the title from that article gets hidden. Not sure how to achieve that in ng-repeat. This the code, where as of now, on a click event titles of all articles get hidden.
<ion-item ng-repeat="article in articles" class="item-light">
<div class="article">
<a ng-show="article.external_media.length == 0 || article.external_media.url == ''" href="#/main/article/{{article.id}}" nav-direction="forward" class="article-image-link">
<img src="{{ fileServer }}/imagecache/cover/{{article.cover_image}}">
<h1>{{ article.title.split(' ', 7).join(' ') }}</h1>
</a>
<div class="iframe" ng-show="article.external_media.length > 0 && article.external_media.url != ''">
<iframe ng-src="{{article.external_media[0].url | safeUrl }} "></iframe>
<div class="article-image-link">
<h1 ng-hide="videoPlaying">{{ article.title.split(' ', 7).join(' ') }}</h1>
</div>
<div class="iframe-overlay" ng-click="playVideo()"></div>
</div>
</div>
</ion-item>
Controller:
$scope.playVideo = function(videoSrc) {
$scope.videoPlaying = true;
$scope.videoSrc = videoSrc + "&autoplay=1";
};
you can also use $index for this purpose.
controller
app.controller('someController',function($scope){
$scope.videoPlaying = [];
$scope.playVideo = function(videoSrc,index) {
$scope.videoPlaying[index] = true;
$scope.videoSrc = videoSrc + "&autoplay=1";
};
$scope.stopVideo = function(videoSrc,index) {
$scope.videoPlaying[index] = false;
$scope.videoSrc = videoSrc + "&autoplay=1";
};
})
HTML
i am changing only two lines rest are same as it is
<h1 ng-hide="videoPlaying[$index]">{{ article.title.split(' ', 7).join(' ') }}</h1>
<div class="iframe-overlay" ng-click="playVideo($index)"></div>
I've created a JSFIDDLE (basic) to give you a head start on what you are trying to do.
VIEW
<div ng-app="app">
<div ng-controller="helloCnt">
<div ng-repeat='item in data'>
<span ng-if="item.hide === undefined || !item.hide">{{item.name}}</span>
<div ng-if="!item.hide" ng-click="playVideo(item)">
HIDE
</div>
<div ng-if="item.hide" ng-click="stopVideo(item)">
SHOW
</div>
<br/>
</div>
</div>
</div>
CONTROLLER
var app = angular.module('app', [])
app.controller('helloCnt', function($scope) {
$scope.data = [{
'name': 'test'
}, {
'name': 'in'
}, {
'name': 'dummy'
}, {
'name': 'data'
}]
$scope.playVideo = function(item, videoSrc) {
// other code
item.hide = true;
};
$scope.stopVideo = function(item, videoSrc) {
// other code
item.hide = false;
};
})
SEE JSFIDDLE
I have a ng-repeat for article comments, that looks like this:
<div ng-repeat="comment in comments">
<li class="item" ng-class-even="'even'">
<div class="row">
<div class="col">
<i class="icon ion-person"></i> {{ comment.user.first_name }} {{ comment.user.last_name }}
<i class="icon ion-record"></i> {{ comment.created_at }}
</div>
<!-- TODO: this needs to be an ng-if admin -->
<div ng-show="hasRole(comment.user)" class="col right">
<i class="icon ion-record admin"></i> Admin
</div>
</div>
<div class="row">
<div class="col">
<p>{{ comment.text }}</p>
</div>
</div>
</li>
</div>
I am trying to show this part only if the user is an admin:
<div ng-show="hasRole(comment.user)" class="col right">
<i class="icon ion-record admin"></i> Admin
</div>
I have tried to set that up following the answers here.
So I made a function in my controller:
$scope.hasRole = function(roleName) {
return $scope.comments.user.roles.indexOf(roleName) >= 0;
}
But it returns -1 every time, even when the user is an admin. My data looks like this:
1:Object
$$hashKey: "object:28"
article_id:"2"
created_at:"2016-05-12 12:19:05"
id:6
text:"someCommentText"
updated_at:null
user:Object
active:"1"
created_at:null
first_name:"admin"
id:1
last_name:"admin"
roles:Array[1]
0:Object
created_at:null
id:1
name:"Admin"
parent_id:null
pivot:Object
slug:"admin"
Use this in your HTML
<div ng-show="hasAdminRole(comment.user.roles)" class="col right">
<i class="icon ion-record admin"></i> Admin
</div>
this is the method to determine that the user belongs to the admin role or not.
$scope.hasAdminRole = function(roles) {
var isAdmin = false;
for(var i = 0; i < roles.length; i++) {
if (roles[i].name == 'Admin') {
isAdmin = true;
break;
}
}
return isAdmin;
}
Perhaps you have an error on this line?
var indexOfRole = $scope.comments.indexOf(user.roles);
You are looking here to see if the list of roles for this users exists within the array of comments.
Maybe you need to just check in the actual user.roles array and see if there is an Admin role there? Something like:
$scope.hasRole = function(user) {
for (var i = 0; i < user.roles.length; i++) {
if (user.roles[i].slug === 'admin') { return true; }
}
return false
}
That's because it's an object, you can fetch the index of only array. In the link that you provided is an array.
I'm building an app in ionic, and I need to pass items into another array, but when I pass the same item again it's considered the same item - really - but I need that to not happen. I need them to be considered different or or at least be considered different. Each item(is an object) of array have a var "qntt"(quantity) and when this bug happen, instead put the same item in array, plus one in this var. OBS: I'm using ng-repeat="item in items track by $index" in html.
<ion-view title="Order">
<ion-content overflow-scroll="true" padding="true" style="background: url(img/background1.jpg) no-repeat center;" class="has-header" ng-controller="pedidoCtrl">
<button class="button button-dark button-small button-block" ng-click="showDelete = !showDelete">Remover</button>
<ion-list show-delete="showDelete">
<ion-item class="item-thumbnail-left item-remove-animate" ng-repeat="item in items track by $index">
<ion-delete-button class="ion-minus-circled" ng-click="deleteItem(item)"></ion-delete-button>
<img src="{{item.img}}">
<h2>{{item.nome}}</h2>
<div class="row">
<div class="col col-60">
<h2>R{{item.subtotal | currency}}</h2>
</div>
<div class="col col-15">
<button class="button button-clear button-assertive button-small icon ion-minus" ng-click="subQtt(item)"></button>
</div>
<div class="col col-10">
<h2>{{item.qtt}}</h2>
</div>
<div class="col col-15">
<button class="button button-icon-right button-clear button-assertive button-small icon ion-plus" ng-click="addQtt(item)"></button>
</div>
</div>
</ion-item>
</ion-list>
<button style="text-align:right;" class="button button-assertive button-block icon-left ion-cash">R{{total | currency}}</button>
<div style="margin-right:-20px;">
<button style="left:-10px;" class="button button-dark button-large button-full icon ion-android-cart">Order</button>
</div>
</ion-content>
.controller('orderCtrl', function($scope, productService) {
$scope.items = null;
$scope.items = productService.getProducts();
$scope.deleteItem = function(item){
$scope.items.splice($scope.items.indexOf(item), 1);
};
$scope.$watchCollection('items', function(array) {
if (array) {
$scope.total = array.reduce(function(total,item) {
return total + item.subtotal;
},0);
}
$scope.addQtt = function(item){
item.qtt = item.qtt + 1;
item.subtotal = item.price * item.qtt;
$scope.total = array.reduce(function(total,item) {
return total + item.subtotal;
},0);
};
$scope.subQtt = function(item){
if(item.qtt > 1)
{
item.qtt--;
item.subtotal = item.price * item.qtt;
}
else
{
$scope.items.splice($scope.items.indexOf(item), 1);
}
$scope.total = array.reduce(function(total,item) {
return total + item.subtotal;
},0);
};
});
})
.service('productService', [function(){
var productList = [];
var addProduct = function(product) {
productList.push(product);
};
var getProducts = function(){
return productsList;
};
return {
addProduct: addProduct,
getProducts: getProducts,
};
}]);
That's because you are not calling the service method addProduto() within your controller. Try calling produtoService.addProduto() within the $scope.addQnt() method as below and it should work:
$scope.addQnt = function(item){
item.qntd = item.qntd + 1;
item.subtotal = item.preco * item.qntd;
$scope.total = array.reduce(function(total,item) {
return total + item.subtotal;
},0);
// Add this line to retain your addition in service
produtoService.addProduto(item);
// Get updated list of products
$scope.items = productoService.getProdutos();
// Alternatively use '$scope.items.push(item)'
};
Also, you need to refresh your $scope.items by triggering a call to productoService.getProdutos(), though you can do it directly by pushing in $scope.items.push(item)