Angular, connect objects and display right information - javascript

I'm creating an angular webapp, listing different cars in a sidebar and some information about the specific car in a informationbox.
The purpose is to show the right information in the box when clicking the different cars.
I have two different arrays(two API-endpoints), where the first array lists the car name, and the other one got the information about the car. But I have no idea how to connect the objects with the primary key and the foreign key, and how I'm supposed to output the right information after clicking the car.
app.js:
angular.module('myApp', [])
.controller('MyController', function($scope, $http) {
function fetch() {
$http({method : 'GET',url : 'http://*cars*'})
.success(function(data) {
$scope.cars = data;
});
$http({method : 'GET',url : 'http://*information*'})
.success(function(data) {
$scope.information = data;
});
}
fetch();
})
html:
<div id="sidebar">
<ul>
<li ng-repeat="name in cars">{{ name.displayName }}</li>
</ul>
</div>
For now all I have done is that I've fetched the data and outputed the cars in the sidebar. But now I've been googling and trying to connect the cars to the information with loops and functions for hours, but stil clueless.
Yes, I'm new to this. Any kind of help would be great! Thanks

You can deal with this with the ng-route. You can do something like :
In your route definition:
.when(/cars/:Id), {
name: 'cars',
templateUrl : 'ayourtemplate.html',
controller : 'yourCtrl'
})
In your html:
<div id="sidebar">
<ul>
<li ng-repeat="name in cars">{{ name.displayName }}</li>
</ul>
</div>
The Id will be your key tou will just have to match the right key in your $scope.information

It depends on what information those arrays contains.
If you're sure, that every element corresponds to other, you can just use $index in the html.
<li ng-repeat="name in cars">
{{ name.displayName }}
<p>{{ information[$index] }}</p>
</li>
However, if elements in array aren't ordered, you will have to check primary keys of objects in arrays. Let's assume, that data in arrays looks like this:
cars:
[
{ id: "1", name: "Carrera GT" },
{ id: "2", name: "DB 11" },
... and so on
]
information:
[
{ id: "2", info: "Lorem ipsum" },
{ id: "1", info: "Dolor sit amet" },
...
]
Then I'd suggest using loops and constructing new array using ids.
var carinfo = [];
cars.forEach(car => {
obj["id"] = car.id;
obj["name"] = car.name;
obj["info"] = ""; // Placeholder
info.forEach(c => {
if (c.id === car.id) {
obj["info"] = c.info;
}
});
carinfo.push(obj);
});
$scope.carInfo = carinfo;
Then you can use $scope.carInfo in the html file.
<li ng-repeat="car in carInfo">
{{ car.name }}
<p>{{ car.info }}</p>
</li>

Related

AngularJS: I clearly do not understand the use of ng-init

I have an extremely hierarchical JSON structure as a scope variable in my AngularJS controller. I want to loop around different sections of that variable. I thought about using ng-init to specify where in the structure I am. Here is some code:
my_app.js:
(function() {
var app = angular.module("my_app");
app.controller("MyController", [ "$scope", function($scope) {
$scope.things = {
name: "a",
children: [
{
name: "a.a",
children: [
{ name: "a.a.a" },
{ name: "a.a.b" }
]
},
{
name: "a.b",
children: [
{ name: "a.b.a" },
{ name: "a.b.b" }
]
}
]
}
}]);
});
my_template.html:
<div ng-app="my_app" ng-controller="MyController">
<ul>
<li ng-init="current_thing=things.children[0]" ng-repeat="thing in current_thing.children>
{{ thing.name }}
</li>
</ul>
</div>
I would expect this to display a list:
a.a.a
a.a.b
But it displays nothing.
Of course, if I specify the loop explicitly (ng-repeat="thing in things.children[0].children") it works just fine. But that little snippet of template code will have to be run at various points in my application at various levels of "things."
(Just to make life complicated, I can get the current thing level using standard JavaScript or else via Django cleverness.)
Any ideas?
ng-init runs at a lower priority (450) than ng-repeat (1000). As a result, when placed on the same element, ng-repeat is compiled first meaning that the scope property created by ng-init won't be defined until after ng-repeat is executed.
As a result, if you want to use it in this manner, you'd need to place it on the parent element instead.
<div ng-app="my_app" ng-controller="MyController">
<ul ng-init="current_thing=things.children[0]">
<li ng-repeat="thing in current_thing.children>
{{ thing.name }}
</li>
</ul>
</div>

AngularJS ng-repeat custom directive

<div>
<ul id="teachers">
<li ng-repeat></li>
</ul>
<ul id="students">
<li ng-repeat></li>
</ul>
</div>
I have two ul elements and dynamic data. For example:
[
{
name: 'Jack'
status: 'teacher'
},
{
name: 'Angelina'
status: 'teacher'
},
{
name: 'Maya'
status: 'student'
},
{
name: 'Kate'
status: 'teacher'
},
{
name: 'Margaret'
status: 'student'
}
]
I want to write some custom directive for ng-repeat, which will generates lists, for students and for teachers, for different ul's.
How can I write directive, with some condition, which will repeat li's in the right ul?
Yes, I can, filter My data and generate two Arrays, for students and teachers and than repeat those Independently.
But, I don't like this way. How it is possible to write one custom directive which will determines, where to repeat current Object?
UPDATE
Okey, I'm new in angular, so I've thought, that there will be something simple trick, something like this:
if(status === 'something')
use this template
else
use this template
So, with your answers I could write conditions which I wanted. Sorry about this, this was stupid decision.
So my actual case is:
I have Breadcrumbs data and main container, which width is equal to 500px.
I want to repeat li in this container and I want to my li's were always always inline.
If my data will be big, or some title will be big and my ul width will be more, than my container, some li elements will be dropped bellow.
because of this, I have two ul elements and lis which won't have there space I want to insert in second ul, which will be hidden and after click on something I will show this ul
Options:
Use in built angular filters. For example:
<ul id="teachers">
<li ng-repeat="person in people | filter: { status: 'teacher' }"></li>
</ul>
plnkr
Split the array in your controller. Both split arrays should still point to the original object (in the original array), so manipulation should be ok.
You can definitely create your own directive, but you will end up encapsulating one of the options above.
Better than write a directive, filter your array javascript with the built-in functions for array.
Example:
HTML
<div ng-controller="ClassroomController as classroom">
<ul id="teachers">
<li ng-repeat="teacher in classroom.teachers track by $index"></li>
</ul>
<ul id="students">
<li ng-repeat="student in classroom.students track by $index"></li>
</ul>
</div>
JAVASCRIPT
function Controller() {
var vm = this;
vm.data = [
{
name: 'Jack'
status: 'teacher'
},
{
name: 'Angelina'
status: 'teacher'
},
{
name: 'Maya'
status: 'student'
},
{
name: 'Kate'
status: 'teacher'
},
{
name: 'Margaret'
status: 'student'
}
];
vm.teachers = vm.data.filter(function(item){return item.status === 'teacher'});
vm.students = vm.data.filter(function(item){return item.status === 'student'});
}
I also think that filtering is the best as already answered. But according to your update you can do something like this in yuor directive controller:
$scope.getTemplateUrl = function() {
if (status == something) {
return '/partials/template1.html';
} else {
return '/partials/template2.html';
}
}
Then define your directive template as follows:
template: '<ng-include src="getTemplateUrl()"/>',
Of course status has to be defined before the directive is rendered.
directive('info', function()
{
return {
restrict : 'E',
template : '<ul> <li ng-repeat="l in list"><div ng-if="check(l)">{{l.name}}</div></li></ul></br><ul><li ng-repeat="l in list"><div ng-if="!check(l)">{{l.name}}</div></li></ul>',
controller : function($scope)
{
$scope.check = function(l)
{
if(l.status=="student")
return true;
else if(l.status=="teacher")
return false;
}
}
};
});

updating text on view from ng-repeat

I have problem with updating view by using ng-repeat.
When click on text, it doesnt update but it overwrites below. (I want have panel with names(links) and show its description on view)
I have searched everything and couldnt find answer or something useful what would help me.
html:
<body>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<ul>
<li><a ng-repeat="item in items" ng-click="getInfo(item)" > {{item.name}} <br> </a></li>
</ul>
</div>
<hr>
<div ng-controller="myInfo">
<div ng-repeat="info in item" >
<h3>Name: {{info.name}}</h3>
<p> ID: {{info._id}}</p>
<p> temp: {{info.temp}} </p>
</div>
</div>
</div>
</body>
js
var app = angular.module('myApp', [])
app.controller('myCtrl', function($scope, $http, shareDataService) {
$http.jsonp('data.json').success(function(data) {
$scope.items = data;
});
$scope.getInfo = function(item) {
shareDataService.addItem(item);
}
});
app.controller('myInfo', function( $scope, shareDataService ){
$scope.item = shareDataService.getItem();
});
app.service('shareDataService', function() {
var myItem = [];
var addItem = function(newObj) {
myItem.push(newObj);
}
var getItem = function(){
return myItem;
}
return {
addItem: addItem,
getItem: getItem
};
});
json
angular.callbacks._0([
{
"_id": 1,
"temp": "asdgdf",
"name": "name1"
},
{
"_id": 2,
"temp": "asdasdasd",
"name": "name2"
},
{
"_id": 3,
"temp": "asasdasdadgdf",
"name": "name3"
}
]);
Plunker: http://plnkr.co/edit/X65oH0yAkRnN8npKnFY2?p=preview
You have an error in your console. Just add track by to your ng-repeat:
<div ng-repeat="info in item track by $index">
ng-repeat needs a unique id to track the items in order to be able to update them. If you add the same item twice, ng-repeat sees the same item twice, ans loses its mind. Using $index (which is unique) resolves that issue.
Keep in mind that using $index is adequate here, but it's preferred to use a unique id from the object if you can.
EDIT:
If your issue is that you want to see only the one element you clicked on in your view, then the issue is that you are adding your item to an array, when you should just be setting a value in your service. And, obviously, no need of a ng-repeat in your view.
http://plnkr.co/edit/Wfg9KhCWKMDreTFtirhR?p=preview
JS:
app.controller('myCtrl', function($scope, $http, shareDataService) {
//[...]
$scope.getInfo = function(item) {
shareDataService.setItem(item);
}
});
app.controller('myInfo', function( $scope, shareDataService ){
$scope.$watch(function () {
return shareDataService.getItem();
}, function (value) {
$scope.info = value;
})
});
app.service('shareDataService', function() {
var myItem;
return {
setItem: function(newObj) {
myItem = newObj;
},
getItem: function(){
return myItem;
}
};
});
HTML:
<div ng-controller="myInfo" ng-show="info">
<h3>Name: {{info.name}}</h3>
<p> ID: {{info._id}}</p>
<p> temp: {{info.temp}} </p>
</div>
If you only want to display information of the item that you just have clicked, then we don't need the second ng-repeat (as jlowcs said).
We also don't have to defined myItem as a array, it's just unnecessary, I think.
Edit:
how embarrassing i am, my answer look exactly to same as jlowcs's. I guess I took to much time to figure out the answer.
One thing to add up:
Why do I need a $watch in myInfo controller?
Because at the first time, we use ng-repeat, this component do the watch part for us. Then when I remove ng-repeat, I need to watch for data changing by myself.

Angular JS: Http:get on JSON created by local API

So I'm teaching myself Angular.js. Following a tutorial I found online, I created a RESTful API with Laravel for storing URLS. There's basic authentication set up and currently user id 1 is signed in. I want to grab the JSON that's being returned here in the index function which is all the URLs in the database where the user_id matches the authorized user's id:
public function index()
{
$urls = Url::where('user_id', Auth::user()->id)->get();
return Response::json(array(
'error=> false,
'urls' => $urls->toArray()),
200
);
}
I believe JSON is being returned and when I visit my local site http://readitlater.loc/api/v1/url I get an array of objects. On my index page, I have the ng-app directive being defined as an attribute of the html element:
<html lang="en" data-ng-app="">
I have a script defining a constructor function:
<script>
function mainController($scope, $http)
{
$http.get("http://readitlater.loc/api/v1/url/")
.success(function(response) {$scope.urls = response;});
}
</script>
That readitlater.loc/api/v1/url is my route to the index in my API. And when I type it in the browser I get an array of objects which I'm guessing is the JSON being created. For some reason, I can't get it to display in the browser down here:
<body class="container" data-ng-controller="mainController">
<ul class="list-group">
<li class="list-group-item" ng-repeat="address in urls">
{{ address.url }}
</li>
</ul>
As per your comments above, your $scope.urls has urls object and this object contains url key. so you would change your code as follows
<ul class="list-group">
<li class="list-group-item" ng-repeat="address in urls.urls">
{{ address.url }}
</li>
</ul>
It's often handy to validate the variables you are using contain what you think. You can do a console.log or console.dir when you set the value, but displaying them in the HTML right where you are trying to use them can help with scope problems too. Here I would change your code to this:
<pre>{{urls|json}}</pre>
<ul class="list-group">
<li class="list-group-item" ng-repeat="address in urls">
{{ address.url }}
<br/>JSON: {{address|json}}
</li>
</ul>
This should quickly show you that your $scope.urls object looks like this:
{
error:false,
urls: [
{ description: "A Great Blog", id: 2, url: "fideloper.com", user_id: 1 }
]
}
THAT is your $scope.urls object, so when you repeat over it you will get the properties of the object, 'false' and the array that I assume is what you mean by urls:
// Code goes here
var app = angular.module('MyApp', []);
var ctrl = app.controller('MyCtrl', function($scope) {
$scope.name = "Jason Goemaat";
$scope.urls = {
error:false,
urls: [
{ description: "A Great Blog", id: 2, url: "fideloper.com", user_id: 1 }
]
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<h1>Hello, {{name}}!</h1>
<pre>{{urls|json}}</pre>
<ul class="list-group">
<li class="list-group-item" ng-repeat="address in urls">
{{ address.url }}
<br/>JSON: {{address|json}}
</li>
</ul>
</div>
So if you want $scope.urls to be your array, you can set it directly to the urls property on your return object in the $http call:
$http.get("http://readitlater.loc/api/v1/url/")
.success(function(response) {$scope.urls = response.urls;});
Now your ng-repeat will be iterating over the array as I think you intend...
var app = angular.module('MyApp', []);
var ctrl = app.controller('MyCtrl', function($scope) {
$scope.name = "Jason Goemaat";
$scope.urls = [
{ description: "A Great Blog", id: 2, url: "fideloper.com", user_id: 1 }
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<h1>Hello, {{name}}!</h1>
<pre>{{urls|json}}</pre>
<ul class="list-group">
<li class="list-group-item" ng-repeat="address in urls">
{{ address.url }}
<br/>JSON: {{address|json}}
</li>
</ul>
</div>

Advanced AngularJS custom filtering on ngRepeat objects

I want to achieve the following theoretical code:
VIEW.html
<li ng-repeat="player in players | filter:myCustomFilter(player)">{{player.name}}
CONTROLLER.js
// some theoretical conditional statement that return a boolean
$scope.otherCondition = true;
$scope.myCustomFilter = function(player) {
return player.name.substring(0,1).match(/A/gi) && $scope.otherCondition;
}
So I want all of my players to be loaded into an Angular model, but I only want to render players into the DOM whose names start with the letter 'A'. When I try and do something like this, my console informs me that player is undefined. Do I need to write a custom filter in order to achieve this (via angular.module().filter())?
Here's a working fiddle: http://jsfiddle.net/orlenko/jV6DK/
Html code (exactly as Karl Zilles suggested):
<div ng-app>
<div ng-controller="MyController">
<h2>Names starting with "A":</h2>
<ul>
<li ng-repeat="player in players | filter:myCustomFilter">{{player.name}}</li>
</ul>
<h2>All Names:</h2>
<ul>
<li ng-repeat="player in players">{{player.name}}</li>
</ul>
</div>
</div>
Javascript:
function MyController($scope) {
$scope.players = [{
name: 'Arthur'
}, {
name: 'William'
}, {
name: 'Bertha'
}, {
name: 'Alice'
}];
$scope.otherCondition = true;
$scope.myCustomFilter = function(player) {
return player.name.substring(0,1).match(/A/gi) && $scope.otherCondition;
}
}
Result
You don't need to pass player to the filter
<li ng-repeat="player in players | filter:myCustomFilter">{{player.name}}
Should work
The answers given are only partially correct, if you need to pass more arguments to the function you would need to create a closure and pass those arguments to the closure as follow:
The 'A' is passed to the closure and player is passed as a part of the context.
HTML:
<div ng-app>
<div ng-controller="MyController">
<h2>Names starting with "A":</h2>
<ul>
<li ng-repeat="player in players | filter:myCustomFilter('A')">{{player.name}}</li>
</ul>
<h2>All Names:</h2>
<ul>
<li ng-repeat="player in players">{{player.name}}</li>
</ul>
</div>
</div>
JS:
function MyController($scope) {
$scope.players = [{
name: 'Arthur'
}, {
name: 'William'
}, {
name: 'Bertha'
}, {
name: 'Alice'
}];
$scope.otherCondition = true;
$scope.myCustomFilter = function(letter) {
return function(player) {
var rgxp = new RegExp(letter, "g");
return player.name.substring(0, 1).match(rgxp) && $scope.otherCondition;
}
}
}
Checkout a working jsfiddle

Categories