Angular-JS ng-repeat with asynchron data - javascript

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

Related

Angular: How to use ng-options to make four separate selections?

JS:
angular
.module('app', [])
function MainCtrl() {
var ctrl = this;
ctrl.selectionList = [
{ id: 1, name: 'apple'},
{ id: 2, name: 'banana'},
{ id: 3, name: 'grapes'},
{ id: 4, name: 'carrot'}
];
ctrl.selectedThing = ctrl.selectionList[0].name;
}
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
HTML:
<div class="row">
<div class="col-sm-3 col-xs-12 unit">
<select
ng-model="ctrl.selectedThing"
ng-options="selections.name as selections.name for selections in ctrl.selectionList">
</select>
</div>
</div><!--end of first row-->
So this code creates four different selections.
The problem is that when I choose an option, let's say for example "apples" on one selection, all the other selections become apples too. Is there any way to solve this with ng-options or should I just write the select in HTML?
You definitely want to use ng-options, as that isn't the issue here. The problem you are seeing is most likely because the ng-model on all of your select elements is the same ctrl variable. So when you update one of them, it changes a single variable that is bound to all four dropdowns. You either need to setup up an array for your selected items, or four different instances of a selected variable.
ctrl.selectedThings = [ctrl.selectedList[0].name, '', '', ''];
Then in your view you can do this...
<select
ng-model="ctrl.selectedThings[rowIndex]"
ng-options="selections.name as selections.name for selections in ctrl.selectionList">
</select>
Not the most robust solution if you are going past 4 items, but you should be able to adapt it to be dynamic.
Your code is working fine, can you check and confirm?!
(function ()
{
var app = angular.module("app", []);
function HomeController()
{
var vm = this;
vm.selectionList = [
{ id: 1, name: 'apple'},
{ id: 2, name: 'banana'},
{ id: 3, name: 'grapes'},
{ id: 4, name: 'carrot'}
];
}
app.controller("HomeController", [HomeController]);
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>Angular JS App</title>
</head>
<body>
<div class="container" ng-controller="HomeController as homeCtrl">
<div class="row">
<div class="col-sm-3 col-xs-12 unit">
<select
ng-model="homeCtrl.selectedThing"
ng-options="selections.name as selections.name for selections in homeCtrl.selectionList">
</select>
<pre>{{homeCtrl.selectedThing}}</pre>
</div>
</div><!--end of first row-->
</div>
</body>
</html>
If you have ng-model="ctrl.selectedThing" for all of your <select> tags, they will all change to the same selection because they're using the same scope property. Think of it like having 4 variables referencing the same data: if you change one, access any of the variables will retrieve the same result.
You need to bind all of your selects to a different property on scope, so ctrl.selectedThing1,2,...n. That's not very scalable, but that would fix your problem.

Angular - can I use a single function on multiple objects within a nested ng-repeat that triggers the ng-show of just that object?

SUMMARYI have a list of brands and a list of products. I am using an ng-repeat to show the list of brands, and an ng-repeat with a filter to show the list of products within their respective brands. I want each brand and each product to have a button that shows more about that brand/product. All of these buttons should use the same function on the controller.
PROBLEMThe button that shows more about the brand also shows more about each of that brand's products, UNLESS (this is the weird part to me) I click the button of a product within that brand first, in which case it will work correctly.
CODEPlease see the Plunker here, and note that when you click on 'show type' on a brand, it also shows all the types of the products within that brand: http://plnkr.co/edit/gFnq3O3f0YYmBAB6dcwe?p=preview
HTML
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MyController as vm">
<div ng-repeat="brand in brands">
<h1>
{{brand.name}}
</h1>
<button ng-click="showType(brand)">
Show Brand Type
</button>
<div ng-show="show">
{{brand.type}}
</div>
<div ng-repeat="product in products
| filter:filterProducts(brand.name)">
<h2>
{{product.name}}
</h2>
<button ng-click="showType(product)">
Show Product Type
</button>
<div ng-show="show">
{{product.type}}
</div>
</div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="script.js"></script>
</body>
</html>
JAVASCRIPT
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
$scope.brands = [{
name: 'Kewl',
type: 'Cereal'
}, {
name: 'Joku',
type: 'Toy'
}, {
name: 'Loko',
type: 'Couch'
}]
$scope.products = [{
name: 'Kewlio',
type: 'Sugar Cereal',
brand: 'Kewl'
}, {
name: 'Kewliano',
type: 'Healthy Cereal',
brand: 'Kewl'
}, {
name: 'Jokurino',
type: 'Rattle',
brand: 'Joku'
}, {
name: 'Lokonoko',
type: 'Recliner',
brand: 'Loko'
}, {
name: 'Lokoboko',
type: 'Love Seat',
brand: 'Loko'
}]
$scope.showType = function(item) {
this.show = !this.show;
}
$scope.filterProducts = function(brand) {
return function(value) {
if(brand) {
return value.brand === brand;
} else {
return true;
}
}
}
});
IMPORTANT NOTE: I realize I could add an attribute to the object (brand.show) and pass the object into the function, then change that attribute to true/false, but I don't want to do this because in my actual application, the button will show a form that edits the brand/product and submits the info to Firebase, and I don't want the object to have a 'show' attribute on it. I would rather not have to delete the 'show' attribute every time I want to edit the info in Firebase.
ng-repeat directive create own scope, when you do
this.show = !this.show
you create/change show property in current scope, if click brand button - for brand scope, that global for product, and when click in product button - for scope concrete product.
To avoid this, you should create this property before clicking button, for example with ng-init, like
ng-init="show=false;"
on element with `ng-repeat" directive
Sample
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
$scope.brands = [{
name: 'Kewl',
type: 'Cereal'
}, {
name: 'Joku',
type: 'Toy'
}, {
name: 'Loko',
type: 'Couch'
}]
$scope.products = [{
name: 'Kewlio',
type: 'Sugar Cereal',
brand: 'Kewl'
}, {
name: 'Kewliano',
type: 'Healthy Cereal',
brand: 'Kewl'
}, {
name: 'Jokurino',
type: 'Rattle',
brand: 'Joku'
}, {
name: 'Lokonoko',
type: 'Recliner',
brand: 'Loko'
}, {
name: 'Lokoboko',
type: 'Love Seat',
brand: 'Loko'
}]
$scope.showType = function(item) {
this.show = !this.show;
}
$scope.filterProducts = function(brand) {
return function(value) {
if (brand) {
return value.brand === brand;
} else {
return true;
}
}
}
});
/* Styles go here */
h1 {
font-family: impact;
}
h2 {
font-family: arial;
color: blue;
margin-bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyController as vm">
<div ng-repeat="brand in brands" ng-init="show=false">
<h1>
{{brand.name}}
</h1>
<button ng-click="showType(brand)">
Show Brand Type
</button>
<div ng-show="show">
{{brand.type}}
</div>
<div ng-repeat="product in products
| filter:filterProducts(brand.name)" ng-init="show=false">
<h2>
{{product.name}}
</h2>
<button ng-click="showType(product)">
Show Product Type
</button>
<div ng-show="show">
{{product.type}}
</div>
</div>
</div>
</div>
</div>
The easiest fix for this, if you don't mind putting temporary properties in your data is the following changes:
<div ng-show="product.show">
{{product.type}}
</div>
and
<div ng-show="brand.show">
{{brand.type}}
</div>
and then in your controller
$scope.showType = function(item) {
item.show = !item.show;
}
Alternatively, if you don't want to touch the object properties, you can create an $scope.shownTypes array and have your click either push the object into or remove the object from the shown array. THen you can check for the object's existence in the array and show or not show the type appropriately. Let me know if you need a sample of that.
Your show boolean attribute same for whole tree (is in same scope). Using angular directive with child scope scope:true in ng-repeat helps to isolate each show property. I have forked your plunker code:
http://plnkr.co/edit/cMSvyfeCQOnTKG8F4l55?p=preview

Incrementing an upvote with ng-repeat

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

Dynamically bind objects to inputs in angular

I have these objects right here that I will use to save data from a form, and later send it to an api as JSON :
$scope.price = {}
$scope.item = {"price":$scope.price, };
I also have these field which will be used to dynamically generate inputs on a html page:
$scope.fields = [
{
name: $scope.item.title,
title: 'Title',
type: {
view: 'input'
}
},
{
name: $scope.price.regular,
title: 'Regualar Price',
type: {
view: 'input'
}
}
];
Now in order to generate the form I use this code:
<div class="form-group" ng-repeat="field in fields">
<label>{{ field.title }}:</label>
<span ng-switch on="field.type.view">
<span ng-switch-when="input">
<input
ng-model=field.name
type="text"
/>
</span>
</span>
</div>
And with this code, it is not assigning the values in the input to the objects. Is there a way to do it? I know I can do it this way:
ng-model="item[field.name]"
But that limits me to only one level of the object. I want to be able to bind nested objects. And I just can't seem to figure it out. Thank You!

list of buttons created by ng-repeat: collect states under one array in the $scope model?

I have a bunch of angular-bootstrap buttons made using ng-repeat:
<div class="btn-group">
<label class="btn btn-primary" ng-model="???what to insert???" btn-checkbox
ng-repeat="station in stationlist">{{station.name}}</label>
</div>
where I want to insert something in ng-model, that would give me in the end an associative array, e.g., buttons[key] = true|false. Key must be e.g. station.name, that is, derived from ng-repeat object.
Note that these angular-bootstrap buttons insert true|false to connected ng-model variables; see example: http://angular-ui.github.io/bootstrap/#/buttons. The problem is that I need to have an array element in the model, so later I want iterate on the array.
Maybe you can try this :
$scope.stationlist = [
{ name: 'a', checked: true },
{ name: 'b', checked: false },
{ name: 'c', checked: true },
...
];
And bind it like :
<label class="btn btn-primary" ng-model="station.checked" btn-checkbox
ng-repeat="station in stationlist">{{station.name}}</label>

Categories

Resources