Calling an angularjs function during controller init - angularjs

I have a large amount of code that I inherited. It is working according to the original spec, so although it may or may not be the "right" way of doing things, it does work and I'd prefer not to mess with it. (I am VERY new to angular, and under a tight deadline - I don't have time right now to "fix" things that aren't broken, even if they're not being done the "correct" way.)
I have a checkbox and a group of address fields. When the checkbox is checked, a function runs that populates the address fields.
<input id='as_check' type="checkbox" ng-model='model.prepop_addr' ng-change="model.handlePrepopAddress()">
<input type="text" placeholder="Address 1" required ng-model='model.payment.addr1'/>
<input type="text" placeholder="Address 2" ng-model='model.payment.addr2'/>
<input type="text" placeholder="City" required ng-model='model.payment.city'/>
<input type="text" placeholder="State" required ng-model='model.payment.state'/>
<input type="text" placeholder="Zip" required ng-model='model.payment.zip'/>
In my controller, I have a function called handlePrepopAddress():
model.handlePrepopAddress = function () {
if (model.prepop_addr) {
console.log("prepop is true");
var del_addr = $window.user_state.deliveryAddress;
model.payment.addr1 = del_addr.address;
model.payment.addr2 = null;
model.payment.city = del_addr.city;
model.payment.state = del_addr.state;
model.payment.zip = del_addr.zip;
} else {
console.log("prepop is false");
model.payment.addr1 = null;
model.payment.addr2 = null;
model.payment.city = null;
model.payment.state = null;
if (!model.payment.saved) {
model.payment.zip = null;
}
}
return true;
};
When I click the checkbox, this runs correctly and updates the various fields. Requirements have changed, and the client now wants the checkbox defaulted to checked, which would then default the values of the fields. Rather than duplicating code, I thought I'd be able to just run the function at the start of the controller, like this:
place_order.controller('AsCreditCardCtrl', ['$scope', 'PaymentModel', function ($scope, model) {
$scope.model = model;
model.prepop_addr = true;
model.handlePrepopAddress();
}]);
The function is running, as the console.log statements I have in the function are running. But the view is not updating; the checkbox is visibly unchecked and the fields are empty aside from the initial placeholder values.
What am I missing? How can I make the function update the view?

You can try
var i= model.handlePrepopAddress();
It might work.

Related

Input text change checkbox model only once [AngularJS]

I have this
<input type="text" ng-click"function1()">
And this:
<input type="checkbox" ng-change="function2()" ng-model="stateCheck">
The functions do this:
$scope.function1 = function(){
$scope.stateCheck = false;
}
$scope.function2 = function(){
$scope.stateCheck = !$scope.stateCheck;
}
But the function1 just works the first time. Does anyone know why? Thanks.
ng-change="function2()"
ng-model="stateCheck"
These two events are conflicting with each other, Basically ng-model makes the $state.stateCheck=true and then functin2() $state.stateCheck=!$state.stateCheck makes it false. Due to this checkbox will always remain unchecked. function1() works fine

Using AngularJS getterSetter with ng-repeat

Using AngularJS 1.6 I have a simple input that uses a getter-setter model:
<input name="bar" type="number"
ng-model="dialog.bar" ng-model-options="{getterSetter:true}" />
The getterSetter option means I can have all the values pass through a method in my controller, for validation and/or updating other values, for example:
let bar = null;
this.bar = function(value) {
if (arguments.length) {
validate(value);
bar = value;
update();
}
return bar;
}
And that works very nicely.
Now I have a foo object that has multiple bar values inside it. I don't know how many bars there will be. I want to create an input for each bar. The UI is simple enough using ng-repeat:
<div ng-repeat="bar in foo.getBars()">
<input name="bar{{$index}}" type="number"
ng-model="dialog.bar" ng-model-options="{getterSetter:true}" />
</div>
Ah, but how do I link the model back to a getter-setter? I somehow would need to indicate to the getter-setter which $index is being used. I could of course use an array of model values directly, but then I wouldn't get the benefit of a getter-setter!
The best idea I've come up with so far is to use an array of getter-setters. Then the model looks like this:
<div ng-repeat="bar in foo.getBars()">
<input name="bar{{$index}}" type="number"
ng-model="dialog.bars[$index]" ng-model-options="{getterSetter:true}" />
</div>
Then for each bars[x] I simply create a separate function that each remembers the $index using closure via normal loop:
const bars = Array(max).fill(0); //values
this.bars = Array(max); //getter-setter functions
for(let i=0; i<max; i++) {
this.bars[i] = function(value) {
if (arguments.length) {
validate(value);
bars[i] = value;
update();
}
return bar;
}
}
Any better ideas?

Angularjs ng-value sum fields

Hi i have inputs like this
<input type="text" ng-model="tbl.Public">
<input type="text" ng-model="tbl.Private">
<input type="text" ng-value="tbl.Public--tbl.Private" ng-model="tbl.Total">
the above form will working fine it will sum the Public and Private value and put it in tbl.Total field. My problem is in edit form where value of tbl.Total, tbl.Public, tbl.Private are assign from database.
js
$scope.tbl.Public=10;
$scope.tbl.Private=25;
$scope.tbl.Total=35;
now after assigning a value from js when i change value of tbl.Public or tbl.Private in form it is not affecting a tbl.Total it should sum the two value and put it in tbl.Total field.
Thank you for your any help and suggestion.
ng-value is usually used on radiobuttons and option elements, it's not a good fit for your use case.
A better thing to do would be implementing an updateTotal() function combined with ng-change. I would also recommend changing your input types to number so you're not allowing users to sum text.
<input type="number" ng-model="tbl.Public" ng-change="updateTotal();">
<input type="number" ng-model="tbl.Private" ng-change="updateTotal();">
<input type="number" ng-model="tbl.Total">
In your controller:
$scope.updateTotal = function() {
$scope.tbl.Total = $scope.tbl.Public + $scope.tbl.Private;
}
It should be like this to prevent from concatenate
$scope.updateTotal = function() {
var Public = Number($scope.tbl.Public || 0);
var Private = Number($scope.tbl.Private || 0);
$scope.tbl.Total = Public + Private;
}

Google Maps Places Autocomplete - Uncaught TypeError: Cannot read property 'getPlace' of undefined

setAutocomplete() {
this.originPlaceId = null;
this.destinationPlaceId = null;
this.travelMode = google.maps.TravelMode.WALKING;
this.directionsDisplay.setMap(this.map);
this.setMapControls(this.map);
this.setupClickListener('changemode-walking', google.maps.TravelMode.WALKING);
this.setupClickListener('changemode-transit', google.maps.TravelMode.TRANSIT);
this.setupClickListener('changemode-driving', google.maps.TravelMode.DRIVING);
console.log(this.originInput);
this.originAutocomplete = new google.maps.places.Autocomplete(this.originInput);
this.originAutocomplete.bindTo('bounds', this.map);
console.log(this.originAutocomplete);
this.originAutocomplete.addListener('place_changed', function() {
console.log(this.originAutocomplete);
var place = this.originAutocomplete.getPlace();
console.log("here", place);
if (!place.geometry) {
window.alert("Autocomplete's returned place contains no geometry");
return;
}
this.expandViewportToFitPlace(this.map, place);
// If the place has a geometry, store its place ID and route if we have
// the other place ID
this.originPlaceId = place.place_id;
this.route(this.directionsService, this.directionsDisplay);
});
this.destinationAutocomplete = new google.maps.places.Autocomplete(this.destinationPlaceInput);
this.destinationAutocomplete.bindTo('bounds',this.map);
this.destinationAutocomplete.addListener('place_changed', function() {
var place = this.destinationAutocomplete.getPlace();
if (!place.geometry) {
window.alert("Autocomplete's returned place contains no geometry");
return;
}
this.expandViewportToFitPlace(this.map, place);
// If the place has a geometry, store its place ID and route if we have
// the other place ID
this.destinationPlaceId = place.place_id;
this.route(this.directionsService, this.directionsDisplay);
//this.getNearbyPlaces(this.destinationPlaceId, 5000);
});
};
setMapControls(map) {
this.originInput = document.getElementById('origin-input');
this.destinationPlaceInput = document.getElementById('destination-input');
this.modes = document.getElementById('mode-selector');
map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.originInput);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.destinationPlaceInput);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.modes);
};
<input id="origin-input" class="controls" type="text" force-selection="true" placeholder="Enter an origin location">
<input id="destination-input" class="controls" type="text" force-selection="true" placeholder="Enter a destination location">
<div id="mode-selector" class="controls">
<input type="radio" name="type" id="changemode-walking" checked="checked">
<label for="changemode-walking">Walking</label>
<input type="radio" name="type" id="changemode-transit">
<label for="changemode-transit">Transit</label>
<input type="radio" name="type" id="changemode-driving">
<label for="changemode-driving">Driving</label>
</div>
<div id="directionsList"></div>
<div id="map_canvas"></div>
I have these functions which are supposed to give me the directions A to B from Google Maps Places from the input fields (originInput and destinationPlaceInput).
After I select an input I have this error in console:
Uncaught TypeError: Cannot read property 'getPlace' of undefined
This error is thrown after the "place_changed" event triggers.
Inside the place_changed-callback the keyword this points to the object which has been triggered the event.
so you may simply use
var place = this.getPlace();
instead of
var place = this.destinationAutocomplete.getPlace();
How to keep the context/this of the controller?
tldr:
Create a function that returns a function with the context:
this.searchBox.addListener('places_changed', ((that) => () => this.placesChanged(that))(this));
Explanation
Like Dr.Molle points out: this points to the object which has triggered the event, namely the element on which you want to add an event listener on.
The addListener takes a function declaration. To keep access on your controller's this, you could also create a closure that passes the controller's this into the function, like so:
this.searchBox.addListener('places_changed', ((that) => () => this.placesChanged(that))(this));
Here I defined a function that takes a parameter called that, and upon execution returns a function that contains the this. It is a closure because the inner function's scope keeps a reference to the that variable from the outer functions scope (and other variables that could get passed to this.placesChanged).
Now if a event places_changed is emitted, the function declaration is ran. I am still able to change properties on the that variable inside the this.placesChanged function. This is possible because in objects are passed by reference (whereas primitive types are passed by values).

Check all boxes toggle method not working like expected

I'm wiriting a method to toggle a checklist as checked / unchecked, as follow:
$scope.checkAllToggle = function(dtProvider, destiny, ev) {
$(ev.currentTarget).parent().find('span').html());
data[destiny] = [];
if ($(ev.currentTarget).parent().find('span').html()=='Check all') {
for (var val in data[dtProvider]) data[destiny].push(data[dtProvider][val].id);
$(ev.currentTarget).parent().find('span').html('Uncheck all');
}
else $(ev.currentTarget).parent().find('span').html('Check all');
}
The label of the toggle button changes every time, but the state of the checkboxs only becomes checked after 2 click, from this point change in each 3 clicks.
What's wrong?
Append
if(!$scope.$$phase) $scope.$apply();
to the method make no difference, as prepending $scope to data
Code in plunker: http://plnkr.co/edit/Zf6UzLbC4osRov7IR5Na?p=preview
Rather than do it that way, this would be the easier way to do it.
Make the master input and assign it to a model.
<input type="checkbox" ng-model="master">
Then have your other inputs be tied to the master like this.
<input type="checkbox" ng-model="box1" ng-checked="master">
<input type="checkbox" ng-model="box2" ng-checked="master">
<input type="checkbox" ng-model="box3" ng-checked="master">
Here is your plunker with these changes.
Master Checkbox Plunker
I got your plunker to work by doing this.
$scope.checkToggle = function(dtProvider, destiny, ev) {
$scope.data[destiny] = [];
if ($(ev.currentTarget).parent().find('span').html() == 'Check all') {
for (var val in $scope.data[dtProvider]){
$scope.data[destiny].push($scope.data[dtProvider][val].id);
$(ev.currentTarget).parent().find('span').html('Uncheck all');
}
} else {
$(ev.currentTarget).parent().find('span').html('Check all');
}
$scope.$apply();
};
I could not get it to work if calling safely like if(!$scope.$$phase){$scope.$apply();} but it is working with straight $scope.$apply(). This might have something to do with rootScope and need to be safetly called in a different manner.
See this link: https://coderwall.com/p/ngisma
You also had some missing {} in your code that could have been causing some issues.

Resources