I'm using Yeoman - angular generator.
JSBin: JSBin Link
I have a simple list of airports being set from a factory angApp.factory("Airports", function() {}); and displayed from ng-repeat <ul ng-repeat="airport in airports.detail">.
I would like to have an interaction that when each link is clicked it will match the airport code and display it in a new paragraph tag <p class="current">Current: {{currentAirport.name}}</p>.
Why wont the setAirport(airport.code) function set the currentAirport.name(or display?) when airport link is clicked in html?
Controller
angular.module("ang6App")
.controller("AirportsCtrl", function ($scope, Airports) {
$scope.formURL = "views/_form.html";
$scope.currentAirport = null;
$scope.airports = Airports;
$scope.setAirport = function(code) {
$scope.currentAirport = $scope.airports[code];
};
});
Factory Service in the same module
angApp.factory("Airports", function() {
var Airports = {};
Airports.detail = {
"PDX": {
"code": "PDX",
"name": "Portland International Airport",
"city": "Portland",
"destinations": [
"LAX",
"SFO"
]
},
"STL": {
"code": "STL",
"name": "Lambert-St. Louis International Airport",
"city": "St. Louis",
"destinations": [
"LAX",
"MKE"
]
},
"MCI": {
"code": "MCI",
"name": "Kansas City International Airport",
"city": "Kansas City",
"destinations": [
"LAX",
"DFW"
]
}
};
return Airports;
});
HTML
<div class="container" ng-controller="AirportsCtrl">
<ul ng-repeat="airport in airports.detail">
<li>{{airport.code}} -- {{airport.city}} </li>
</ul>
<p class="current"> current: {{currentAirport.name}}</p>
</div>
Your setAirport function should be written as:
$scope.setAirport = function (code) {
$scope.currentAirport = $scope.airports.detail[code];
};
But then, this could be simplified by passing the actual airport object directly:
<a href ng-click="setAirport(airport)">{{airport.code}}</a>
$scope.setAirport = function (airport) {
$scope.currentAirport = airport;
};
Related
I am trying to make it so that when the user clicks one of the 30 teams, the team that is clicked is queried with the Giphy API.
The giphy API key used is the public API key.
// all 30 NBA Teams //
var predefinedButtons = [
"Atlanta Hawks",
"Boston Celtics",
"Brooklyn Nets",
"Charlotte Hornets",
"Chicago Bulls",
"Cleveland Cavaliers",
"Dallas Mavericks",
"Denver Nuggets",
"Detroit Pistons",
"Golden State Warriors",
"Houston Rockets",
"Indiana Pacers",
"LA Clippers",
"LA Lakers ",
"Memphis Grizzlies",
"Miami Heat",
"Milwaukee Bucks",
"Minnesota Timberwolves",
"New Orleans Hornets",
"New York Knicks",
"Oklahoma City Thunder",
"Orlando Magic",
"Philadelphia Sixers",
"Phoenix Suns",
"Portland Trail Blazers",
"Sacramento Kings",
"San Antonio Spurs",
"Toronto Raptors",
"Utah Jazz",
"Washington Wizards"
];
console.log(predefinedButtons);
// The Buttons added dynamically //
var $nbaTeams;
var nbaButtons = function nbaGiphy() {
for ( i in predefinedButtons ) {
$nbaTeams = $("<button class='.btn btn-secondary' 'onclick='getNBAGiphy()''>").text(predefinedButtons[i]);
$("#nbaTags").append($nbaTeams);
}
}
nbaButtons();
// The code below is where the event listener is 'undefined' //
function getNBAGiphy() {
var nbaSearchGifs;
nbaSearchGifs.addEventListener('click', function() {
nbaSearchGifs = $(".btn btn-secondary").val();
xhr = $.get("http://api.giphy.com/v1/gifs/search?q="+nbaSearchGifs+"&api_key=dc6zaTOxFJmzC&limit=15");
xhr.done(function (response) {
console.log("success got data", response);
nbaTeamData = response.data
$("#giphyContent").html("");
console.log(nbaSearchGifs);
})
});
}
getNBAGiphy();
You are declaring variable and you have not assigned any value to it. So by default it is undefined. Code i am referring to is:
function getNBAGiphy() {
var nbaSearchGifs;
nbaSearchGifs.addeventListner
I have an object like:
export const contact = {
_id: "1",
first:"John",
name: {
first:"John",
last:"Doe"
},
phone:"555",
email:"john#gmail.com"
};
I am reading it like
return (
<div>
<h1>List of Contact</h1>
<h1>{this.props.contact._id}</h1>
</div>
)
in this scenario I am getting expected output.
return (
<div>
<h1>List of Contact</h1>
<h1>{this.props.contact.name.first}</h1>
</div>
)
But when I read the nested property I am getting error like
Uncaught TypeError: Cannot read property 'first' of undefined
How to read these king of nested objects in react? Here is my source
Three things you need to address here:
this is your contacts-data and i don't see any first property within the name object:
export const contacts = {
"name": "mkyong",
"age": 30,
"address": {
"streetAddress": "88 8nd Street",
"city": "New York"
},
"phoneNumber": [{
"type": "home",
"number": "111 111-1111"
}, {
"type": "fax",
"number": "222 222-2222"
}]
};
You are calling this.props.fetchContacts(); on componentDidMount, hence in the first render call the state is still empty, the action call and the reducer will pass new props or change the state then you get to the second render call and that's the moment you have the data ready for use.
So you should check for the existing of the data before you try to use it. one way is to just conditionally render it (of course there are better ways to do it, this is just to make a point):
render() {
const { contacts } = this.props;
return (
<div>
<h1>List of Contacts</h1>
<h2>{contacts && contacts.name && contacts.name.first
//still getting error. "this.props.contacts.name" alone works
}</h2>
<h2>{contacts && contacts.address && contacts.address.city}</h2>
</div>
)
}
You are trying to use this.props.contact.name.first is that a typo? shouldnt it be contacts instead of contact?
EDIT:
As a followup for your comment, as a general rule in JavaScript (or any other language for that manner) you should always check the existence reference of an object before you are trying to access it's properties.
As for your use case you can use defaultProps if you must have a value to render or you can even simplify the scheme of your data.
This is much simpler to manage:
export const contacts =
{
"fName": "mkyong",
"lName": "lasty",
"age": 30,
"streetAddress": "88 8nd Street",
"city": "New York",
"homeNumber": "111 111-1111",
"faxNumber": "222 222-2222"
};
Than this:
export const contacts =
{"name": "mkyong",
"age": 30,
"address": {
"streetAddress": "88 8nd Street",
"city": "New York"
},
"phoneNumber": [
{
"type": "home",
"number": "111 111-1111"
},
{
"type": "fax",
"number": "222 222-2222"
}
]
};
So I am trying to use the orderby function of angularjs. Currently I have an original data set.
$scope.customers = [
{"name" : "Bottom-Dollar Marketse" ,"city" : "Tsawassen"},
{"name" : "Alfreds Futterkiste", "city" : "Berlin"},
{"name" : "Bon app", "city" : "Marseille"},
{"name" : "Cactus Comidas para llevar", "city" : "Buenos Aires"},
{"name" : "Bolido Comidas preparadas", "city" : "Madrid"},
{"name" : "Around the Horn", "city" : "London"},
{"name" : "B's Beverages", "city" : "London"}
];
$scope.reverse= false;
$scope.toggleOrder = function(){
$scope.reverse=!$scope.reverse;
}
If I use the following to display my customers, I would get the array reverse ordered by the city. Currently I could click on the toggle button and reverse the array if wanted to.
<button ng-click="toggleOrder()">ToggleReverse</button >
<li ng-repeat="x in customers | orderBy : 'city': reverse">{{x.name + ", " + x.city}}</li>
But now the issue is if I didn't want the orderBy function at all. If I wanted to get my original customers data without any order how could I do that with the same toggleOrder function?
For instance, When I load the data it would be the original array. If 1st click of the toggleOrder button, it would sort in based on city, 2nd click of the toggleOrder button would reverse sort the city, and third click of the toggleOrder button would have no sort and give me the original array, and so on.
If the orderBy function isn't the best to go by let me know.
Any help would be great!
I'm not sure why you want to add this feature on your application, but here you go:
(function() {
'use strict';
angular
.module('app', [])
.constant('BUTTON_VALUES', {
1: 'Ascending',
2: 'Descending',
3: 'No order',
})
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope', 'BUTTON_VALUES'];
function MainCtrl($scope, BUTTON_VALUES) {
$scope.customers = [
{
"name": "Bottom-Dollar Marketse",
"city": "Tsawassen"
},
{
"name": "Alfreds Futterkiste",
"city": "Berlin"
},
{
"name": "Bon app",
"city": "Marseille"
},
{
"name": "Cactus Comidas para llevar",
"city": "Buenos Aires"
},
{
"name": "Bolido Comidas preparadas",
"city": "Madrid"
},
{
"name": "Around the Horn",
"city": "London"
},
{
"name": "B's Beverages",
"city": "London"
}
];
$scope.btnValue = BUTTON_VALUES[3];
$scope.reverse = true;
$scope.orderParam = '';
var increment = 0;
$scope.toggleOrder = function() {
increment++;
$scope.btnValue = BUTTON_VALUES[increment];
switch (increment) {
case 1:
case 2:
$scope.orderParam = 'city';
$scope.reverse = !$scope.reverse;
break;
case 3:
$scope.orderParam = '';
increment = 0;
break;
}
}
}
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="toggleOrder()">{{btnValue}}</button>
<pre ng-bind-template="Order - {{orderParam}}"></pre>
<pre ng-bind-template="Reverse? {{reverse}}"></pre>
<hr>
<li ng-repeat="x in customers | orderBy : orderParam: orderParam && reverse">{{x.name + ", " + x.city}}</li>
</body>
</html>
Note: I added a constant as an example to demonstrate how you can handle your button name.
I hope it helps.
So, you want the original order the third time you click on the order by button. Seems doable but complicated. Maybe instead you should have another button that is labeled "original order" and a hidden column that lists the index of your original order. Pushing that button orders by that original index.
/edited I rather use another approach of angualrjs filters which is basically taking string as param and matching it to the object in list.
jsfiddle.net/2q14sryb
Hope it works!
fiddle here http://jsfiddle.net/prantikv/1nvdzv24/9/
i have some uneven data like so
[{
"fname": "Tonja", //common
"lname": "Mize",
"tel": "(963)784-1098",
"address": "3999 Quis Ln",
"city": "Sebring",
"state": "MI",
"zip": 76593
},
{
"fname": "Stella", //common
"Othername": "Lester",
"mobile": "(936)898-2886"
}];
notice only the fname property is common between the two objects
so when i do this
<li ng-repeat="(key,val) in populationList | filter:name">
{{ val.**fname**}}
</li>
i do get the fname but the data is uneven so i cannot figure out how to go through over each object. also the length of the object is different as well.
what i want to do is to filter the data over a select list
<select ng-model="name">
<option value="Tonja" selected="Tonja">Tonja</option>
<option value="Stella">Stella</option>
</select>
but i am unable to figure out a way to display the unmatched properties of objects
is there a way i get all the key:value pairs on the sub data dynamically?
WORKING DEMO
Your Html,
<div ng-app='app'>
<div ng-controller="DemoCtrl">
<select ng-options="item.fname for item in populationList | fieldList:'fname'" ng-model="myItem" ng-change="changeSelection(myItem)">
</select>
<li ng-repeat="key in availableKeys">
{{selectedObject[key]}}
</li>
</div>
</div>
JS
angular.module('filters',[]).
filter('fieldList', function() {
return function(populationList, parameter) {
var filteredArray = [];
angular.forEach(populationList, function(value, index) {
if(value.hasOwnProperty(parameter)) {
filteredArray.push(value);
}
});
return filteredArray;
};
});
angular.module('app',['filters'])
.controller('DemoCtrl', function($scope) {
$scope.changeSelection = function(item) {
$scope.selectedObject = item;
$scope.availableKeys = Object.keys($scope.selectedObject);
};
$scope.populationList = [{
"fname": "Tonja", //common
"lname": "Mize",
"tel": "(963)784-1098",
"address": "3999 Quis Ln",
"city": "Sebring",
"state": "MI",
"zip": 76593
},
{
"fname": "Stella", //common
"Othername": "Lester",
"mobile": "(936)898-2886"
}];
});
I am reading the below json value from a module.js
.controller('home.person',['$scope','$filter','personResource',function($scope,$filter,personResource) {
$scope.searchPerson = function() {
var params = $scope.search || {};
params.skip=0;
params.take =10;
$scope.personDetails =
{
"apiversion": "0.1",
"code": 200,
"status": "OK",
"mydata": {
"myrecords": [
{
"models": [
{
"name": "Selva",
"dob": "10/10/1981"
}
],
"Address1": "ABC Street",
"Address2": "Apt 123",
"City": "NewCity1",
"State": "Georgia"
},
{
"models": [
{
"name": "Kumar",
"dob": "10/10/1982"
}
],
"Address1": "BCD Street",
"Address2": "Apt 345",
"City": "NewCity2",
"State": "Ohio",
"Country":"USA"
},
{
"models": [
{
"name": "Pranav",
"dob": "10/10/1983"
}
],
"Address1": "EFG Street",
"Address2": "Apt 678",
"City": "NewCity3",
"State": "NewYork",
"Country":"USA",
"Zipcode" :"123456"
}
]
}
}
}
}])
Now i am able to statically build the UX. But my each record set's key value pair count is different. So i want to build my html dynamically as per the current record set's count.Country & Zipcode is not exist in all records so i need to build dynamically the build and populate the html output.Most of the time, my json output is dynamic. Instead of persondetails, i may get the json output of a product details instead of PersonDetails.
<div ng-show="personDetails.mydata.myrecords.length > 0" ng-repeat="recordSingle in personDetails.mydata.myrecords">
<div >
<span >Address1: {{recordSingle.Address1}}</span>
<span >Address2: {{recordSingle.Address2}}</span>
<span>City: {{recordSingle.City}}</span>
<span>State: {{recordSingle.State}}</span>
<span>Country: {{recordSingle.Country}}</span>
<span>Zipcode: {{recordSingle.Zipcode}}</span>
</div>
</div>
One way is to use ng-if statement, for the optional span elements:
<span ng-if="recordSingle.Address1">Address1: {{recordSingle.Address1}}</span>
[Update #1: updated based on revised comments to question]
[Update #2: fixed typos in function and included plunkr]
I now understand that you want to dynamically build the display objects based on properties from the JSON object. In this case, I would iterate through the properties of the object. I would use a function to produce this array of properties for each object so that you can filter out any prototype chains. I would also remove out any unwanted propoerties, such as the internal $$hashKey and perhaps the array objects e.g.
In your controller:
$scope.getPropertyNames = getPropertyNames;
function getPropertyNames(obj) {
var props = [];
for (var key in obj) {
if (obj.hasOwnProperty(key) && !angular.isArray(obj[key]) && key !== '$$hashKey') {
props.push(key);
}
}
return props;
}
Then in your HTML view:
<div ng-repeat="record in personDetails.mydata.myrecords">
<div ng-repeat="prop in getPropertyNames(record)">
<span ng-bind="prop"></span>: <span ng-bind="record[prop]"></span>
</div>
</div>
This works for me... see this plunker. It is displaying each of the properties of the object in the array dynamically (you could have any property in the object). Is this not what you are trying to achieve?