-
-
Save auser/7210781 to your computer and use it in GitHub Desktop.
| angular.module('myApp', ['ngRoute']) | |
| .provider('Weather', function() { | |
| var apiKey = ""; | |
| this.getUrl = function(type, ext) { | |
| return "http://api.wunderground.com/api/" + | |
| this.apiKey + "/" + type + "/q/" + | |
| ext + '.json'; | |
| }; | |
| this.setApiKey = function(key) { | |
| if (key) this.apiKey = key; | |
| }; | |
| this.$get = function($q, $http) { | |
| var self = this; | |
| return { | |
| getWeatherForecast: function(city) { | |
| var d = $q.defer(); | |
| $http({ | |
| method: 'GET', | |
| url: self.getUrl("forecast", city), | |
| cache: true | |
| }).success(function(data) { | |
| d.resolve(data.forecast.simpleforecast); | |
| }).error(function(err) { | |
| d.reject(err); | |
| }); | |
| return d.promise; | |
| }, | |
| getCityDetails: function(query) { | |
| var d = $q.defer(); | |
| $http({ | |
| method: 'GET', | |
| url: "http://autocomplete.wunderground.com/aq?query=" + | |
| query | |
| }).success(function(data) { | |
| d.resolve(data.RESULTS); | |
| }).error(function(err) { | |
| d.reject(err); | |
| }); | |
| return d.promise; | |
| } | |
| } | |
| } | |
| }) | |
| .factory('UserService', function() { | |
| var defaults = { | |
| location: 'autoip' | |
| }; | |
| var service = { | |
| user: {}, | |
| save: function() { | |
| sessionStorage.presently = | |
| angular.toJson(service.user); | |
| }, | |
| restore: function() { | |
| service.user = | |
| angular.fromJson(sessionStorage.presently) || defaults | |
| return service.user; | |
| } | |
| }; | |
| service.restore(); | |
| return service; | |
| }) | |
| .config(function(WeatherProvider) { | |
| WeatherProvider.setApiKey('REPLACE_WITH_YOUR_KEY'); | |
| }) | |
| .config(['$routeProvider', function($routeProvider) { | |
| $routeProvider | |
| .when('/', { | |
| templateUrl: 'templates/home.html', | |
| controller: 'MainCtrl' | |
| }) | |
| .when('/settings', { | |
| templateUrl: 'templates/settings.html', | |
| controller: 'SettingsCtrl' | |
| }) | |
| .otherwise({redirectTo: '/'}); | |
| }]) | |
| .directive('autoFill', function($timeout, Weather) { | |
| return { | |
| restrict: 'EA', | |
| scope: { | |
| autoFill: '&', | |
| ngModel: '=', | |
| timezone: '=' | |
| }, | |
| compile: function(tEle, tAttrs) { | |
| var tplEl = angular.element('<div class="typeahead">' + | |
| '<input type="text" autocomplete="off" />' + | |
| '<ul id="autolist" ng-show="reslist">' + | |
| '<li ng-repeat="res in reslist" ' + | |
| '>{{res.name}}</li>' + | |
| '</ul>' + | |
| '</div>'); | |
| var input = tplEl.find('input'); | |
| input.attr('type', tAttrs.type); | |
| input.attr('ng-model', tAttrs.ngModel); | |
| input.attr('timezone', tAttrs.timezone); | |
| tEle.replaceWith(tplEl); | |
| return function(scope, ele, attrs, ctrl) { | |
| var minKeyCount = attrs.minKeyCount || 3, | |
| timer; | |
| ele.bind('keyup', function(e) { | |
| val = ele.val(); | |
| if (val.length < minKeyCount) { | |
| if (timer) $timeout.cancel(timer); | |
| scope.reslist = null; | |
| return; | |
| } else { | |
| if (timer) $timeout.cancel(timer); | |
| timer = $timeout(function() { | |
| scope.autoFill()(val) | |
| .then(function(data) { | |
| if (data && data.length > 0) { | |
| scope.reslist = data; | |
| scope.ngModel = data[0].zmw; | |
| scope.timezone = data[0].tz; | |
| } | |
| }); | |
| }, 300); | |
| } | |
| }); | |
| // Hide the reslist on blur | |
| input.bind('blur', function(e) { | |
| scope.reslist = null; | |
| scope.$digest(); | |
| }); | |
| } | |
| } | |
| } | |
| }) | |
| .controller('MainCtrl', | |
| function($scope, $timeout, Weather, UserService) { | |
| $scope.date = {}; | |
| var updateTime = function() { | |
| $scope.date.tz = new Date(new Date() | |
| .toLocaleString("en-US", {timeZone: $scope.user.timezone})); | |
| $timeout(updateTime, 1000); | |
| } | |
| $scope.weather = {} | |
| $scope.user = UserService.user; | |
| Weather.getWeatherForecast($scope.user.location) | |
| .then(function(data) { | |
| $scope.weather.forecast = data; | |
| }); | |
| updateTime(); | |
| }) | |
| .controller('SettingsCtrl', | |
| function($scope, $location, Weather, UserService) { | |
| $scope.user = UserService.user; | |
| $scope.save = function() { | |
| UserService.save(); | |
| $location.path('/'); | |
| } | |
| $scope.fetchCities = Weather.getCityDetails; | |
| }); |
| <div> | |
| <div id="datetime"> | |
| <h1>{{ date.tz | date:'hh mm ss' }}</h1> | |
| <h2>{{ date.tz | date:'EEEE, MMMM yyyy' }}</h2> | |
| </div> | |
| <div id="forecast"> | |
| <ul class="row list-unstyled"> | |
| <li ng-repeat="day in weather.forecast.forecastday" class="col-md-3"> | |
| <span ng-class="{today: $index == 0}"> | |
| <img class="{{ day.icon }}" ng-src="{{ day.icon_url }}" /> | |
| <h3>{{ day.high.fahrenheit }}</h3> | |
| <h4 ng-if="$index == 0">Now</h4> | |
| <h4 ng-if="$index != 0">{{ day.date.weekday }}</h4> | |
| </span> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> |
| /* Include this file in your html if you are using the CSP mode. */ | |
| @charset "UTF-8"; | |
| [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], | |
| .ng-cloak, .x-ng-cloak, | |
| .ng-hide { | |
| display: none !important; | |
| } | |
| ng\:form { | |
| display: block; | |
| } | |
| body { | |
| background-color: #eee; | |
| font-family: 'Actor', sans-serif; | |
| color: #444; | |
| } | |
| #datetime { | |
| text-align: center; | |
| } | |
| #datetime h1 { | |
| font-size: 6.1em; | |
| } | |
| #datetime h2 { | |
| font-size: 1.0em; | |
| } | |
| @font-face { | |
| font-family: 'Actor'; | |
| src: url('chrome-extension://__MSG_@@extension_id__/fonts/DroidSansMono.ttf'); | |
| } | |
| #forecast ul li { | |
| font-size: 4.5em; | |
| text-align: center; | |
| } | |
| #forecast ul li img { | |
| } | |
| #forecast ul li h3 { | |
| font-size: 1.5em; | |
| } | |
| #actionbar { | |
| position: absolute; | |
| bottom: 0.5em; | |
| right: 1.0em; | |
| } | |
| #actionbar a { | |
| font-size: 2.2rem; | |
| color: #000; | |
| } | |
| .typeahead ul { | |
| max-height: 150px; | |
| border: 1px solid #333; | |
| padding: 3px; | |
| overflow: auto; | |
| } |
| { | |
| "manifest_version": 2, | |
| "name": "Presently", | |
| "description": "A currently clone", | |
| "version": "0.1", | |
| "permissions": [ | |
| "http://api.wunderground.com/api/" | |
| ], | |
| "background": { | |
| "scripts": ["js/vendor/angular.min.js"] | |
| }, | |
| "content_security_policy": "script-src 'self'; object-src 'self'", | |
| "chrome_url_overrides" : { | |
| "newtab": "tab.html" | |
| } | |
| } |
| <h2>Settings</h2> | |
| <form ng-submit="save()"> | |
| <input type="text" | |
| ng-model="user.location" | |
| timezone="user.timezone" | |
| auto-fill="fetchCities" | |
| autocomplete="off" | |
| placeholder="Location" /> | |
| <input class="btn btn-primary" | |
| type="submit" value="Save" /> | |
| </form> |
| <!doctype html> | |
| <html data-ng-app="myApp" data-ng-csp=""> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Presently</title> | |
| <link rel="stylesheet" href="css/bootstrap.min.css"> | |
| <link rel="stylesheet" href="css/main.css"> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div data-ng-view></div> | |
| <div id="actionbar"> | |
| <ul class="list-inline"> | |
| <li><a class="glyphicon glyphicon-home" href="#/"></a></li> | |
| <li><a class="glyphicon glyphicon-list" href="#/settings"></a></li> | |
| </ul> | |
| </div> | |
| </div> | |
| <script src="./js/vendor/angular.min.js"></script> | |
| <script src="./js/vendor/angular-route.min.js"></script> | |
| <script src="./js/app.js"></script> | |
| </body> | |
| </html> |
How to solve the autocomplete problem ?
How in the tutorial we are getting different locations instead I am getting some number as in the above issue.
This tutorial has been broken since the first time I did it. Autocomplete does not work at ALL. The list of locations stays hidden and there's no way to select the city. Same issue as @elvispro and @ponty7. A shame. Completely misleading and disappointing for people that are starting to learn Angular. Is there any way to fix it?
@mehrdadrafiee and @bhansa, to get the city in the input field change:
scope.ngModel = data[0].zmw;
for
scope.ngModel = data[0].name;
Let's try to figure this together knowing that the author does not care.
I was banging my head against the wall for the drop down. I eventually learned the problem is the directive compile function looks at he parent scope not the isolated scope that we defined by doing scope: {...}
The way to fix this is to replace scope.reslist with scope.$parent.reslist everywhere in the function
autocomplete doesn't work properly. When the input gets 3 characters, it replaces them with some random number (which seems that are not latitude or longitude either!).
Here is the input for "los" (Los Angeles).