ngInit not working asynchronously(with $q promise) - javascript

Edit:
Plunker is working, actual code isn't:
http://plnkr.co/edit/5oVWGCVeuTwTARhZDVMl?p=preview
The service is contains typical getter\setter stuff, beside that, it functions fine, so I didn't post it's code to avoid TLDR.
TLDR version: trying to ng-init a value fetched with AJAX into the ngModel of the text-area, the request resolves with the correct value, but the textarea remain empty.
parent controller function(talks to the service):
$scope.model.getRandomStatus = function(){
var deffered = $q.defer();
var cid = authService.getCompanyId();
var suggestions = companyService.getStatusSuggestions(cid);
if(suggestions && suggestions.length > 0){
deffered.resolve(suggestions[Math.floor(Math.random(suggestions.length) + 1)].message);
return deffered.promise;//we already have a status text, great!
}
//no status, we'll have to load the status choices from the API
companyService.loadStatusSuggestions(cid).then(function(data){
companyService.setStatusSuggestions(cid, data.data);
var result = data.data[Math.floor(Math.random(data.data.length) + 1)];
deffered.resolve(result.message);
},
function(data){
_root.inProgress = false;
deffered.resolve('');
//failed to fetch suggestions, will try again the next time the compnay data is reuqired
});
return deffered.promise;
}
child controller:
.controller('shareCtrl', function($scope){
$scope.layout.toggleStatusSuggestion = function(){
$scope.model.getRandomStatus().then(function(data){
console.log(data);//logs out the correct text
//$scope.model.formData.shareStatus = data;//also tried this, no luck
return data.message;
});
$scope.model.formData.shareStatus = $scope.layout.toggleStatusSuggestion();//Newly edited
}
});
HTML:
<div class="shareContainer" data-ng-controller="shareCtrl">
<textarea class="textAreaExtend" name="shareStatus" data-ng-model="model.formData.shareStatus" data-ng-init="model.formData.shareStatus = layout.toggleStatusSuggestion()" cols="4"></textarea>
</div>

I believe what you are wanting is :
$scope.model.getRandomStatus().then(function(data){
$scope.model.formData.shareStatus = data.message;
});
Returning something from within then does not return anything from the function wrapping it and therefore does nothing

Turns out that I had a custom validation directive that was watching the changes in the model via $formatters, and limting it to 80 chars(twitter), it was failing silently as I didn't expect to progmatically insert invalid values into my forms, very stupid, but could happen to anyone.
Had to make some changes to it, so it's worth to remember in case it happens to anyone else.

Related

Calling a function from html and getting results from angularjs

I am stuck on getting value back after I call a function in my HTML file. I did some research and tried could things but no success. Please see below code:
HTML:
<div class="form-group">
<div class="col-sm-9"><label for="IPAddr">IP Address</label><input ng-model="IPAddr" id="IPAddr" name="IPAddr" ng-blur="IPAvail=validateIP()"
type="text" class="form-control">{{IPAvail}}</div>
</div>
A basic form with an input field for IP address. Calls the validateIP(), and shows returned value "{{IPAvail}}".
JS:
$scope.validateIP = function(IPAvail) {
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
var ipVar = $.param({ip: $scope.ipadd});
$http({
url: 'https://test.com/flask_server/findIP',
method: "POST",
data: ipVar
})
.then(function(response) {
$scope.result = response.data;
ip = response.data;
if (ip.indexOf("10.10.10.1") >=0) {
alert("Matches with IP")
IPAvail = "Yes"
return IPAvail
} else {
alert("Does not match with IP")
}
IPAvail = "No"
return IPAvail
});
};
I simplified the code to make it easy to read but basically, everything is working correctly but the only thing I am having an issue is returning IPAvail variable back to HTML so it can be used here:
type="text" class="form-control">{{IPAvail}}</div>
Thanks
Damon
Instead of returning IPAvail, set $scope.IPAvail to the required value. Rather than use onblur, you should defer to ng-change="validateIP()".
Using ng-change allows AngularJS to know about the effect. Otherwise you may run into trouble with AngularJS not knowing. It is better for AngularJS to know.

angularjs change in model renders only on clicking somewhere

I am working on a search suggestion functionality using firebase and angular js. Basically on key up of a search input box, I call the below function:
scope.suggestString = {};
scope.search = function(){
scope.suggestString = {};
firebaseDb.ref("users")
.orderByChild("Name")
.startAt(scope.searchedString)
.endAt(scope.searchedString + "\uf8ff")
.on("value", function(snapshot) {
snapshot.forEach(function(childSnapshot) {
if(scope.suggestString.hasOwnProperty(childSnapshot.key) == false){
scope.suggestString[childSnapshot.key] = childSnapshot;
}
});
});
}
HTML:
<form>
<input type="text" ng-model="searchedString" ng-keyup="search()">
<ul>
<li ng-repeat="(key,item) in suggestString">
{{item.val().firstName}}
</li>
</ul>
</form>
The code works , the call goes to firebase and fetches the records but I have to click somewhere in search input box for the suggestions to be displayed.
I tried using scope.$apply() but its not working .Its says already applying scope
maybe you can use something like:
<input ng-model="searchedString" ng-model-options="{debounce: 1000}" type="text">
on input box, which will update ng-model (searchString) with search string in input element, after 1 second delay of typing.
After that you can put something like:
scope.$watch('searchedString', (newVal,oldVal)=>{
scope.suggestString = {};
if(scope.searchedString.length <3){
return;
}
firebaseDb.ref("users")
.orderByChild("Name")
.startAt(scope.searchedString)
.endAt(scope.searchedString + "\uf8ff")
.on("value", function(snapshot) {
snapshot.forEach(function(childSnapshot) {
if(scope.suggestString.hasOwnProperty(childSnapshot.key) == false){
scope.suggestString[childSnapshot.key] = childSnapshot;
}
});
});
});
Now it should work if your code for getting data is correct.
Angular doesn't know about this async call, so you have to trigger a digest. You can do that safely without getting a console error by calling $evalAsync after you get the response:
// ...
.on("value", function (snapshot) {
scope.$evalAsync(function () {
snapshot.forEach(function (childSnapshot) {
if (scope.suggestString.hasOwnProperty(childSnapshot.key) == false) {
scope.suggestString[childSnapshot.key] = childSnapshot;
}
})
});
});
But I suspect scope.$apply will be fine too, if you do it outside the loop as I am doing above.
Another option is to wrap all of this firebase functionality into a separate function (possibly in a service), and return a promise from that function using the $q service, and resolve the firebase response. Then you won't have to manually trigger a digest, since Angular knows about the $q service.
Safe apply was the thing I was looking for, the search is smooth now.

Display results from api after user input

I'm learning JS and I need some help figuring out why my info isn't getting populated in the html. I'm just trying to get the basic functionality to work, so that I can continue to expand on it.
User is supposed to input a 3 digit route value, which will then return all the route information from an api call. I was able to get the route info to display earlier when I got the api call set up, but I'm struggling to figure why it's not displaying now that I tried adding in a feature to allow the user to input the route. See attached pen
HTML
<div class='container'>
<h1 id='header'>Route Info</h1>
<input id="input" type="text" placeholder="Enter 3 digit route ex 005" >
<input type="button" value="Get Route" onclick="getRoute()">
<br>
<p id = 'p'><span id="routeInfo"></span></p>
</div>
Javascript
$(document).ready(function() {
var route = $('#input');
getRoute.click(function() {
var scriptTag = document.createElement('SCRIPT');
scriptTag.src = "https://wsdot.wa.gov/Traffic/api/Bridges/ClearanceREST.svc/GetClearancesAsJson?AccessCode=59a077ad-7ee3-49f8-9966-95a788d7052f&callback=myCallback&Route=" + route;
document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
var myCallback = function(data) {
var myarray = Array.prototype.slice.call(data);
document.getElementById("routeInfo").innerHTML = JSON.stringify(myarray);
}
});
});
It looks like you are jumping through a lot of hoops you don't need to. As long as you are using Jquery, you should look into getting the api data with an ajax request. It's much easier and more intuitive. Also you have a few problems such as trying to get the input value with var route = $('#input'); which return the actual input element. You are also processing the returned data in a way that won't work.
Here's a basic example to get you going on (IMO) a better track:
function getRoute() {
var route = $('#input').val();
var url = "https://wsdot.wa.gov/Traffic/api/Bridges/ClearanceREST.svc/GetClearancesAsJson?AccessCode=59a077ad-7ee3-49f8-9966-95a788d7052f&Route=" + route;
$.ajax({url: url, success: function(data){
var retValue = "";
var i = 0
for(i; i< data.length; i++) {
retValue += data[i].BridgeName + "<br>"
}
document.getElementById("routeInfo").innerHTML = retValue;
}});
}
If you intend functionality in the getRoute.click callback to run, you need to rewrite that as a method function getRoute(), or get the button element via jQuery and assign that to the variable getRoute. As it stands, you have the click method wired via the markup to a function named getRoute which does not exist. In the JS you are trying to register a click event to a jQuery object named getRoute which does not exist.
getRoute needs to be a global function for it to be called from html :
getRoute = (function() {
Also, myCallback needs to be a global function for it to be called from your loaded script (just remove the var):
myCallback = function(data) {

updating a json value with angular

I am trying to update an json object value from a textbox using angular and I'm not sure what the best way to go about it is.
This is the json object...
$scope.productAttributes = {
"CostRequirements":[
{
"OriginPostcode": 'NW1BT',
"BearerSize":100
}
]
}
And when a use types in a text field and clicks a button, I would like to grab that textfield value and pass it into the json object to replace the postcose value (OriginPostcode) I tried to pass in a scope variable but that didnt work.
<input type="text" placeholder="Please enter postcode" class="form-control" ng-model="sitePostcode"/>
And this is the fucntion that is fired when the user clicks a button to submit the json
var loadPrices = function () {
productsServices.getPrices1($scope.productAttributes)
.then(function (res) {
$scope.selectedProductPrices = res.data.Products;
// $scope.selectedProductAddOns = res.data.product_addons;
})
.finally(function () {
$scope.loadingPrices = false;
$scope.loadedPrices = true;
});
};
Could anyone tell me what I need to do to put the user input in the textbox into the json object?
Many thanks
What we don't see is the function that runs the update with the button. It should look something like this
// your HTML button
<button ng-click='updateThingy()'>Update</button>
// your HTML input
<input type="text" ng-model="myObject.sitePostcode"/>
// your controller
$scope.myObject = { // ties to the ng-model, you want to tie to a property of an object rather than just a scope property
sitePostcode : $scope.productAttributes.CostRequirements[0].OriginPostcode // load in post code from productAttributes
};
$scope.updateThingy = function(){
$scope.productAttributes.CostRequirements[0].OriginPostcode = $scope.myObject.sitePostcode;
};
Here is a demo plunker for updating a value on button click, hope it helps out.
http://plnkr.co/edit/8PsVgWbr2hMvgx8xEMR1?p=preview
I guess loadPrices function is inside your controller. Well, then you should have sitePostCode variable available inside your controller and your function. So you just need to inject that value inside $scope.productAttributes.
$scope.productAttributes.sitePostCode = $scope.sitePostCode;
This you need to put it before you make the productsServices.getPrices1 call.
var loadPrices = function() {
$scope.productAttributes.sitePostCode = $scope.sitePostCode;
productsServices.getPrices1($scope.productAttributes)
.then(function(res) {
$scope.selectedProductPrices = res.data.Products;
// $scope.selectedProductAddOns = res.data.product_addons;
})
.finally(function() {
$scope.loadingPrices = false;
$scope.loadedPrices = true;
});
};
Let me know if it worked.

Ajax/jQuery live search is duplicating the output results

I'm currently working on Ajax and jQuery live search which finds a results in a JSON file. Script is working fine, but the is only one problem - it's duplicating the result data.
EXAMPLE:
MARKUP:
<div class="row">
<h3>Live Search Results</h3>
<div id="update-results">
<p>event_name | club_name | memberid</p>
<ul id="update">
<!-- <li></li> -->
</ul>
</div>
</div>
SCRIPT:
$('#search').keyup(function() {
var searchField = $('#search').val();
var $update = $('#update');
$update.empty();
$.get("getEventsWithVideos.php?text=" + searchField, function(data) {
var vals = jQuery.parseJSON(data);
if($.isArray(vals['Event'])) {
$.each(vals['Event'], function(k,v){
$update.append("<li value='"+v['id']+"'><a href='#'>" + v['event_name'] + "</a></li>");
});
} else {
$update.append("<li value='"+vals['Event']['id']+"'><a href='#'>" + vals['Event']['event_name'] + "</a></li>");
}
});
});
I've tried to debug and stop the error, but it was unsuccessful. Can anyone help me please with that?
Put the empty() inside the response handler:
$.get("getEventsWithVideos.php?text=" + searchField, function(data) {
$update.empty();
basically you are clearing the list on every keystroke (rapid), then requesting the data, then (sometime later) appending the results that come back (which could be multiple results depending on the timing).
I didn't reproduce your error but I suspect that you have problem with multiple request to server and adding them all instead of last one. Probably adding below code will fix your problem
$update.empty();
Anyway I suggest you to use 2 more functions: throtlle and debounce from underscore to prevent too much request on every keyup.
Also you could try Rx.js witch give following example (https://github.com/Reactive-Extensions/RxJS):
var $input = $('#input'),
$results = $('#results');
/* Only get the value from each key up */
var keyups = Rx.Observable.fromEvent($input, 'keyup')
.map(function (e) {
return e.target.value;
})
.filter(function (text) {
return text.length > 2;
});
/* Now debounce the input for 500ms */
var debounced = keyups
.debounce(500 /* ms */);
/* Now get only distinct values, so we eliminate the arrows and other control characters */
var distinct = debounced
.distinctUntilChanged();
Try changing this line $update.empty(); of your code to $update.find('li').remove(); and put it inside the response handler.
This removes all the previous data before you append the new values. Hopefully it might work.

Categories

Resources