I am working on an AngularJS tutorial
This tutorial covers the ng-repeat directive, an AngularJS directive used repeating data.
To show an example of ng-repeat, The author enters periodic table elements in a JSON format, covering element's name, element #, etc into controller logic($scope)
To display the elements(code below), the author simply used the directive with a html un-ordered list
<ul>
<li data-ng-repeat="element in periodic.elements">{{element.name}} </li>
</ul>
I tried doing the same JsFiddle but the list of elements isn't showing up, only {{element.name}}
At first I thought this was an AngularJS syntax issue but I checked over the scope attribute, if the controller names match, etc.... I made sure to enable the AngularJS option in JsFiddle as well.
Does anyone know what the issue is or why this list isn't showing up?
You forget completing controller sytax '});' at the end of the code.
'use strict';
var chemistryApp = angular.module('chemistryApp', []);
chemistryApp.controller(
'chemistryController',
function chemistryController($scope) {
$scope.periodic = {elements: [
{
"atomicNumber": 1,
"name": "Hydrogen",
"atomicWeight": 1.00794,
"phase": "Gas",
"ionization": 13.5984,
"melting": -259.15,
"boiling": -252.87
},
{
"atomicNumber": 2,
"name": "Helium",
"atomicWeight": 4.002602,
"phase": "Gas",
"ionization": 24.5874,
"melting": 0,
"boiling": -268.93
},
{
"atomicNumber": 3,
"name": "Lithium",
"atomicWeight": 6.941,
"phase": "Solid",
"ionization": 5.3917,
"melting": 180.54,
"boiling": 1342
}
]
};
});
Working Fiddle
Related
I have a nav menu that is rendered using a navigation.ts json file for the menu items. When it gets to the navitem component it uses a ngIf to check if the item from the navigation file has a "function" key and if it does, the desired behavior is for it to use the string value from item.function in the object to fill the value for the (click) event.
In reality, the console throws an error saying "_co.item.function is not a function"
HTML
<span class="nav-link" *ngIf="item.function" (click)="item.function()" matRipple>
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{item.icon}}</mat-icon>
<span class="nav-link-title" [translate]="item.translate">{{item.title}}</span>
<span class="nav-link-badge" *ngIf="item.badge" [translate]="item.badge.translate"
[ngStyle]="{'background-color': item.badge.bg,'color': item.badge.fg}">
{{item.badge.title}}
</span>
</span>
Navigation.ts
[{
"id": "accounting",
"title": "Accounting",
"type": "collapse",
"children": [
{
"id" : "salesmenSalesLocation",
"title": "Salesmen Sales Location",
"type": "item",
"function": "handleSelect(ReportTypes.SalesmenSalesLocations)"
},
{
"id": "laggingLedgerEntries",
"title": "Lagging Ledger Entries",
"type": "item",
"function": "handleSelect(ReportTypes.LaggingLedgerEntries)"
}
]}]
I have also tried it as (click)="item.function" with no success.
I'm assuming you can change the data source here, because otherwise I don't see any good solution.
A string is not a function, and while you can turn it into one with eval that is a bad idea. What you should really do instead is just pass in a value that tells the function what to use.
Change your data to something like this:
{
"id" : "salesmenSalesLocation",
"title": "Salesmen Sales Location",
"type": "item",
"reportTypeSource": "SalesmenSalesLocations"
},
{
"id": "laggingLedgerEntries",
"title": "Lagging Ledger Entries",
"type": "item",
"reportTypeSource": "LaggingLedgerEntries"
}
Then pass that value to your function and use that to tell it where to look:
handleSelect (reportTypeSource: string) {
const reportType = ReportTypes[reportTypeSource]
// continue as before
}
And call it in your HTML like this:
(click)="handleSelect(item.reportTypeSource)"
Problem lies here:
"function": "handleSelect(ReportTypes.LaggingLedgerEntries)"
And here:
(click)="item.function()"
You cannot simply pass a string and expect the component to execute a function and also know exactly what to do. Here you need to pass the actual function.
Your setup looks over-config'd. I would tear the config down and put the logic into the component itself. Don't be afraid to have more template as well, if anything it makes things more legible (as opposed to the config)
Does that function exist in the component or just the model? If it is just on the model it won't work. (click) is looking for a method on the component. It is, ostensibly just a string in this instance.
I’ve been experimenting with this GitHub repo via a course on Lynda.com (https://github.com/planetoftheweb/learnangular) by Ray Villalobos -- it functions similarly to a basic web app that I’m hoping to build, but I’ve recently hit a bit of a road block.
In that repo linked above, in app/component.app.ts, is the following array:
var ARTISTS: Artist[] = [
{
"name": "Barot Bellingham",
"shortname": "Barot_Bellingham",
"reknown": "Royal Academy of Painting and Sculpture",
"bio": "Some bio here."
},
// etc...
]
This array is filtered by a pipe as seen in app/pipe.search.ts:
export class SearchPipe implements PipeTransform {
transform(pipeData, pipeModifier) {
return pipeData.filter((eachItem) => {
return eachItem['name'].toLowerCase().includes(pipeModifier.toLowerCase()) ||
eachItem['reknown'].toLowerCase().includes(pipeModifier.toLowerCase());
});
}
}
Here's the filter input:
<input class="search-input" [(ngModel)]="field1Filter" placeholder="type in search term here" (click)="showArtist(item); field1Filter=''">
And the code for the filter results:
<ul class="artistlist cf" *ngIf="field1Filter">
<li class="artistlist-item cf"
(click)="showArtist(item);"
*ngFor="let item of (artists | search: field1Filter)">
<artist-item class="content" [artist]=item></artist-item>
</li>
</ul>
<artist-details *ngIf="currentArtist" [artist]="currentArtist"></artist-details>
This all works perfectly, however, in my project, I would need to include three nested arrays, and have the ability to filter based upon the values in those arrays. A sample of the kind of array I need will look something like this:
var ARTISTS: Artist[] = [
{
"name": "Barot Bellingham",
"shortname": "Barot_Bellingham",
"reknown": "Royal Academy of Painting and Sculpture",
"bio": "Some bio here...",
"friends": [
"James",
"Harry",
"Bob",
"Liz",
"Kate",
"Jesse"
],
"emails": [
"bb#this.com",
"aa#this.com"
],
"car": [
"honda",
"scion",
"aston martin"
]
},
// etc...
]
Therefore, I hope to filter by “Harry,” and only display objects that contain “harry” in either “name,” “reknown,” “friends,” "emails," or "cars." Is this possible, and if so, how can I edit the pipe filter to do this? Thank you!!
(I'm pretty green at angular and JS in general, so I want to apologize in advance if I’ve used incorrect terminology or overlooked/misunderstood something basic.)
I deleted my prior answer because it was more confusing than helpful. I pasted example code without applying it to your variables/properties/objects and it was misleading. Let's try again:
export class SearchPipe implements PipeTransform {
transform(pipeData, pipeModifier) {
pipeModifier = pipeModifier ? pipeModifier.toLowerCase() : null;
return pipeModifier ? pipeData.filter(eachItem => {
eachItem['name'].toLowerCase().indexOf(pipeModifier) !== -1 ||
eachItem['reknown'].toLowerCase().indexOf(pipeModifier !== -1) : pipeData;
});
}
}
The first line of code in the transform method ensures that the modifier passed in is also lowercase so that the compare always compares lower case values. It also has a null check to ensure it does not try to lowercase it if it is null.
The second line of code also uses the "?" syntax to handle the case of a null pipeModifier.
I changed includes to indexOf. Includes checks arrays. Are these items, such as eachItem['name'], an array?
That should be closer.
NOTE: Without a provided plunker ... I did not check the syntax or correct execution of this code.
I'm trying to make a custom element for the 8Tracks API using Polymer, and am trying to get a user's mixes and display their covers. I'm attempting to do it as follows:
<link rel="import" href="../bower_components/polymer-jsonp/polymer-jsonp.html">
<polymer-element name="eight-tracks">
<template>
<polymer-jsonp id="ajax" auto url="https://8tracks.com/users/1/mixes.json?api_key=MY_API_KEY?api_version=3&callback=?"></polymer-jsonp>
<div class="instagram">
<template id="mixes" repeat="{{item in mixes}}" index="i">
<div id="{{item.index}}" class="item">
<img src="{{mixes.cover_urls.sq200}}">
</div>
</template>
</div>
</template>
<script>
Polymer('eight-tracks', {
ready: function () {
this.$.mixes.model = this.mixes;
this.$.ajax.addEventListener('polymer-response',
function (e) {
this.mixes = {mixes: e.detail.response.data};
this.$.mixes.model = {mixes: e.detail.response.data};
this.fire('eight-tracks-load', {response: e.detail.response.data});
}.bind(this)
);
}
});
</script>
</polymer-element>
I'm trying to model it on the x-instagram and x-flickr ones with little success. I'm very new to all of this, so any guidance would be appreciated. I know that the JSON/API call is working, because I get this back in the console:
{
"mix_set": {
"pagination": {
"current_page": 1,
"per_page": 12,
"offset_by": 0,
"next_page": 2,
"previous_page": null,
"total_entries": 31,
"total_pages": 3
},
"mixes": [
{
"id": 679109,
"path": "/mixes/679109",
"web_path": "/remi/chill-hip-hop-beats-part-2",
"name": "Chill Hip-Hop Beats (part 2)",
"user_id": 1,
"published": true,
"cover_urls": {
"sq56": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?fm=jpg&q=65&w=56&h=56&fit=crop",
"sq100": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?fm=jpg&q=65&w=100&h=100&fit=crop",
"sq133": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?fm=jpg&q=65&w=133&h=133&fit=crop",
"max133w": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?fm=jpg&q=65&w=133&fit=max",
"max200": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?fm=jpg&q=65&w=200&h=200&fit=max",
"sq250": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?fm=jpg&q=65&w=250&h=250&fit=crop",
"sq500": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?fm=jpg&q=65&w=500&h=500&fit=crop",
"max1024": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?fm=jpg&q=65&w=1024&h=1024&fit=max",
"original_imgix_url": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?q=65&sharp=15&vib=10&fm=jpg&fit=crop",
"original": "http://8tracks.imgix.net/i/001/067/826/68609.original-9904.jpg?q=65&sharp=15&vib=10&fm=jpg&fit=crop",
"animated": false
},
"description": "More beats to relax and lounge to\nSequel to http://8tracks.com/remi/laid-back-hip-hop-beats \n",
"plays_count": 1013,
"tag_list_cache": "hip-hip, lounge, beats, chill, smoke",
"first_published_at": "2012-03-26T19:11:15Z",
"first_published_at_timestamp": 1332789075,
"likes_count": 111,
"certification": "gold",
"duration": 2211,
"tracks_count": 8
},
...
}
This continues for all the mixes, but you get the idea.
I know that I'm very likely to have screwed the tag up, but I'm only just starting out with APIs/Web Components, so any help would be brilliant.
One of the key features of Polymer is its data-binding and emphasis on declarative solutions to problems. In your case, you can instruct the polymer-jsonp element to place the response in a variable for you then wire it declaratively into your template.
<polymer-jsonp response="{{response}}" ...></polymer-jsonp>
<template repeat="{{item, index in response.mix_set.mixes}}">...</template>
You can then use Polymer's declarative event mapping to wire up your response handler (to fire the eight-tracks-loaded event for any external listeners that may want to know).
<polymer-jsonp on-polymer-response="{{onResponse}}" ...></polymer-jsonp>
<script>
Polymer('eight-tracks', {
...
onResponse: function(e) {
this.fire('eight-tracks-load', {response: e.detail.response});
}
});
</script>
Here's a working jsbin with a hard-coded response to show that it works, and here's one without the hard-coded response that should work for you if you plug in your API key (although you probably don't want that on jsbin :-)
Here is the basic setup, which has a default noemployee.html partial: as the ng-view
Index.html content:
<div id="container" ng-controller="EmployeeCtrl">
<!-- Side Menu -->
<span id="smenuSpan">
<ul id="thumbList">
<li ng-repeat="employee in employees | filter:categories">
<img class="smallImage" ng-src="content/app/images/{{employee.image}}" alt="{{employee.description}}">
</li>
</ul>
</span>
<!-- Content -->
<span id="contentSpan">
<div ng-view></div>
</span>
</div>
My Route Provider:
var EmployeeModule = angular.module('EmployeeModule', [], function($routeProvider, $locationProvider) {
$routeProvider.when('/', { templateUrl: 'content/app/partials/noemployee.html', controller: EmployeeModule.EmployeeCtrl });
$routeProvider.when('Employee/:id', { templateUrl: 'content/app/partials/employee.html', controller: EmployeeModule.EmployeeCtrl });
$routeProvider.otherwise({ redirectTo: '/' });
$locationProvider.html5Mode(true);
My Controller:
function EmployeeCtrl($scope, $http, $routeParams, $timeout) {
$scope.employees = [
{ "id": 1, "category": "ones", "image": "person1.jpg", "description": "person 1 description", name:"Jane Smith" },
{ "id": 2, "category": "twos", "image": "person2.jpg", "description": "person 2 description", name: "Mark Sharp" },
{ "id": 3, "category": "threes", "image": "person3.jpg", "description": "person 3 description", name: "Kenny Suave" },
{ "id": 4, "category": "fours", "image": "person4.jpg", "description": "person 4 description", name: "Betty Charmer" },
{ "id": 5, "category": "fives", "image": "person5.jpg", "description": "person 5 description", name: "John Boss" }
];
$scope.employeesCategories = [];
$scope.currentEmployee = {};
$scope.params = $routeParams;
$scope.handleEmployeesLoaded = function (data, status) {
//$scope.images = data;
// Set the current image to the first image in images
$scope.currentEmployee = _.first($scope.employees);
// Create a unique array based on the category property in the images objects
$scope.employeeCategories = _.uniq(_.pluck($scope.employees, 'category'));
}
$scope.fetch = function () {
$http.get($scope.url).success($scope.handleEmployeesLoaded);
};
$scope.setCurrentEmployee = function (employee) {
$scope.currentEmployee = employee;
};
// Defer fetch for 1 second to give everything an opportunity layout
$timeout($scope.fetch, 1000);
}
Observations:
At present, if I click on any employee, no 'Employee/??' is added to the address bar path [ which isn't a crime to me], however, the main content div does not change the partial to the employee.html.
If I comment out "$locationProvider.html5Mode(true);", the default localhost is now "http://localhost:31219/#/" and when I click on any employee the address bar shows 'http://localhost:31219/Employee/1', and the page is navigated away to a 404 error page.
I know I am bastardizing something here that the solution is so simple it escapes me.
Goals:
I really would like to avoid hash tags in my address bar.
It would be nice but no req that the employee/id not show up in the address bar but I suspect the partial cannot change w/o it. and, naturally
I want the partial to change to the 'employee.html" page when an employee is clicked.
Does anyone see where I am going wrong with this code?
Thanks in Advance!
Solution:
I needed to put '#/' in the img href --> href="#/Employee/{{employee.id}}"
Comment out
'$locationProvider.html5Mode(true);'
As a side note, I sure wish I knew how to get this to work w/o those pesky hash tags. Any ideas anyone?
In order to use html5mode, your server has to serve up the main app index file for otherwise invalid routes.
So, for example, if your server side code handles CRUD operations on paths like: /api/employees, /api/employees/:id, etc...
and it serves up static content, images, html, css, js, etc.
For any other request, that would otherwise be a 404, it should, instead of responding with a 404, respond with a 200 code, and serve up the index.html file.
This way any non static and non server side route gets handled by the angular app.
This is mentioned in the Angular guide on this page: http://docs.angularjs.org/guide/dev_guide.services.$location
Note the 'server side' comment at the end:
Html link rewriting
When you use HTML5 history API mode, you will need
different links in different browsers, but all you have to do is
specify regular URL links, such as: link
When a user clicks on this link:
In a legacy browser, the URL changes to /index.html#!/some?foo=bar
In a modern browser, the URL changes to /some?foo=bar In cases like the
following, links are not rewritten; instead, the browser will perform
a full page reload to the original link.
Links that contain target element
Example: link
Absolute links that go to a different domain
Example: link
Links starting with '/' that lead to a different base path when base is defined
Example: link
Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite
all your links to entry point of your application (e.g. index.html)
This was the problem:
<img class="smallImage" ng-src="content/app/images/{{employee.image}}" alt="{{employee.description}}">
Solution:
I needed to put '#/' in the img href --> href="#/Employee/{{employee.id}}"
Comment out '$locationProvider.html5Mode(true);'
As a side note, I sure wish I knew how to get this to work w/o those pesky hash tags. Any ideas anyone?
I'm using backbone and handlebars for templating and i'm new to this.
My current json is in the below format and the code works fine.
[
{
"id": "10",
"info": {
"name": "data10"
}
},
{
"id": "11",
"info": {
"name": "data11"
}
}
]
But when i change my json structure to something like shown below i'm having difficulty in getting things to be populated.
{
"total_count": "10",
"dataElements": [
{
"id": "10",
"info": {
"name": "data10"
}
},
{
"id": "11",
"info": {
"name": "data11"
}
}
]
}
How can i populate name, info and total_count keeping the current code structure ?
JSFiddle : http://jsfiddle.net/KTj2K/1/
Any help really appriciated.
A few things that you need to do in order for this to work.
Replace Backbone's core 'reset' on your collection with a custom one that understands the data you are passing to it. For example:
reset: function (data) {
this.totalCount = data.total_count;
Backbone.Collection.prototype.reset.call(this, data.dataElements);
}
Now when you reset your collection, it will pull the total_count out of the object you are resetting it with, and use Backbone's core reset with the dataElement array. Keep in mind you may have to do a similar thing with 'parse' if you're intending on pulling this from the server.
I'd recommend that (if your example looks anything like the real code you're working with) you reset your collection before getting to rendering.
var dataCollectionList = new dataCollection();
dataCollectionList.reset(jsonData);
var App = new AppView({model : dataCollectionList});
Now in your view's "render" method you can grab the 'totalCount' property off the collection -
render : function() {
//Should spit the total count into the element, just as an example
this.$el.append(this.model.totalCount);
//or console.log it
console.log(this.model.totalCount);
return this;
}
Voila. Side note - as someone who works with Backbone a lot, it drives me nuts when people set an attribute of something like "model" (i.e. peopleModel, itemModel, etc) and it ends up being a backbone collection. It's much clearer to name it after what it is - though some MVC purists may disagree a bit.
Also, in this code block:
_.each(this.model.models, function (myData) {
$(this.el).append(new ItemView({model:myData}).render().el);
}, this);
You don't need to do _.each(this.model.models.......). Since you're working with a collection, the collection has a built in 'each' method.
this.model.each(function (myData) { ..... } , this);
Quite a bit cleaner.