Strange behaviour when Ordering by dynamic count AngularJS - - javascript

I want to order this list of exercises by count, this works on first load, but when you start to click the increase or decrease, it increases/decreases the count of other indexes
HTML:
<div class="row" ng-repeat="exercise in exercises | orderBy:'count' ">
<div class="exercise-icon col-xs-2"> <img ng-src="{{ exercise.icon }}" /></div>
<div class="col-xs-6">
<p class="exercise-name"> {{ exercise.name }} </p>
</div>
<div class="col-xs-4 counters">
<span class="decrease" ng-click="decrease($index)">-</span>
<span class="count">{{ exercise.count }} </span>
<span class="increase" ng-click="increase($index)">+</span>
</div>
</div>
JS - Controller
app.controller('MainController', ['$scope', function($scope) {
$scope.exercises = [
{
icon: 'img/pushup.jpg',
name: 'Pushups',
count: 20
},
{
icon: 'img/squat.jpg',
name: 'Squats',
count: 15
},
{
icon: 'img/pullup.jpg',
name: 'Pullups',
count: 10
},
...
$scope.increase = function(index) {
$scope.exercises[index].count += 1;
};
$scope.decrease = function(index) {
if ( ($scope.exercises[index].count) > 0){
$scope.exercises[index].count -= 1;
}
};

Instead using $index pass object itself
Like this
<span class="decrease" ng-click="decrease(exercise)">-</span>
JS
$scope.decrease=function(exercise){
exercise.count--;
}

Related

grocery cart on Angular

I create add-to-cart app.
Want to click each item and add it to cart.
But firstly I need to click button 'add to cart' and increase its value with every click.
As I added ng-repeat, I don't know how to write a function that will be responsible for adding separate item.
angular.module('TransactionApp', [])
.controller('TransactionsCtrl', function($scope) {
$scope.title = 'Online-store';
$scope.itemsArray = [
{ price: 50, name: "Whey protein", img: 'img/item-1.png', quantity: 0},
{ price: 60, name: "Protein bar", img: 'img/item-2.png', quantity: 0 },
{ price: 35, name: "BCAA", img: 'img/item-3.png', quantity: 0 },
{ price: 50, name: "Whey protein", img: 'img/item-1.png', quantity: 0 },
{ price: 60, name: "Protein bar", img: 'img/item-2.png', quantity: 0 },
{ price: 80, name: "BCAA", img: 'img/item-3.png', quantity: 0 }
];
// $scope.count = 0;
$scope.addTo = function(){
}
});
here is html:
<h2 class="title">{{title}} <i class="em em-shopping_bags"></i></h2>
<div class="container">
<div class="row">
<div class="col-lg-4 col-md-2 col-sm-6">
<div class="card" style="width: 18rem;" ng-repeat='item in itemsArray'>
<img class="card-img-top" ng-src={{item.img}} alt="Card image cap">
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">{{item.name}}</p>
<p class="price">{{ item.price | currency }}</p>
<i class="em em-shopping_trolley"></i> Add to cart <span class="number">{{ item.quantity }}</span>
</p>
</div>
</div>
</div>
</div>
</div>
Pass the item to controller with addTo(item):
<a href="#" class="btn btn-warning" ng-click="addTo(item)">
<i class="em em-shopping_trolley"></i>
Add to cart
<span class="number">{{ item.quantity }}</span>
</a>
after your addTo accepts a parameter:
$scope.addTo = function(item){ // 'item' is a reference to an element in itemsArray
item.quantity++;
}
I believe each of your item in view has its own Add to Cart Button against it and I also believe you want to increase the quantity property of each of the item each time a user clicks the button against that item.
For that all you have to do is pass the item to addTo() method like :-
<i class="em em-shopping_trolley"></i> Add to cart <span class="number">{{ item.quantity }}</span>
and modify the method definition in controller
$scope.addTo = function(var item){
item.quantity++;
}

Vue JS dynamic modal with components

in news.twig
{% extends 'layouts.twig' %}
{% block content %}
<section class="ls section_padding_bottom_110">
<div id="cart" class="container">
<cart
v-bind:materials="news"
type="news"
test="materials"
></cart>
<modal></modal>
</div>
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask" v-if="active" #click="close()">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<h3>${ item.name }</h3>
</div>
<div class="modal-body">
${ item.body }
<br>
modal #${ item.id }
</div>
<div class="modal-footer">
<button class="modal-default-button" #click="close()">
close
</button>
</div>
</div>
</div>
</div>
</transition>
</script>
</section>
{% endblock %}
I have 2 components and 1 Vue in my js.
var Hub = new Vue();
Vue.component(
'modal', {
template: '#modal-template',
delimiters: ['${', '}'],
data: function() {
return {
active: false,
item: {
id: '',
name: '',
body: ''
}
}
},
methods: {
open: function (item) {
this.active = true;
this.item = item;
},
close: function () {
this.active = false;
}
},
mounted: function() {
this.$nextTick(function () {
Hub.$on('open-modal', this.open);
Hub.$on('close-modal', this.close);
}.bind(this));
}
});
Vue.component('cart', {
props: {
materials: { type: Array, required: true},
type: { type: String, required: true}
},
computed: {
isPoints() {
return this.type == 'paymentPoints';
},
isNews() {
return this.type == 'news';
}
},
template : `
<div class="row masonry-layout isotope_container">
<div class="col-md-4 col-sm-6 isotope-item" v-for="item in materials">
<div class="vertical-item content-padding topmargin_80">
<div class="item-media">
<img v-bind:src="item.image" alt="">
<div class="media-links p-link">
<div class="links-wrap">
<i class="flaticon-arrows-2"></i>
</div>
<a v-if="!isNews" v-bind:href="item.image" class="abs-link"></a>
</div>
</div>
<button #click="openModal(item)" #keyup.esc="closeModal()">more</button>
<div class="item-content with_shadow with_bottom_border">
<span v-if="isNews" class="categories-links" style="font-size:20px;">
<a rel="category" href="#modal1" data-toggle="modal">
{{item.name}}
</a>
</span>
<p>{{item.body}}</p>
<div v-if="isPoints">
<hr class="light-divider full-content-divider bottommargin_10">
<div class="media small-icon-media topmargin_5">
<div class="media-left">
<i class="fa fa-map-marker grey fontsize_18"></i>
</div>
<div class="media-body">
{{item.adress}}
</div>
</div>
<div class="media small-icon-media topmargin_5">
<div class="media-left">
<i class="fa fa-phone grey fontsize_18"></i>
</div>
<div class="media-body">
{{item.telephone}}
</div>
</div>
</div>
<div v-if="isNews" class="text-center">
<hr class="light-divider full-content-divider bottommargin_10">
<span class="date">
<i class="flaticon-clock-1 grey"></i>
<time class="entry-date">
{{item.date}}
</time>
</span>
</div>
</div>
</div>
</div>
</div>
`
});
var vm = new Vue({
el: '#cart',
name: 'cart',
delimiters: ['${', '}'],
data: {
complated: [],
continuing: [],
planned: [],
points: [],
infoSlider: [],
news: []
},
methods: {
openModal: function (item) {
Hub.$emit('open-modal', item);
},
closeModal: function () {
Hub.$emit('close-modal');
}
},
created() {
axios.get(url).then(res => {
var proje = res.data.projects[0];
this.complated = proje.complated;
this.continuing = proje.continuing;
this.planned = proje.planned;
this.points = res.data.paymentPoints;
this.infoSlider = res.data.sliderÄ°nfos;
this.news = res.data.news;
})
.catch(e => {
console.log(e);
})
}
});
When I click openModal(item) button give me error ;
Property or method "openModal" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I can not use the openModal function in component.
I can use the function in news.twig without any problems, but then I can not use the component. Can you help me?
You are using openModal in cart component, but that method is defined in root component.
According to Vue's documentation:
Everything in the parent template is compiled in parent scope;
everything in the child template is compiled in the child scope.
in my case need to define variable in vuejs
like this
<script>
export default {
name: "MegaMenu",
props: {
categories: Array,
},
}

Angularjs change text according to true false status

I am making a box with ng-repeat here is the html
<div class="mission_box clearfix" ng-repeat="mission in missions">
<img src="images/tick_patch.png" class="expired_img">
<h2>{{mission.name}}</h2>
<p class="clearfix"><em>Win</em> <span><img src="images/crown.png"> {{mission.crownwin}} Crowns</span> <span><img src="images/voucher.png"> {{mission.voucherwin}} Voucher</span></p>
<div class="progress_box_outer clearfix">
<div class="progress_box">
<span style="width:{{100/mission.tasktotal*mission.taskdone}}%"></span>
</div>
<p class="progress_count">{{mission.taskdone}}/{{mission.tasktotal}}</p>
</div>
<div class="expiry_date">
<p>{{mission.expiry | missionexpire}}</p>
</div>
</div>
And here is the defined array in controller for this
$scope.missions = [
{'name':'3 Start Checkins', 'crownwin':'100', 'voucherwin':'flipkart','tasktotal':'4','taskdone':'3','expiry':'Expire on 25/06','isexpired':false,'iscompleted':true},
{'name':'3 Start Checkins', 'crownwin':'100', 'voucherwin':'flipkart','tasktotal':'4','taskdone':'2','expiry':'Expire on 25/06','isexpired':false,'iscompleted':true}
]
And filter i have created yet
.filter('missionexpire', function(){
return function(input){
if(input == true){
input = 'Completed'
} else {
input = input
}
}
})
In the above code you can see the last div in html with class .expiry_date i have defined filter in that.
What exactly i want is to change the text of {{mission.expiry}} if the status of {{mission.iscomplete}} is true.
Any help is appreciated. Thanks.
You could simply achieve this without a filter. Just use a conditional expression like
<p>{{mission.iscompleted === true?'Completed': mission.expiry}}</p>
angular.module('app', []).controller('AppController', function($scope) {
$scope.missions = [{
'name': '3 Start Checkins',
'crownwin': '100',
'voucherwin': 'flipkart',
'tasktotal': '4',
'taskdone': '3',
'expiry': 'Expire on 25/06',
'isExpired': false,
'iscompleted': true
}, {
'name': '3 Start Checkins',
'crownwin': '100',
'voucherwin': 'flipkart',
'tasktotal': '4',
'taskdone': '2',
'expiry': 'Expire on 25/06',
'isExpired': true,
'iscompleted': false
}, {
'name': '3 Start Checkins',
'crownwin': '100',
'voucherwin': 'flipkart',
'tasktotal': '4',
'taskdone': '2',
'expiry': 'Expire on 25/06',
'isExpired': false,
'iscompleted': false
}]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="AppController">
<div class="mission_box clearfix" ng-repeat="mission in missions">
<h2>{{mission.name}}</h2>
<p class="clearfix"><em>Win</em> <span>{{mission.crownwin}} Crowns</span> <span> {{mission.voucherwin}} Voucher</span>
</p>
<div class="progress_box_outer clearfix">
<div class="progress_box">
<span style="width:{{100/mission.tasktotal*mission.taskdone}}%"></span>
</div>
<p class="progress_count">{{mission.taskdone}}/{{mission.tasktotal}}</p>
</div>
<div class="expiry_date">
<p>{{mission.iscompleted === true?'Completed': (mission.isExpired === true ? 'Expired' : mission.expiry)}}</p>
</div>
</div>
</div>
</body>
If you want to achieve this by filter try this. From the filter you need to return anything.
.filter('missionexpire', function(){
return function(input, iscompleted){
if(iscompleted){
return 'Completed';
} else {
return input;
}
}
})
and
<div class="expiry_date">
<p>{{mission.expiry | missionexpire:misson.iscompleted}}</p>
</div>
var app = angular.module('myApp', []);
app.controller('MyCtrl',['$scope', function($scope){
$scope.missions = [
{'name':'3 Start Checkins', 'crownwin':'100', 'voucherwin':'flipkart','tasktotal':'4','taskdone':'3','expiry':'Expire on 25/06','isexpired':false,'iscompleted':true},
{'name':'3 Start Checkins', 'crownwin':'100', 'voucherwin':'flipkart','tasktotal':'4','taskdone':'2','expiry':'Expire on 25/06','isexpired':false,'iscompleted':true}
]
}])
app.filter('missionexpire', function(){
return function(input, isCompleted){
if(isCompleted){
return 'Completed';
} else {
return input;
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='myApp' ng-controller='MyCtrl'>
<div class="mission_box clearfix" ng-repeat="mission in missions">
<h2>{{mission.name}}</h2>
<p class="clearfix"><em>Win</em> <span>{{mission.crownwin}} Crowns</span> <span> {{mission.voucherwin}} Voucher</span>
</p>
<div class="progress_box_outer clearfix">
<div class="progress_box">
<span style="width:{{100/mission.tasktotal*mission.taskdone}}%"></span>
</div>
<p class="progress_count">{{mission.taskdone}}/{{mission.tasktotal}}</p>
</div>
<div class="expiry_date">
<p>{{mission.expiry | missionexpire:mission.iscompleted}}</p>
</div>
</div>
</div>
To filter content according to a specific property value in AngularJs there are used Pipes ( | )
So, in your section:
<div class="expiry_date">
<p>{{mission.expiry | missionexpire}}</p>
</div>
You add only the property but not the evaluation for that property. To do so you need to change that to,
<div class="expiry_date">
<p>{{mission.expiry | missionexpire:iscomplete}}</p>
</div>

How can I fetch two consecutive array objects simultaneously with ng-repeat

Let's say we have an array in our controller as follows:
$scope.elements = [
{
title: title-1,
desc: description-1
},
{
title: title-2,
desc: description-2
},
{
title: title-3,
desc: description-3
},
{
title: title-4,
desc: description-4
}
]
I wish to loop through the array so I can place the elements as follows:
<div class="row">
<div class="col-sm-6">
{{ elements[0].title }}
{{ elements[0].desc }}
</div>
<div class="col-sm-6">
{{ elements[1].title }}
{{ elements[1].desc }}
</div>
</div>
<div class="row">
<div class="col-sm-6">
{{ elements[2].title }}
{{ elements[2].desc }}
</div>
<div class="col-sm-6">
{{ elements[3].title }}
{{ elements[3].desc }}
</div>
</div>
...and so on.
This could be achieved if we could fetch two consecutive elements simultaneously via an ng-repeat and pass it to the directive. Can this be done? Also if so, how would the fetched array objects be handled inside the directive?
I'd use the $index property to do this. The hiding with ng-if is a little inefficient, if this is a huge repeat, you might want a more elegant solution.
<div class="row" ng-repeat="element in elements" ng-if="$index <= elements.length /2">
<div class="col-sm-6">
{{ elements[$index*2].title }}
{{ elements[$index*2].desc }}
</div>
<div class="col-sm-6" ng-if="elements[$index*2 + 1]"> //ng-if for if you want to remove last element if odd array.
{{ elements[$index*2 + 1].title }}
{{ elements[$index*2 + 1].desc }}
</div>
</div>
Seems like this would be as simple as creating another array to iterate over
$scope.TemplateArray = [
[0,1],
[2,3],
etc...
]
Then use this for your ng-repeat
array in TemplateArray
<div class="row">
<div class="col-sm-6">
{{ elements[array[0]].title }}
{{ elements[array[0]].desc }}
</div>
<div class="col-sm-6">
{{ elements[array[1]].title }}
{{ elements[array[1]].desc }}
</div>
</div>
Probably the best way to do this is going to be to pre-process your array into groups of however many columns you have and then ng-repeat over that array. So pre-process your elements into something that looks like:
$scope.processedElements = [
[{title:'1',desc:'1d'},{title:'2',desc:'2d'},{title:'3',desc:'3d'},{title:'4',desc:'4d'}],
[{title:'5',desc:'5d},....... ]
];
and then
<div ng-repeat="item in processedElements">
{{item[0].title}} {{item[1].title}} // etc. etc
</div>
I think this can solve your problem to a limit
var app = angular.module('plunker', []);
app.controller('example', function($scope) {
$scope.limit = 2;
$scope.elements = [];
$scope.$watch('limit', function() {
$scope.elements = elements;
});
elements = [{
title: 'title-1',
desc: 'description-1'
}, {
title: 'title-2',
desc: 'description-2'
}, {
title: ' title-3',
desc: 'description-3'
}, {
title: 'title-4',
desc: 'description-4'
}];
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="example">
<input type="number" ng-model="limit">
<div class="row">
<div class="col-sm-6" ng-repeat="el in elements" ng-if="$index < limit">
{{el.title}} {{el.desc}}
</div>
</div>
<p>seperator</p>
<div class="row">
<div class="col-sm-6" ng-repeat="el in elements" ng-if="$index >= limit">
{{el.title}} {{el.desc}}
</div>
</div>
</div>
</body>
</html>

multidimensional array in angular

I have a multidimensional array from an API. Is it possible to programatically loop through the array?
{
success: true,
categories: [{
cat_id: "2",
name: "This is category One",
description: null,
photo_url: "/img/test.png",
order: "1",
items: [{
item_id: "1",
title: "Desk",
item_url: "/690928460",
photo_url: "/desk.png",
}, {
item_id: "2",
title: "Chair",
item_url: "/18882823",
photo_url: "/chair.png",
},
}]
}]
}
My controller looks like this:
myApp.controller('items', function($scope, $http, $location, Data) {
var apiUrl = '/api/items';
$http.get(apiUrl).
success(function(data) {
$scope.data = Data;
$scope.allData = data;
});
$scope.changeView = function(view) {
$location.path(view);
}
});
Angular index file just has: <div ng-view=""></div>
View file
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="row" ng-repeat="categories in allData">
<div class="col-xs-6" ng-repeat="category in categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
</div>
I can loop through the category names fine, but when trying to return items for EACH category I don't understand the logic behind it...
I would suggest some simple nested for loops, as for each gives rise to more complexity.
As I'm not sure what you want to do with the data let's just create an array of all item names and one of all category names:
Within your success function:
var items = [], categories = []
for(var i = 0; i < data.categories.length;i++){
categories.push(data.categories[i].name);
for(var j = 0; j < data.categories[i].items.length;j++){
items.push(data.categories[i].items[j].name);
}
}
console.log(categories);
console.log(items);
EDIT:
Completely missed your html code somehow, here is my solution:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
EDIT 2:
As to your comment:
If you want to select the secondary view's contents(ie the items) based on the selection of a category I would suggest a ng-click attribute. A directive could be used but isn't necessary:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="title" ng_click="selected_category = category">
{{ category.name }}
</div>
</div>
<div class="col-xs-6" ng-repeat="item in selected_category.items">
<div class="title">
{{ item.name }}
</div>
</div>
</div>
</div>
So when your categories data is loaded the first ng-repeat is populated with the categories. Each div with class title will have a function called on click which will make the selected_category object equal the selected category.
This will then cause the second view to be populated with all the items in the selected category by Angular's two way bind.

Categories

Resources