angularjs 1 ngModel two $watchs - javascript

I'm using angular google places directive plugin. when user selects the address then it shows on $scope.watch, but I want to watch every key hit, so I can clear other values, if user start changing any value on the input box.
Is there a way to watch for any key or value changes?

It seems like the issue in your case is that the Google Places directive does not update the variable assigned to ng-model ($scope.place) until a selection is made from the dropdown menu. This makes sense - the point is for the user to decide which place is the one they are looking for, out of several results.
Check out the source to see where this happens:
https://github.com/kuhnza/angular-google-places-autocomplete/blob/0.2.7/src/autocomplete.js#L70
https://github.com/kuhnza/angular-google-places-autocomplete/blob/0.2.7/src/autocomplete.js#L166
I'm including a code snippet of the plugin in case it will help anyone else with future debugging. You'll need to add your Maps API key to make it work.
"use strict";
angular.module('example', ['google.places'])
// Setup a basic controller with a scope variable 'place'
.controller('MainCtrl', function ($scope) {
$scope.place = null;
$scope.updatePlace = function() {
console.log($scope.place);
};
});
angular.module("google.places",[]).factory("googlePlacesApi",["$window",function(a){if(!a.google)throw"Global `google` var missing. Did you forget to include the places API script?";return a.google}]).directive("gPlacesAutocomplete",["$parse","$compile","$timeout","$document","googlePlacesApi",function(a,b,c,d,e){return{restrict:"A",require:"^ngModel",scope:{model:"=ngModel",options:"=?",forceSelection:"=?",customPlaces:"=?"},controller:["$scope",function(a){}],link:function(a,f,g,h){function i(){f.bind("keydown",l),f.bind("blur",m),f.bind("submit",m),a.$watch("selected",n)}function j(){var c,e=angular.element("<div g-places-autocomplete-drawer></div>"),f=angular.element(d[0].body);e.attr({input:"input",query:"query",predictions:"predictions",active:"active",selected:"selected"}),c=b(e)(a),f.append(c)}function k(){h.$parsers.push(o),h.$formatters.push(p),h.$render=q}function l(b){0!==a.predictions.length&&-1!==w(A,b.which)&&(b.preventDefault(),b.which===z.down?(a.active=(a.active+1)%a.predictions.length,a.$digest()):b.which===z.up?(a.active=(a.active?a.active:a.predictions.length)-1,a.$digest()):13===b.which||9===b.which?(a.forceSelection&&(a.active=-1===a.active?0:a.active),a.$apply(function(){a.selected=a.active,-1===a.selected&&r()})):27===b.which&&a.$apply(function(){b.stopPropagation(),r()}))}function m(b){0!==a.predictions.length&&(a.forceSelection&&(a.selected=-1===a.selected?0:a.selected),a.$digest(),a.$apply(function(){-1===a.selected&&r()}))}function n(){var b;b=a.predictions[a.selected],b&&(b.is_custom?a.$apply(function(){a.model=b.place,a.$emit("g-places-autocomplete:select",b.place),c(function(){h.$viewChangeListeners.forEach(function(a){a()})})}):C.getDetails({placeId:b.place_id},function(b,d){d==e.maps.places.PlacesServiceStatus.OK&&a.$apply(function(){a.model=b,a.$emit("g-places-autocomplete:select",b),c(function(){h.$viewChangeListeners.forEach(function(a){a()})})})}),r())}function o(b){var c;return b&&u(b)?(a.query=b,c=angular.extend({input:b},a.options),B.getPlacePredictions(c,function(b,c){a.$apply(function(){var d;r(),a.customPlaces&&(d=s(a.query),a.predictions.push.apply(a.predictions,d)),c==e.maps.places.PlacesServiceStatus.OK&&a.predictions.push.apply(a.predictions,b),a.predictions.length>5&&(a.predictions.length=5)})}),a.forceSelection?h.$modelValue:b):b}function p(a){var b="";return u(a)?b=a:v(a)&&(b=a.formatted_address),b}function q(){return f.val(h.$viewValue)}function r(){a.active=-1,a.selected=-1,a.predictions=[]}function s(b){var c,d,e,f=[];for(e=0;e<a.customPlaces.length;e++)c=a.customPlaces[e],d=t(b,c),d.matched_substrings.length>0&&f.push({is_custom:!0,custom_prediction_label:c.custom_prediction_label||"(Custom Non-Google Result)",description:c.formatted_address,place:c,matched_substrings:d.matched_substrings,terms:d.terms});return f}function t(a,b){var c,d,e,f=a+"",g=[],h=[];for(d=b.formatted_address.split(","),e=0;e<d.length;e++)c=d[e].trim(),f.length>0&&(c.length>=f.length?(x(c,f)&&h.push({length:f.length,offset:e}),f=""):x(f,c)?(h.push({length:c.length,offset:e}),f=f.replace(c,"").trim()):f=""),g.push({value:c,offset:b.formatted_address.indexOf(c)});return{matched_substrings:h,terms:g}}function u(a){return"[object String]"==Object.prototype.toString.call(a)}function v(a){return"[object Object]"==Object.prototype.toString.call(a)}function w(a,b){var c,d;if(null==a)return-1;for(d=a.length,c=0;d>c;c++)if(a[c]===b)return c;return-1}function x(a,b){return 0===y(a).lastIndexOf(y(b),0)}function y(a){return null==a?"":a.toLowerCase()}var z={tab:9,enter:13,esc:27,up:38,down:40},A=[z.tab,z.enter,z.esc,z.up,z.down],B=new e.maps.places.AutocompleteService,C=new e.maps.places.PlacesService(f[0]);!function(){a.query="",a.predictions=[],a.input=f,a.options=a.options||{},j(),i(),k()}()}}}]).directive("gPlacesAutocompleteDrawer",["$window","$document",function(a,b){var c=['<div class="pac-container" ng-if="isOpen()" ng-style="{top: position.top+\'px\', left: position.left+\'px\', width: position.width+\'px\'}" style="display: block;" role="listbox" aria-hidden="{{!isOpen()}}">',' <div class="pac-item" g-places-autocomplete-prediction index="$index" prediction="prediction" query="query"',' ng-repeat="prediction in predictions track by $index" ng-class="{\'pac-item-selected\': isActive($index) }"',' ng-mouseenter="selectActive($index)" ng-click="selectPrediction($index)" role="option" id="{{prediction.id}}">'," </div>","</div>"];return{restrict:"A",scope:{input:"=",query:"=",predictions:"=",active:"=",selected:"="},template:c.join(""),link:function(c,d){function e(c){var d=c[0],e=d.getBoundingClientRect(),f=b[0].documentElement,g=b[0].body,h=a.pageYOffset||f.scrollTop||g.scrollTop,i=a.pageXOffset||f.scrollLeft||g.scrollLeft;return{width:e.width,height:e.height,top:e.top+e.height+h,left:e.left+i}}d.bind("mousedown",function(a){a.preventDefault()}),a.onresize=function(){c.$apply(function(){c.position=e(c.input)})},c.isOpen=function(){return c.predictions.length>0},c.isActive=function(a){return c.active===a},c.selectActive=function(a){c.active=a},c.selectPrediction=function(a){c.selected=a},c.$watch("predictions",function(){c.position=e(c.input)},!0)}}}]).directive("gPlacesAutocompletePrediction",[function(){var a=['<span class="pac-icon pac-icon-marker"></span>','<span class="pac-item-query" ng-bind-html="prediction | highlightMatched"></span>','<span ng-repeat="term in prediction.terms | unmatchedTermsOnly:prediction">{{term.value | trailingComma:!$last}} </span>','<span class="custom-prediction-label" ng-if="prediction.is_custom"> {{prediction.custom_prediction_label}}</span>'];return{restrict:"A",scope:{index:"=",prediction:"=",query:"="},template:a.join("")}}]).filter("highlightMatched",["$sce",function(a){return function(b){var c,d="",e="";return b.matched_substrings.length>0&&b.terms.length>0&&(c=b.matched_substrings[0],d=b.terms[0].value.substr(c.offset,c.length),e=b.terms[0].value.substr(c.offset+c.length)),a.trustAsHtml('<span class="pac-matched">'+d+"</span>"+e)}}]).filter("unmatchedTermsOnly",[function(){return function(a,b){var c,d,e=[];for(c=0;c<a.length;c++)d=a[c],b.matched_substrings.length>0&&d.offset>b.matched_substrings[0].length&&e.push(d);return e}}]).filter("trailingComma",[function(){return function(a,b){return b?a+",":a}}]);
.pac-container{background-color:#fff;position:absolute!important;z-index:1000;border-radius:2px;border-top:1px solid #d9d9d9;font-family:Arial,sans-serif;box-shadow:0 2px 6px rgba(0,0,0,.3);-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden}.pac-container:after{content:"";padding:1px 1px 1px 0;height:16px;text-align:right;display:block;background-image:url(//maps.gstatic.com/mapfiles/api-3/images/powered-by-google-on-white2.png);background-position:right;background-repeat:no-repeat;background-size:104px 16px}.hdpi.pac-container:after{background-image:url(//maps.gstatic.com/mapfiles/api-3/images/powered-by-google-on-white2_hdpi.png)}.pac-item{cursor:default;padding:0 4px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;line-height:30px;text-align:left;border-top:1px solid #e6e6e6;font-size:11px;color:#999}.pac-item:hover{background-color:#fafafa}.pac-item-selected,.pac-item-selected:hover{background-color:#ebf2fe}.pac-matched{font-weight:700}.pac-item-query{font-size:13px;padding-right:3px;color:#000}.pac-icon{width:15px;height:20px;margin-right:7px;margin-top:6px;display:inline-block;vertical-align:top;background-image:url(//maps.gstatic.com/mapfiles/api-3/images/autocomplete-icons.png);background-size:34px}.hdpi .pac-icon{background-image:url(//maps.gstatic.com/mapfiles/api-3/images/autocomplete-icons_hdpi.png)}.pac-icon-search{background-position:-1px -1px}.pac-item-selected .pac-icon-search{background-position:-18px -1px}.pac-icon-marker{background-position:-1px -161px}.pac-item-selected .pac-icon-marker{background-position:-18px -161px}.pac-placeholder{color:gray}.custom-prediction-label{font-style:italic}
<head>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<!-- Required dependencies -->
<script src="https://maps.googleapis.com/maps/api/js?key=INSERT_YOUR_GMAPS_API_HERE&libraries=places"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="example" ng-controller="MainCtrl">
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Basic Usage</h1>
<h5>Result:</h5>
<pre>{{place | json}}</pre>
<form class="form">
<input class="form-control" g-places-autocomplete ng-model="place" ng-change="updatePlace()"/>
</form>
</div>
</div>
</div>

Related

Angular Js Show the Checkbox's Checked when Page Loads

Angular Js Show the Checkbox's Checked when Page Loads.
I am saving the Checkboxes. when I Reload the Page I want to show the selected checkboxes
<md-checkbox ng-repeat="primaryPrograms in ctrl.primaryProgramStudies" ng-model="ctrl.primaryProgramStudiesSelected[primaryPrograms.id]" ng-checked="primaryPrograms.selected==true">
{{primaryPrograms.name}}
</md-checkbox>
Script :
ctrl.primaryProgramStudiesSelected =
[{"id":1,"name":"SAT","selected":false},{"id":2,"name":"ACT","selected":true},{"id":3,"name":"PSAT","selected":false},{"id":4,"name":"ISEE\/SSAT","selected":false},{"id":5,"name":"AP","selected":true},{"id":6,"name":"General GPA Management","selected":true},{"id":7,"name":"Reading","selected":false},{"id":8,"name":"Math","selected":false},{"id":9,"name":"Science","selected":false},{"id":10,"name":"Social Studies","selected":false},{"id":11,"name":"ESL","selected":true},{"id":12,"name":"College Admissions","selected":true},{"id":13,"name":"TOEFL ","selected":false}]]`
ngChecked directive should not be used together with ngModel, as this can lead to unexpected behavior.
https://docs.angularjs.org/api/ng/directive/ngChecked
angular.module('MyApp',['ngMaterial', 'ngMessages'])
.controller('AppCtrl', function($scope) {
$scope.items = [{"id":1,"name":"SAT","selected":false},{"id":2,"name":"ACT","selected":true},{"id":3,"name":"PSAT","selected":false},{"id":4,"name":"ISEE\/SSAT","selected":false},{"id":5,"name":"AP","selected":true},{"id":6,"name":"General GPA Management","selected":true},{"id":7,"name":"Reading","selected":false},{"id":8,"name":"Math","selected":false},{"id":9,"name":"Science","selected":false},{"id":10,"name":"Social Studies","selected":false},{"id":11,"name":"ESL","selected":true},{"id":12,"name":"College Admissions","selected":true},{"id":13,"name":"TOEFL ","selected":false}];
});
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-route.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-sanitize.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="AppCtrl" class="md-padding demo checkboxdemoSelectAll">
<div layout="row" layout-wrap="">
<div flex="100" layout="column">
<div>
<fieldset class="demo-fieldset">
<legend class="demo-legend">Select </legend>
<div layout="row" layout-wrap="" flex="">
<div class="demo-select-all-checkboxes" flex="100">
<md-checkbox ng-repeat="item in items" ng-model="item.selected">
{{ item }}
</md-checkbox>
</div>
</div>
</fieldset>
</div>
</div>
</div></div>
</div>
While reload the app, Everything reload & the code is render freshly. And whatever changes made in client side(without server side), everything gone.
So as per #Pengyy comment you have to store the selected items to server / cookies / localstorage. Then when reload pages just read the stored data and reset check boxes.
Change ng-model="ctrl.primaryProgramStudiesSelected[primaryPrograms.id]" to ng-model="ctrl.primaryProgramStudiesSelected[primaryPrograms.selected]".
Then add selected property to every object in your primaryProgramStudiesSelected array. If you want it to be selected by default, make the selected property true.
Can you try something like this:
<div ng-repeat="primaryPrograms in ctrl.primaryProgramStudies">
<div class="checkbox">
<label>
<input ng-model="primaryPrograms.selected" ng-true-value="true" ng-false-value="false" type="checkbox" ng-checked="primaryPrograms.selected === true"> Checked
</label>
</div>
</div>
Note: if you are using mysql your true/false fields are probably tiny int, and you should use ng-true-value="1" ng-false-value="0".
Also note that in controller you already should set selected to true, im not sure how you get your data and how you know if field is selected. But this example should work.

AngularJS advice on how to fix a bug when pushing two of the same strings into the array

I'm having trouble trying to figure out how to fix a bug that happens when I try to push the same string as added previously to the array. It gets stuck and will not allow the app to post another string.
How do I make sure that your repeat doesn't get stuck when there two of the same values in the array?
Bug Example Screenshot
--> Bug happens when I try to push "1"into the array again after a "1" is already posted.
HTML Code
<body>
<div data-ng-controller="appController" class="container">
<div class="row">
<form>
<label for = "status"> Status: </label>
<input data-ng-model = "input_data" type="text" id="status"/>
<button data-ng-click="add_data()"> OK </button>
<ul class = "list-group">
<li class="list-group-item" data-ng-repeat="x in array_data">
{{x}}
<button data-ng-click = "remove_data($index)">DEL</button>
</li>
</ul>
</form>
</div>
</div>
<script src="framework/js/jquery.min.js"></script>
<!-- All Bootstrap plug-ins file -->
<script src="framework/js/bootstrap.min.js"></script>
<!-- Basic AngularJS -->
<script src="framework/js/angular.min.js"></script>
<!-- Your Controller -->
<script src="framework/js/appstatpost.js"></script>
</body>
AngularJS code
var app = angular.module("myApp", []);
app.controller("appController", function ($scope) {
$scope.array_data = [];
$scope.add_data = function () {
$scope.array_data.push($scope.input_data);
};
$scope.remove_data = function (index) {
$scope.array_data.splice(index, 1);
};
});
You could use track by $index, example:
<li class="list-group-item" data-ng-repeat="x in array_data track by $index">
AngularJS tries by default to find a key in your array to index by. Normally this works well, but if you have duplicates then you have to tell AngularJS to make a new index, in this case, $index.

Angular JS view hasn't been updated properly

I have found an issue in AngularJS which relates to wrong update of view. It occurs from time to time. The problem is when model gets a new value, view is not updated by new model value, but old value is appended by new model value.
While troubleshooting I checked that model contains a correct value.
Here is a view.
<div class="container">
<div ng-repeat="p in point" id="{{'point-' + p.Id}}" class="{{p.BackgroundClass}}">
<div class="point-number">{{p.Id}}</div>
<div class="{{p.ImageClass}}"></div>
<div class="point-amount">{{p.Amount}}</div>
<div class="point-quantity">{{p.Quantity}}</div>
</div>
</div>
Controller code which contains SignalR events processing:
wetApiHubProxy.on('updatePointState', function (pointId, backgroundClassProp, imageClassProp) {
pointsService.getPointById(pointId).then(function (point) {
point.BackgroundClass = backgroundClassProp;
console.log('imageClassProp ' + point.ImageClass);
point.ImageClass = imageClassProp;
});
});
p.ImageClass is changing quite often. Changes/updates of view work in a correct way until sometimes occurs concatenation of old and new value.
Old p.ImageClass value is "point-state-configure".
New p.ImageClass value is "pump-state-off".
As a wrong result I have, where ImageClass contains concatenated values:
<div ng-repeat="p in points" id="point-4" class="point point-off" role="button" tabindex="0" style="">
<div class="point-number ng-binding">4</div>
<div class="point-state-configure pump-state-off" style=""></div>
<div class="point-amount ng-binding">926.93</div>
<div class="point-quantity ng-binding">417.35 L</div>
</div>
I have tried to call $scope.$apply() and $evalAsync, but that was hopeless. The strangest thing that issue occurs spontaneously. The only constant condition it's when $rootscope contains bigger amount of child scopes. Can anyone tell what place to dig and how to get rid of this problem?
class attribute is not intended to be used this way. You should use the ng-class directive instead.
I've created an example for you: https://jsfiddle.net/coldcue/o7q6gfs4/
JavaScript
angular.module('testApp', [])
.controller("TestController", function($scope) {
// Initialize the value
$scope.state = "state-blue";
// Change class on click
$scope.click = function() {
$scope.state = ($scope.state === "state-blue") ? "state-red" : "state-blue";
}
});
HTML
<div ng-controller="TestController">
<div ng-class="state">
Some label
</div>
<input type="button" ng-click="click()" value="Click me">
</div>
But there are many more ways to use ng-class, read more here: https://docs.angularjs.org/api/ng/directive/ngClass

Combining results from WP-API using AngularJS

I currently have this site - http://dev.5874.co.uk/scd-data/ where I have a dropdown which displays results from WP-API which I am pulling in through AngularJS.
It currently combines the two sets of results as they're separate URL's, the results are in categories within a custom post type so if both posts are 'tagged' in the same category chosen they display twice. I need a way to combine the two sets of results but only showing one of the posts - I hope this makes sense. I'm very new to API data and AngularJS and I imagine there is a much simpler way of doing this. Any help would be much appreciated. Here is a snippet of my code to show how it's currently working.
Thanks in advance!
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
<style>
.desc {display: none;}
</style>
<script type="text/javascript">
$(function(){
$('.selectOption').change(function(){
var selected = $(this).find(':selected').text();
//alert(selected);
$(".desc").hide();
$('#' + selected).show();
}).change()
});
</script>
<script>
var app = angular.module('myApp', []);
app.controller('northWestCtrl', function($scope, $http) {
var url = 'http://scd.blaze.wpengine.com/wp-json/posts?type=listings&filter[listing_area]=northwest';
$http.get(url).then(function(data) {
$scope.data = data.data;
});
});
</script>
<select class="selectOption">
<option>Search by Region</option>
<option>NorthWest</option>
<option>NorthEast</option>
<option>Midlands</option>
<option>EastAnglia</option>
<option>SouthEast</option>
<option>SouthWest</option>
<option>Scotland</option>
<option>Wales</option>
<option>NorthernIreland</option>
<option>ChannelIslands</option>
</select>
<div id="changingArea">
<body ng-app="myApp">
<div id="NorthWest" class="desc">
<div ng-controller="northWestCtrl">
<div ng-repeat="d in data">
<h2 class="entry-title title-post">{{d.title}}</h2>
<img src="{{d.acf.logo}}">
<div id="listing-contact">Contact: {{d.acf.contact}}, {{d.acf.position}}</div>
<div id="listing-address-1">
{{d.acf.address_1}}, {{d.acf.address_2}} {{d.acf.address_3}} {{d.acf.town}} {{d.acf.county}} {{d.acf.postcode}}
</div>
<div id="listing-phone">Telephone: {{d.acf.telephone}}</div>
<div id="listing-mobile">Mobile: {{d.acf.mobile}}</div>
<div id="listing-email">Email: {{d.acf.email}}</div>
<div id="listing-website">Website: {{d.acf.website}}</div>
<div id="listing-established">Established: {{d.acf.established}}</div>
<div id="listing-about">About: {{d.acf.about}}</div>
<div id="listing-mailingaddress">Mailing Address: {{d.acf.mailing_address_}}, {{d.acf.mailing_address_2}}, {{d.acf.mailing_address_3}}, {{d.acf.mailing_town}}, {{d.acf.mailing_county}}, {{d.acf.mailing_postcode}}</div>
<div id="listing-directions">Directions: {{d.acf.directions}}</div>
<div id="scd-link">View on The Shooting Club Directory</div>
</div>
</div>
</div>
</body>
</div>
Here is a working code pen - http://codepen.io/anon/pen/yePYdq
Angular is a great JavaScript front-end framework to choose, and you're off to a good start, but a lot of changes could be made. I've made some suggested changes for easier ways to do things below.
See this CodePen for all changes.
It looks like you've grasped the idea of ng-repeat, but there's definitely a lot of repeated HTML and JS in your view and controller, so let's see if we can do better.
Let's try this without jQuery to avoid direct manipulation of the DOM. And instead of many controllers, we can do this with a single controller.
<div ng-app="MyApp">
<div ng-controller="MyController">
...
</div>
</div>
<script type="text/javascript">
var app = angular.module('MyApp', []);
app.controller('MyController', ...);
</script>
For the dropdown, we'll use ng-repeat in our view and display the names of the shooting types from our model
...
<select ng-model="selectedListing">
<option
ng-repeat="listingShootingType in listingShootingTypes"
value="{{listingShootingType.name}}">
{{listingShootingType.name}}
</option>
</select>
...
<script type="text/javascript">
...
// Our selections/filters
$scope.listingShootingTypes = [
'All',
'Air Rifle/Air Pistol',
'Clay',
'ABT',
'Double Trap',
'English Skeet',
'English Sporting',
'Fitasc',
'Olympic Skeet',
'Olympic Trap',
'Simulated Game',
'Sport Trap/Compact',
'Universal Trench',
'ZZ/Helice',
'Rifle',
'Centrefire Target Rifle',
'Gallery Rifle',
'Muzzle Loading',
'Practice Shotgun',
'Smallbore Rifle'
];
...
</script>
With only one controller, we can still use ng-repeat for each listing.
<div ng-repeat="d in data">
<h2 class="entry-title title-post">{{d.title}}</h2>
<div id="listing-image"><img src="{{d.acf.logo}}"></div>
<div id="listing-contact">Contact: {{d.acf.contact}}, {{d.acf.position}}</div>
<div id="listing-address-1">
{{d.acf.address_1}}, {{d.acf.address_2}} {{d.acf.address_3}} {{d.acf.town}} {{d.acf.county}} {{d.acf.postcode}}
</div>
<div id="listing-phone">Telephone: {{d.acf.telephone}}</div>
<div id="listing-mobile">Mobile: {{d.acf.mobile}}</div>
<div id="listing-email">Email: {{d.acf.email}}</div>
<div id="listing-website">Website: {{d.acf.website}}</div>
<div id="listing-established">Established: {{d.acf.established}}</div>
<div id="listing-about">About: {{d.acf.about}}</div>
<div id="listing-mailingaddress">Mailing Address: {{d.acf.mailing_address_}}, {{d.acf.mailing_address_2}}, {{d.acf.mailing_address_3}}, {{d.acf.mailing_town}}, {{d.acf.mailing_county}}, {{d.acf.mailing_postcode}}</div>
<div id="listing-directions">Directions: {{d.acf.directions}}</div>
<div id="scd-link">View on The Shooting Club Directory</div>
</div>
Finally... How do we only display listings that match our selected shooting type from the dropdown? We could use a custom Angular filter!
...
<div ng-repeat="d in data | filter:isSelectedListing">
...
<script type="text/javascript">
...
// Let's define a custom Angular filter because the WordPress JSON is complex
$scope.isSelectedListing = function(listing) {
// Show nothing if nothing is selected
if (angular.isUndefined($scope.selectedListing) || $scope.selectedListing == '') {
return false;
}
// Show all if 'All' is selected
if ($scope.selectedListing == 'All') {
return true;
}
// If the shooting type we're looking for is present, show the listing.
// To do this, we parse the WordPress JSON object model.
if (angular.isDefined(listing.terms.listing_shooting_type)) {
for (var i = 0; i < listing.terms.listing_shooting_type.length; i++) {
if (listing.terms.listing_shooting_type[i].name == $scope.selectedListing) {
return true;
}
}
}
return false;
};
...
</script>
Hopefully this gives you an idea of how we better leverage ng-repeat + DRY :)
The entire CodePen is here.

AngularJS, D3 and JQuery in the same page. D3 can't read the correct DOM dimensions

I am struggling with my page that can't load properly. I am using a simple header-body-footer structure in html5 and CSS3.
+----------+
| HEADER |
+---+----------+---+
| BODY |
+---+----------+---+
| FOOTER |
+----------+
What I am doing right now is create a svg with D3 inside the body space, reading width and height dynamically after window is loaded (and pictures also).
Now I want to add angular to avoid code redundancy inside each page of the site and I did this:
(function() {
var app = angular.module('neo4art', []);
var pages = {
"genesis.html": "The genesis of the project",
"about.html": "About Us",
"team.html": "The Team",
"index.html": "Traversing Art Through Its Connections"
}
app.directive("header", function() {
return {
restrict : 'E',
templateUrl : 'header.html'
};
});
app.directive("footer", function() {
return {
restrict : 'E',
templateUrl : 'footer.html'
};
});
app.controller('menuController', function($scope, $location, rememberService){
$scope.isActive = function(route){
return rememberService.getActualPage() === route;
};
});
app.controller('MainController', function(Page){
this.page = pages;
});
app.factory('rememberService', function($location) {
return {
getActualPage: function(){
var arr = $location.$$absUrl.split("/");
var pageName = arr[arr.length -1];
return pageName;
}
};
});
app.factory('Page', function(rememberService) {
return {
getTitle: function(){
return "neo4Art - "+ pages[rememberService.getActualPage()];
}
};
});
})();
To handle footer and header with Directives (< header>< /header>)
this is (part of) the code used to create the svg. I will only show you what I am interested in, the part that read the "on-field" measurements.
function Search(){
var width = $(".container-svg").width();
var height = $(".container-svg").height();
console.log("width:" + width + " - height:"+ height);
}
Before the use of angular I was using this in:
$(window).load(function(d) {
var search = new Search();
});
and all was going pretty well.
now using:
angular.element(window).load(function() {
var search = new Search();
});
I have a wrong height inside the var. It seems like the "new Search()" is called when the svg is still too high (the header is not yet rendered)
This is the html of the index (simplified)
<!DOCTYPE html>
<html lang="it-IT" ng-app="neo4art" ng-controller="MainController as mc">
<head>
<meta charset="utf-8">
<script src="resources/js/jquery/jquery-2.1.3.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" type="text/css" media="screen" href="resources/css/style.css" />
<link rel="stylesheet" type="text/css" media="screen" href="resources/css/style-index-new.css" />
<link rel="stylesheet" type="text/css" href="resources/css/font-awesome-4.3.0/css/font-awesome.min.css" />
<title>{{"neo4Art - " + mc.page["about.html"]}}</title>
</head>
<body>
<script type="text/javascript" src="resources/js/angular.min.js"></script>
<script type="text/javascript" src="resources/js/search.js"></script>
<script type="text/javascript" src="resources/js/neo4art.js"></script>
<div class="container-page scrollbar-macosx" when-ready="isReady()">
<div class="content-home">
<header></header>
<div class="text-home">
<svg class="container-svg">
</svg>
</div>
<div class="footer">
© 2015 neo4<span class="palette-dark-blue">A</span><span class="palette-blue">r</span><span class="palette-orange">t</span> - All
rights reserved <a href="//www.iubenda.com/privacy-policy/430975" class="iubenda-white iubenda-embed"
title="Privacy Policy">Privacy Policy</a>
</div>
</div>
</div>
</body>
</html>
This is the code of the header:
<div class="header">
<div class="logo">
<img src="resources/img/neo4art-logo-big.png" />
<div class="motto">Traversing Art through its connections</div>
</div>
<form method="get" action="graph.html" name="query">
<div class="menu">
<div class="search-bar-container">
<span class="icon"><i class="fa fa-search"></i></span><input type="search" id="search" placeholder="Search..." name="query" />
</div>
<ul>
<li>HOME</li>
<li>ABOUT</li>
<li>GENESIS</li>
<li>TEAM</li>
</ul>
</div>
</form>
</div>
Without angularjs it is rendered correct, with angular I have a wrong value like the image is not loaded at all.
I hope my question is clear enough, the problem I think is in when each function is called during the load of the page.
EDIT: It's strange also that sometimes the page is loaded correctly (random occurency)
AngularJS version 1.3.16
So you are having this weird cross road in understanding of angular. First lets talk about angular.element is not the same as a jquery element. You shouldn't be doing DOM manipulation from an element you create like that you should have a directive with a link function to make sure this is happening in the digest cycle when you want it to. This is a hard concept to get but I think the best example I can find is from this other question.
Angular.js: set element height on page load
This does a reasonable job of showing and explaining what we are talking about.
A simple trick to see if it's related to initialization of the page would be to wrap you measuring code in a setTimeout call and delay it for 500ms. If this fixes the measurement you'd have to go a search for the actual correct place to put your measuring code :-)

Categories