I want to build a directive that checks for duplicate username. So I have created an index.html file and an uniqueId directive. Now in the directive I am not able to do ngModel.setValidity(). It is getting undefined.
Also I am fetching data from a local json file username.json.
When I console log console.log(ngModel.$setValidity('unique', unique)) I get undefined.
I have created a plunk for the code::
https://embed.plnkr.co/XTPf9PjiMn9if5Y0DaHt/
You need to iterate through the users present in JSON. And, if your currentValue matches any of those, you need to set it as invalid using $setValidity. Like this:
dataService.getUsers().then(function(currentusers) {
console.log(currentusers)
//Ensure value that being checked hasn't changed
//since the Ajax call was made
if (currentValue == element.val()) {
currentusers.forEach(function(user) {
if (currentValue === user.property) {
ngModel.$setValidity('unique', false)
}
});
}
}, function() {
//Probably want a more robust way to handle an error
//For this demo we'll set unique to true though
ngModel.$setValidity('unique', true);
});
});
Also, your service was getting the JSON every time. Alternatively, you can store your JSON response in a variable inside your angular service (which is a singleton) so it's faster than before. Like this,
dataFactory.getUsers = function() {
var deferred = $q.defer()
if (angular.isDefined(savedResults)) {
deferred.resolve(savedResults.data);
return deferred.promise;
} else {
return $http.get(serviceBase).then(
function(results) {
savedResults = results
return results.data;
});
}
};
Here, we return a resolved promise if data is already available. It will get the JSON for the first time and will use from within.
working plunker
please check plnkr link
If you type nitesh#gmail.com, it will display email already in use.
If you type nitesh#gmail.com1, it won't show error message.
I have changed condition.
Related
I need to fetch suggest options using typeahead making a http call
$scope.getlist = function getListofNames(name) {
return $http({
method: 'GET',
url: '/v1/'+name,
data: ""
}).then(function(response){
if(response.data.statusMessage !== 'SUCCESS'){
response.errorMessage = "Error while processing request. Please retry."
return response;
}
else {
return response.data.payload;
}
}, function(response){
response.errorMessage = "Error while processing request"
return response;
}
);
}
response.data.payload is an array of objects , It is fetched successfully but I am getting this error
Error: [filter:notarray] http://errors.angularjs.org/1.4.5/filter/notarray?
Note : I am using angular 1.4.5 and Bootstrap v3.1.1
I'm guessing your typeahead markup looks like this:
<input [...] typeahead="item in getItems($viewValue) | filter: $viewValue">
Why it is not working:
The problem occurs when the array of items is fetched asynchronously. In your case the getItems function is called getListofNames, and your items are indeed fetched asynchronously, due to the call to $http. Therefore at the time the error occurs, getListofNames() is still an unresolved promise object, not yet an array of names.
How could you make that work:
Remove the filter from the template. You should filter the array before returning it in getItems. Ideally, you want to do the filtering server-side. Indeed, the server receives the substring typed by the user (this is the $viewValue argument), and therefore it has all the data to filter the array. That would prevent returning all the elements and make the response shorter.
Alternatively, you could filter client-side in the callback of the promise:
$scope.getList = function getListofNames(name) {
return $http(...}).then(
function(response){
// filter response.data.payload according to
// the 'name' ($viewValue) substring entered by the user
return filteredArray; // <- no need to pipe a filter in the template anymore
}
);
};
I have a checkbox, like:
<input type="checkbox" ng-model="isPreCheckIn" />
I'm getting isPreCheckin (boolean) from a service which uses $q and either returns from the server or localStorage (if it exists).
The call in the controller looks like:
deviceSettings.canCheckIn().then(function (canCheckIn) {
$scope.isPreCheckin = !canCheckIn ? true : false;
});
And deviceSettings.canCheckIn looks like:
function canCheckIn() {
var dfrd = $q.defer();
LoadSettings().then(function (success) {
return dfrd.resolve(localStorage.canCheckIn);
});
return dfrd.promise;
};
So, on first page load, the checkbox doesn't bind correctly to isPreCheckIn; in fact, if I do a {{isPreCheckIn}}, it doesn't either. If I switch off of that page and go back, it works.
It appears that canCheckIn is outside of angular, based on that assumption, you need to wrap your assignment within $scope.apply:
deviceSettings.canCheckIn().then(function (canCheckIn) {
$scope.$apply(function(){
$scope.isPreCheckin = !canCheckIn ? true : false;
});
});
This tells angular to recognize the changes on your $scope and apply to your UI.
I think you should wrap the following in a $apply:
function canCheckIn() {
var dfrd = $q.defer();
LoadSettings().then(function (success) {
scope.$apply(function() {
dfrd.resolve(localStorage.canCheckIn);
}
});
return dfrd.promise;
};
It sounds like a timing issue. You may need to put a resolve clause in your route to give this call time to run and then pass in the result as a DI value. Without knowing which router you are using it is impossible to give you an accurate answer, but you might look at the video on egghead.io regarding routes and resolve.
All over my code I have things like:
SearchModel.findAll($scope.report).then(function (xhr) {
$scope.searchResults= xhr.data;
});
is there anyway to just automagically assign the searchResults variable to the view after the request is done. Seems like there should be if not...
Promises are only resolved during a $digest cycle, so this should "automagically" update.
It really is that easy!
To prove it I made a fiddle that simulates a server response using a service and assign the data to a scope. The dom will automatically display the data.
service.get().then(function(data) {
$scope.data = data;
});
Hope this helped!
If you are dealing with a promise that returns an array, you can use a helper function like this:
app.factory('PromiseList', function() {
return function(promise, error) {
var list = [];
promise.then(function (result) {
angular.copy(result, list); # or in your case .copy(result.data, list)
}, error);
return list;
}
});
Then in your code, do:
$scope.searchResults = PromiseList(SearchModel.findAll($scope.report))
You can also use this solution with other kinds of objects, however not with strings or numbers as they are immutable objects in JavaScript.
If you are only developing for an old version of AngularJS (think pre-1.2), you can also just pass the promise to the template and it will be unwrapped automatically.
I am creating a simple CRUD using AngularJS and facing a minor issue. While editing a form I am getting json string returned by server in model and I have created my custom directive to format that date. It formats the date but scope is not applied and when I submit the form I get the old json date in alert.
MY Js goes like this:
var myApp = angular.module('myApp', ['toaster']).controller('MyController',function($scope){
$scope.old = { ship_date : '/Date(1359743400000)/' };
$scope.UpdateShipment = function () {
alert($scope.old.ship_date);
}
}).directive('formatDate', function () {
return {
require: 'ngModel',
scope : {'ngModel' : '='},
link: function (scope, element, attr, ngModelController) {
ngModelController.$formatters.unshift(function (valueFromModel) {
if (angular.isUndefined(valueFromModel)) {
return valueFromModel;
}
var date = new Date(parseInt(valueFromModel.substr(6)));
console.log(valueFromModel);
return date.toLocaleDateString();
});
}
};
});
View:
<div ng-app="myApp">
<div ng-controller="MyController">
<input type="text" ng-model="old.ship_date" /> <form ng-submit="UpdateShipment()">
<input type="text" format-Date ng-model="old.ship_date" />
<input type="submit" id="submit" value="Save" />
</form>
</div>
</div>
PLEASE HELP , MANY THANKS.
I had similiar issues, but i handled it in $http interceptor. You can transform the date strings into javascript objects. No need for custom directive. I think it is a lot cleaner approach.
Here is example implementation:
http://aboutcode.net/2013/07/27/json-date-parsing-angularjs.html
app.config(["$httpProvider", function ($httpProvider) {
$httpProvider.defaults.transformResponse.push(function(responseData){
convertDateStringsToDates(responseData);
return responseData;
});
}]);
All previous Answers about you needing to call $scope.apply() are correct, but as you have seen it can throw an error when another digest/apply is already in progress so to call it safely use this
if(!$scope.$$phase){$scope.$apply();}
this will only call it if it is not in progress and hopefully might fix your issue.
Use $scope.$apply() when your operating a function outside the angular way.
Try after the ngModelController({...}) function add $scope.$apply();
You need to use $scope.$apply. Refer this post about modify scope inside directive.
just remove the scope-defintion from your directive
enter code here
http://jsfiddle.net/2vxCA/
To build off of the answer from 'stride', you can also implement this using an interceptor. There's a few critical change you need to make when implementing it as an interceptor.
First, your .config section would look like this:
.config([
$httpProvider", function($httpProvider) {
$httpProvider.interceptors.push("httpInterceptorTransformResponseService");
}
])
With that in place, you would build a factory service to intercept and transform the data similar to the example from http://aboutcode.net/2013/07/27/json-date-parsing-angularjs.html. The regex is somewhat arbitrary and depending on the data you're actually retrieving via your requests, you may want/need to change it.
(function () {
"use strict";
angular.module("yourAppNameHere")
.factory("httpInterceptorTransformResponseService",
[
function () {
// Purpose: This interceptor is intended to convert Json strings that match the ISO8601 date format into DateTime objects.
// This is necessary in many cases because there is not an object type embedded into serialized Json that represents a DateTime object.
// There are numerous variations of this regex. Choose the one that works best for you, based on what data you expect
var regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,})(Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;
function convertDateStringsToDates(input) {
// Ignore things that aren't objects.
if (typeof input !== "object")
return;
for (var key in input) {
if (!input.hasOwnProperty(key))
continue;
var value = input[key];
var match;
// Check for string properties which look like dates.
if (typeof value === "string" && (match = value.match(regexIso8601))) {
var milliseconds = Date.parse(match[0])
if (!isNaN(milliseconds)) {
input[key] = new Date(milliseconds);
}
} else if (typeof value === "object") {
// Recurse into object
convertDateStringsToDates(value);
}
}
}
// declare the service
var transformResponseService = {
response: function (response) {
// convert all parsable date strings returned from the data into Date objects
convertDateStringsToDates(response.data);
return response;
}
};
return transformResponseService;
}
]);
})();
CRITICAL PART: Notice near the end where the service is declared and the response for the transform is declared. The version above for the interceptor is called as follows:
convertDateStringsToDates(response.data);
and NOT
convertDateStringsToDates(response);
If you send response instead of response.data to the function when using an interceptor, what happens is that it will parse EVERYTHING that comes back from the entire http request. This means that in addition to the data it retrieves, it will also apply this to things like the http configuration, headers, etc.
I ran into an issue where it was running the regex against the config section of the http request. Because it's a recursive service and objects are treated like references, it entered an infinite loop and would overflow the stack. It's highly likely that you will only want to apply this to the data that's returned, not the configuration, headers, etc.
In this case does anyone know how to update $scope.list with specific "id" with the code below after it successfully sent a post request to Recheck services it query again and reload all the DOM. I want to know how can we make a change only to specific element. Thanks!
function TrackingList($scope, List, Recheck) {
$scope.list = List.query();
$scope.update = function(id) {
Recheck.save({id: id}, function(){
$scope.list = List.query();
});
}
}
#mark-rajcok is correct, it is unnecessary to requery your list. Your restful service should be returning the object that contains the updates. It should match the structure of your Recheck object. Unless you have something else to do, your success function is unnecessary.