I'm new to angular and am trying to build to learn. I have the following array in a controller. This represents a trade of two players (from two teams).
I think my logic is spot on, why isn't my code working?
$scope.unique = [["Name", "Name", "Name", "Name", {"upvotes": 0}]]
//more can be added to the outer array, in sets of five like this.
In the same controller I have the following function:
$scope.incrementUpvotes = function(value) {
value[4].upvotes++;
};
In my Angular view I am trying to have an ng-repeat, with ng-click incrementing the upvote when this area is clicked. The upvote is working, but it's only being rendered for the first five elements, the ng-repeat doesn't seem to be working.
<div class="text-center" ng-repeat="value in unique">
<span ng-click="incrementUpvotes(value)">
{{value[0]}} and {{value[1]}} for {{value[2]}} and {{value[3]}} upvotes: {{value[4].upvotes}}
</span>
</div>
While you logic is spot on, the error is coming from the fact that your array is multi-dimensional. To fix it as is look at Anik's answer.
But let me suggest a better structure that will make your code much more readable, maintainable and more aligned to JavaScript conventions
$scope.teams = [
{
members: ["Name1", "Name2", "Name3", "Name4"],
upvotes: 0
},
{
members: ["Name5", "Name6", "Name7", "Name8"],
upvotes: 0
}
];
So now your upvote function is more readable like so:
$scope.incrementUpvotes = function(team) {
team.upvotes++;
};
And so is your HTML much cleaner now:
<div class="text-center" ng-repeat="team in teams">
<span ng-click="incrementUpvotes(team)">
{{team.member[0]}} and {{team.member[1]}} for {{team.member[2]}} and {{team.member[3]}} upvotes: {{team.upvotes}}
</span>
</div>
I think your structure is a bit confusing. Try something like this:
JS
$scope.unique = [{team: ["Name", "Name", "Name", "Name"], "upvotes": 0},
{team: ["Name", "Name", "Name", "Name"], "upvotes": 0}]
$scope.incrementUpvotes = function(value) {
value.upvotes++;
};
HTML
<div class="text-center" ng-repeat="value in unique">
<div ng-click="incrementUpvotes(value)">
<span ng-repeat="v in value.team track by $index">
{{v}}
</span>
<span>
upvotes: {{value.upvotes}}
</span>
</div>
</div>
See it working here (click on the row to increment vote): http://www.bootply.com/7LOE1zpE4z
Related
Please find below a simplified example of what I try to achieve.
I have a big list of which I create an overview using alpinejs.
I now added a filter to just show some of my elements.
I also have a global number of elements shown as well as a global count of stuff the elements have.
In below example I show for each client the cars they own and the number of cars they own. Globally I show the number of clients and the total amount of cars.
This works as long as I do not filter.
As soon as filtering starts, the global numbers act weird.
When started, I have 3 clients and 5 cars.
When I enter an E, It should be 1 client and 2 cars, but it remains as 3 and 5.
When I remove the E, the numbers go up to 8 clients and 12 cars.
I'm sure it is due to the fact that the global counts do not get reset to 0.
But I couldn't find a way to achieve this reset. Any help would be much appreciated.
Please note that I know, I can avoid actually counting the cars with ++individualcount by just replacing x-text="individualcount" with x-text="cars.length".
<html>
<head>
<script defer src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js"></script>
</head>
<body>
<h1 x-data="{ message: 'I ❤️ Alpine' }" x-text="message"></h1>
<div
x-data="{
search: '',
items: [
{client: {name: 'Hugo', id:'0001' }, stuff: {cars: [ 'Horch', 'Benz' ] } },
{client: {name: 'Hans', id:'0002' }, stuff: {cars: [ 'Horch' ] } },
{client: {name: 'Egon', id:'0003' }, stuff: {cars: [ 'VW', 'Maybach' ] } },
],
get filteredItems() {
return this.items.filter(
i => i.client.name.startsWith(this.search)
)
},
}"
>
<input x-model="search" placeholder="Search...">
<ul x-data="{ clientcount : 0, carcount : 0 }">
<p>Number of clients: <span x-text="clientcount"></span></p>
<p>Number of cars: <span x-text="carcount"></span></p>
<template x-for="item in filteredItems" :key="item.client.name">
<li x-data="{ individualcount : 0 }"
x-effect="carcount += individualcount; ++clientcount">
<span x-text="item.client.id"></span>
<span x-text="item.client.name"></span>
(<span x-text="individualcount"></span>)
<ul>
<template x-for="car in item.stuff.cars" :key="car">
<li x-text="car"
x-effect="++individualcount">
</li>
</template>
</ul>
</li>
</template>
</ul>
</div>
</body>
</html>
Suppose I have a object like as shown below:
var ob = [
{name: "root",id: 1},
{name: "root2",id: 2}
];
And I want to append children object dynamically to it. For example:
Suppose if I click on id 1 then children object should be appended to ob object.
var ob = [
{name: "root",id: 1, children: [
{name: 'sub1', id:'5'},
{name: 'sub2', id:'6'},
]
},
{name: "root2",id: 2}
];
Now if I click again on id 6 again children should be added to id 6.
var ob = [
{name: "root",id: 1, children: [
{name: 'sub1', id:'5'},
{name: 'sub2', id:'6', children: [
{name: 'subsub1', id:'8'},
{name: 'subsub2', id:'9'},
]
},
]
},
{name: "root2",id: 2}
];
I am trying to write a recursive function for it but no success. On click of any term I have reference only to the clicked term. I don't know about the parent term.
EDIT:
Below is my code:
<div *ngFor = "let term of terms">
<div class="row tr">
<a (click) = "showTerms($event)">{{term.id}}</a>
</div>
<div class="col-xs-6">{{term.desc}}</div>
<app-icd-codes *ngIf = "term.children" [terms] = "term.children"></app-icd-codes>
</div>
Here on click of a tag I am adding children's. So I need to create a dynamic object and update that object as shown above.
The most easy way is pass as argument the index of "terms". Put two buttons, one to AddTerms and another one to hideTerms/showTerms.
<div *ngFor = "let term of terms;let i=index">
<!--see the way to get the index of the array -->
<div class="row tr">
{{term.id}}
<!--you make a link, I use a button-->
<!--the button "add" is visible when there're NOT "children"-->
<button *ngIf="!term.terms" (click)="addTerms(i)">Add</button>
<!--the button to Show/hide is visible when there ARE "children"-->
<button *ngIf="term.terms" (click)="term.show=!term.show">
<span *ngIf="term.show">^</span>
<span *ngIf="!term.show">v</span>
</button>
</div>
<ng-container *ngIf ="term.terms && term.show">
<app-icd-codes [terms]="term.terms"></app-icd-codes>
</ng-container>
</div>
Then you must put your function addTerms. A simple function can be like
//see that you received the "index" of children
addTerms(index: number) {
this.terms[index].show = true; //<--to show the children
this.terms[index].terms = [{ id: 3 }, { id: 4 }]; //a "easy" way to add
}
Ok, really the function must be like
addTerms(index: number) {
let id=this.terms[index].id; //we get the "id"
//call a httpClient and subscribe
this.httpClient.get("url/"+id).subscribe(res=>
//in the subscription
{
this.terms[index].show = true; //<--to show the children
this.terms[index].terms = res
})
}
NOTE: Can result "strange" add new properties to an Object (in this case "children" and "show"). If we feel more confortable, we can add the properies when we create the object with a null value
I've a store containing a lot of items. All of these items belongs to a category. So I have a function in my store to get all existing categories.
My code:
http://plnkr.co/edit/rB4NMezhexEX4aFqveUO
But after loading the store, the $scope will be updated, but not the ng-repeat. What is the right Angular-Way to do this?
Regards
Bytecounter
So i scrambled your code and I thing this is what u need:
js
function ClickToEditCtrl($scope) {
$scope.products = [{name: 'Mercedes', type: "car"},
{name: 'Ford', type: "car"},
{name: 'BMW', type: "car"}];
$scope.submitProduct = function () {
$scope.products.push({name: 'Fiat', type: 'car'});
};
}
html:
<div ng-app>
<div ng-controller="ClickToEditCtrl">
<div data-ng-repeat="product in products">
{{product.name}}
</div>
<input type="button" ng-click="submitProduct()" value="Click me">
</div>
</div>
fiddle:
http://jsfiddle.net/5DMjt/3459/
So ng-repeat list all objects in arrey, something happens (click action in my case) and that array changes, you just need to push it inside.
Hope it helps
So I've used ng-repeat to create a list of all my songs in an album (refer to this question I asked earlier)
So what I am trying to do now is make it so when a user clicks an item from the list, it plays the refered track. This is my app:
enitoniApp.controller('musicItems', ['$scope', function ($scope) {
$scope.ngplaySong = function (ref, name) {
playSong(ref, name)
}
$scope.albums = [
{
name: 'Over The Mountains',
price: 0,
tracks: [
{
name: 'Over The Mountains',
ref: 'otm',
released: 0,
},
{
name: '!C3',
ref: 'ice',
released: 0,
},
{
name: 'Dark Clouds',
ref: 'clouds',
released: 0
},
{
name: 'Fog',
ref: 'fog',
released: 0
}
]
},
{
name: 'test-album',
price: 5000,
tracks: [
{
name: 'test',
ref: 'null'
},
]
}
]
}]);
As you can see, I'm trying to call a regular function using ng-click. This regular function (playSong()) is inside the code for my player, and it plays a track based on the reference id.
snippet from player.js:
/** Play single song **/
function playSong(ref, name) {
showPlayer();
clearPlaylist()
playlistPosition = 0;
addToPlaylist(ref, name, 'play')
}
So I have this in my html:
<li ng-repeat="album in albums">
<div class="info">
<p>{{album.name}}</p>
<p>{{album.price | currency}}</p>
</div>
<ul>
<li ng-animate="grid-fade" ng-repeat="track in album.tracks">
<div class="grid-item" ng-click="ngplaySong('{{track.ref}}','{{track.name}}')">
<div class="cover">
<img ng-src="/img/covers/art_{{track.ref}}.png" alt="">
</div>
<div class="info">
<p>{{track.name}}</p>
<p>{{track.released}}</p>
</div>
</div>
</li>
</ul>
</li>
The weird thing is that even though this is rendering correctly:
THIS gets outputted into the console even though the parameters are correct:
Why is it not binding the data when the function gets called, am I missing something here?
I do not think that you need those braces inside your ng-click. Try this:
<div class="grid-item" ng-click="ngplaySong(track.ref, track.name)">
The thing is that you pass an expression to ng-click which is then parsed by Angular and it is smart enough to recognize the variables from current scope. You can read more on Angular expressions here: https://docs.angularjs.org/guide/expression
In fact, there is a very nice and easy example in Angular ng-click documentation which includes accessing a local variable inside the ng-click expression: https://docs.angularjs.org/api/ng/directive/ngClick
The menu is what I want, when mouse over the left, the right should changes but doesn't.
Here is my simplified viewmodel:
var currentSelectIndex = 0;
var AppModel = {
CurrentIndex: ko.observable(currentSelectedIndex),
OnMouseOver: function (data, event) {
// change currentIndex or currentSelectedIndex here
// CurrentSubCategory didn't updated
},
CurrentSubCategory: ko.computed({
read: function() {
return AppModel.Menu[AppModel.CurrentIndex()].subcategory;
},
deferEvaluation: true
}),
Menu: [
{
subcategory: [
{ name: '1', id: 50000436 },
{ name: '2', id: 50010402 },
{ name: '3', id: 50010159 }
],
}
};
And my html:
<div class="categories" id="categories">
<div class="first-category" id="first-category">
<ul data-bind="foreach:Menu">
<li data-bind="text:name,attr:{id:id,class:className},event{ mouseover: $root.myfunction}"></li>
</ul>
</div>
<div class="sub-category" id="sub-category">
<ul data-bind="foreach:CurrentSubCategory()">
<li><a data-bind="text:name,attr:{href:getListUrl(id)}"></a></li>
</ul>
<div class="clear">
</div>
</div>
<div class="clear">
</div>
</div>
Sorry, can't post images due to less than 10 reputation.
Thanks for any help!
There were several syntax errors in your code which I imagine are a result of your making it simpler to post.
I have posted a working jsFiddle here: http://jsfiddle.net/Gy6Gv/2/
I changed Menu to be an observable array only because knockout provides the helper method .indexOf to make it easier to get the index of the menu from the mouseover. Other than that there was no problem with the computed. I imagine there is some other syntactical error in your actual code.