I'm trying to write an autocomplete directive that fetches data from the server using an $http request (without using any external plugins or scripts). Currently it works only with static data. Now, I know that I need to insert my $http request into the source: of the directive, but I can't find any good documentation on the subject.
http request
$http.post($scope.url, { "command": "list category() names"}).
success(function(data, status) {
$scope.status = status;
$scope.names = data;
})
.
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
Directive
app.directive('autoComplete', function($timeout) {
return function(scope, iElement, iAttrs) {
iElement.autocomplete({
source: scope[iAttrs.uiItems],
select: function() {
$timeout(function() {
iElement.trigger('input');
}, 0);
}
});
};
});
View
<input auto-complete ui-items="names" ng-init="manualcat='no category entered'" ng-model="manualcat">
So, how do I piece this all together correctly the Angular way?
I made an autocomplete directive and uploaded it to GitHub. It should also be able to handle data from an HTTP-Request.
Here's the demo: http://justgoscha.github.io/allmighty-autocomplete/
And here the documentation and repository: https://github.com/JustGoscha/allmighty-autocomplete
So basically you have to return a promise when you want to get data from an HTTP request, that gets resolved when the data is loaded. Therefore you have to inject the $qservice/directive/controller where you issue your HTTP Request.
Example:
function getMyHttpData(){
var deferred = $q.defer();
$http.jsonp(request).success(function(data){
// the promise gets resolved with the data from HTTP
deferred.resolve(data);
});
// return the promise
return deferred.promise;
}
I hope this helps.
Use angular-ui-bootstrap's typehead.
It had great support for $http and promises.
Also, it doesn't include any JQuery at all, pure AngularJS.
(I always prefer using existing libraries and if they are missing something to open an issue or pull request, much better then creating your own again)
You need to write a controller with ng-change function in scope. In ng-change callback you do a call to server and update completions. Here is a stub (without $http as this is a plunk):
HTML
<!doctype html>
<html ng-app="plunker">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.4.0.js"></script>
<script src="example.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
<div class='container-fluid' ng-controller="TypeaheadCtrl">
<pre>Model: {{selected| json}}</pre>
<pre>{{states}}</pre>
<input type="text" ng-change="onedit()" ng-model="selected" typeahead="state for state in states | filter:$viewValue">
</div>
</body>
</html>
JS
angular.module('plunker', ['ui.bootstrap']);
function TypeaheadCtrl($scope) {
$scope.selected = undefined;
$scope.states = [];
$scope.onedit = function(){
$scope.states = [];
for(var i = 0; i < Math.floor((Math.random()*10)+1); i++){
var value = "";
for(var j = 0; j < i; j++){
value += j;
}
$scope.states.push(value);
}
}
}
the easiest way to do that in angular or angularjs without external modules or directives is using list and datalist HTML5. You just get a json and use ng-repeat for feeding the options in datalist. The json you can fetch it from ajax.
in this example:
ctrl.query is the query that you enter when you type.
ctrl.msg is the message that is showing in the placeholder
ctrl.dataList is the json fetched
then you can add filters and orderby in the ng-reapet
!! list and datalist id must have the same name !!
<input type="text" list="autocompleList" ng-model="ctrl.query" placeholder={{ctrl.msg}}>
<datalist id="autocompleList">
<option ng-repeat="Ids in ctrl.dataList value={{Ids}} >
</datalist>
UPDATE : is native HTML5 but be carreful with the type browser and version.
check it out : https://caniuse.com/#search=datalist.
I found this link helpful
$scope.loadSkillTags = function (query) {
var data = {qData: query};
return SkillService.querySkills(data).then(function(response) {
return response.data;
});
};
Related
I have defined a controller like this :
app.controller("home", function ($scope, $http, $common) {
$http({
method: "GET",
url: '/posts/loadData'
}).then(function (response) {
//console.clear()
if (typeof response.data.posts != 'undefined') {
console.log(response.data.posts);
$scope.posts = $common.arrangePosts(response.data.posts);
}
});
})
and a service to arrange data :
app.service('$common', function ($timeout, $sce, $httpParamSerializerJQLike) {
var that = this;
this.arrangePosts = function (rawPosts) {
var posts = [];
$.each(rawPosts, function (key, value) {
posts.push({
postId: value.postId,
postLink: '/post/' + that.cleanString(value.title) + '/' + value.postId,
title: value.title,
summary: $sce.trustAsHtml(value.summary)
});
});
return posts;
}
});
using values in html like this :
<div class="widget fullwidth post-single">
<h4 class="widget-title">Latest</h4>
<div class="widget-content">
<ul>
<li ng-repeat="post in posts">
<h4 class="list-title">{{post.title}}</h4>
{{post.summary}}
</li>
</ul>
</div>
</div>
Data coming from server in JSON form :
Object { postId="4", title="asdf", summary="<p>asdf</p>"}
but all the html tags are printing on my page as it is (like a text) in summary.
In many SO posts people suggested to use $sce.trustAsHtml but its not working for me. Please suggest anyway to solve my problem.
Any help will be appreciated..!!
have you tried this?
<div ng-bind-html='post.summary'></div>
You could solve this over a directive. Did you know, that you can use JQuery Lite inside AngularJS to manipulate the DOM?
Here a quick example:
angular.module("PostsDirective",[])
.directive("posts", function($sce){
return {
link: function($scope, $element, $attrs){
//the HTML you want to show
var post = "<div>hello world</div>";
var posts = [post,post,post,post];
//iterating through the list (_.each is a function from underscore.js)
_.each(posts, function(element){
//if you want to save the trusted html string in a var you can do this with getTrustedHtml
//see the docs
var safeHtml = $sce.getTrustedHtml($sce.trustAsHtml(element));
//and here you can use JQuery Lite. It appends the html string to the DOM
//$element refers to the directive element in the DOM
$element.append(safeHtml);
});
}
};
});
And the html
<posts></posts>
This also pretty nice for the readability for your HTML code. And you can use it everywhere on your page.
BTW:
As i can see, you get the HTML elements directly from a REST-Service. Why don't you get just the data and insert it into the ng-repeat? If you transfer all the HTML you get a pretty high overhead if you have loads of data.
This plnkr : https://plnkr.co/edit/BjETLN7rvQ1hNRIm51zG?p=preview binds content to three divs within loop : <div ng-repeat="id in ids">
src :
{ "content" : "divContent" , "id" : "r1" }
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="FetchCtrl">
<div ng-repeat="id in ids">
<div ng-bind="content" ></div>
</div>
</div>
</body>
</html>
// Example of how to call AngularJS $http service and
// process the returned promise
function FetchCtrl($scope, $http, $q) {
$scope.ids = ["r1", "r2", "r3"];
$scope.ids = $scope.ids.map(function(id){
var getString = 'http-hello1.html?id='+id
return $http.get(getString);
});
$scope.responses = [];
$q.all($scope.ids).then(function (values) {
var counter = 0;
values.map(function(m){
$scope.content = m.data.content;
})
})
}
But how bind the result of each get request to the specific div ?
Could add id : <div id="{{id}}" ng-bind="content" ></div> but this means I need to maintain a map of id,value entries ? Is there an idiomatic angularjs way to achieve this ?
I think a directive which dynamically fetches your content might be the answer for you.
angular.module('whateverYourModuleNameIs')
.directive('dynamicRow', ['$http', '$interval', dynamicRowDirectiveFn]);
function dynamicRowDirectiveFn($http, $interval) {
return {
restrict: "EA", // I guess this is your choice how it renders
scope: {
id: '=' // you could grab the id and use it in your request
},
link: function linkFn(scope, element, attrs) {
// Use $interval to repeatedly fetch content
var repeatFetchWhichReturnsAPromise = $interval(fetchNewContent, 60000 * 15) //Executes function every x milliseconds.
// Function that executes every time your interval occurs
function fetchNewContent() {
$http.get('urlYouNeedToFetch'+id).then(
fetchNewContentSucess, fetchNewContentError
);
}
function fetchNewContentSuccess(responseObject){
//This sets your new HTML based on promise success
element = responseObject.data;
}
function fetchNewContentError(responseObject){
//If its a bad request we probably either want to stop repeating
// You can choose to do something else
$interval.cancel(repeatFetchWhichReturnsAPromise);
}
}
}
}
So Instead of using $q.all(), Id recommend individually fetching the content based on a timer or specific trigger. The downside with $q.all() is that if one of the promises fail, they all fail.
In terms of knowing what specific URL the directive needs to fetch, you'll have to provide that information to the directive to be used.
This is a very rough example of a directive that you could write. The upside is that you don't have to worry about bind-unsafe-html or include ngSanitize, you are instead just resetting the value of element inside your link function.
As I don't have a better picture of what you are trying to accomplish from a feature/product standpoint I can only suggest this based on the info provided.
I have some serious troubles using typeahead in angular.js. The data is not populated even tough i can see in the function linked with the typehead attribute, that the objects are exiting
Relevant part of my controller:
angular.module('rechnungCtrl', ['rechnungService','kundeService'])
.controller('RechnungController', function(Rechnung, Kunde, $route, $routeParams, $location, $http) {
vm.searchKunde = function(val) {
Kunde.search(val)
.success(function(data) {
//When seting a breakpoint in chrome here i can see that the data is correctly loaded from the REST Service
return data;
});
}
Relevant part of my view:
<input type="text" class="form-control" id="kunde" placeholder="Kunde" typeahead="obj.vorname for obj in ctrl.searchKunde($viewValue)" ng-model="ctrl.selectedKunde">
Usage of the module
angular.module('EasyApp', ['ngBootbox', 'ui.bootstrap'])
And finally the relevant parts of index.html
<link href="app/views/bower_components/angular-bootstrap/ui-bootstrap-csp.css"></script>
<script src="app/views/bower_components/angular-bootstrap/ui-bootstrap.min.js"></script>
<script src="app/views/bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
I highly appreciate any help on this problem!!
BR,
Martin
The problem comes from the function vm.searchKunde. This function is not returning anything, therefore typeahead values are not populated. Try returning the promise, i.e. :
vm.searchKunde = function(val) {
return Kunde.search ...
}
I have one form, that should be submitted with everage POST, without ajax. Just after it's rendering.
As it is an exclusion, I decided to use some jquery inside controller.
So my form:
<form name="Form" id="externalForm" action="{{form.url}}" method="POST" ng-controller="ExternalFormCtrl">
<input type="hidden" name="{{k}}" value="{{v}}" ng-repeat="(k, v) in form.form">
</form>
Amd my controller
app.controller('ExternalFormCtrl', ["$scope", "DataTransfer", function ($scope, DataTransfer) {
$scope.form = DataTransfer.get();
$('#externalForm').submit();
}]);
In DataTransfer I receive form with fields I need to send to external resource.
When it will render template, I want immidiatelly submit form with jquery.
How to submit form after rendering my form?
How to solve this task more ellegant, with angular way?
UPD: DataTransfer factory
app.factory('DataTransfer', [function() {
var savedData = {}
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
}]);
You could use vanilla javascript
document.getElementById('externalForm').submit();
I've been stuggling with angularjs services ans resource for a while, and can't get what I'm missing.
Here is a fiddle of my example:
http://jsfiddle.net/spacm/bfUVj/47/
(code below details)
This fiddle attempts to load periods (2012-2013,2013-2014...) from a web resource, to fill a select.
The fiddle doesn't work, I expect the $scope.perriodeJson to be filled by the periodes service. The http get request runs, but I only get an empty object, and don't get any update of the promise I expect.
Thanx for pointing what's wrong in this code, and suggesting enhancements.
<div ng-app='mod_periodes'>
<div id="appChoixPeriode" ng-controller='CtrlChoixPeriode'>
test:{{test}}<hr>
json periodes:{{periodeJson}}<hr>
Select periode:
<select ng-model="periodeSelect" ng-options="item.name for item in periodeSelectModel" ng-change="changePeriodeUtilisee()"></select>
<button type='button' ng-click='resetDefaultPeriode()'>G</button>
</div><!--appChoixPeriode-->
</div>
angular.module('mod_periodes', ['ngResource']);
angular.module('mod_periodes').factory('periodes', function($resource) {
var res = $resource('http://emm.menard.free.fr/test/fakereq.html');
/*content returned:
{"status":"success",
"data":{
"periodes":[
{"id_periode":"1","name":"2012-2013","ts_debut":"2012-07-01 00:00:00","ts_fin":"2013-06-30 23:59:59"},
{id_periode":"2","name":"2013-2014","ts_debut":"2013-07-01 00:00:00","ts_fin":"2014-06-30 23:59:59"},
{"id_periode":"3","name":"2014-2015","ts_debut":"2014-07-01 00:00:00","ts_fin":"2015-06-30 23:59:59"}
],
"id_periode":"2",
"temps_periode_utilisee":"present"},
"messages":[]}
*/
return res;
});
angular.module('mod_periodes').controller('CtrlChoixPeriode',implCtrlChoixPeriode);
function implCtrlChoixPeriode($scope, $resource, periodes) {
$scope.test="angular works";
$scope.periodeJson=periodes.get(); //why dont I get anything in $scope.periodeJson??
//the http request runs and looks successful
//below this, what I intended to do once I retrieve data
/* $scope.periodeJson=periodes.get(function() {
$scope.periodeSelectModel=$scope.periodeJson.data.periodes;
$scope.periodeSelect=$scope.periodeSelectModel[1];
});*/
// $scope.periodeSelectModel=$scope.periodeJson.data.periodes;
// $scope.periodeSelect=$scope.periodeSelectModel[1];
}
As pointed by ranru, my problem came from cross-domain issues.
Here is the exemple i ran locally:
<html>
<head>
<title>example</title>
<meta charset='utf-8'>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular-resource.min.js"></script>
<script>
angular.module('mod_periodes', ['ngResource']);
angular.module('mod_periodes').factory('periodes', function($resource) {
////////// here the url domain must be the same as the one hosting this page
//var res = $resource('http://myserv/fakereq.html');
/////////////////////////////
/*content returned:
{"status":"success",
"data":{
"periodes":[
{"id_periode":"1","name":"2012-2013","ts_debut":"2012-07-01 00:00:00","ts_fin":"2013-06-30 23:59:59"},
{id_periode":"2","name":"2013-2014","ts_debut":"2013-07-01 00:00:00","ts_fin":"2014-06-30 23:59:59"},
{"id_periode":"3","name":"2014-2015","ts_debut":"2014-07-01 00:00:00","ts_fin":"2015-06-30 23:59:59"}
],
"id_periode":"2",
"temps_periode_utilisee":"present"},
"messages":[]}
*/
return res;
});
angular.module('mod_periodes').controller('CtrlChoixPeriode',implCtrlChoixPeriode);
function implCtrlChoixPeriode($scope, $resource, periodes) {
$scope.test="angular works";
//this way, different $scope vars are set when the resource is received
$scope.periodeJson=periodes.get(function() {
$scope.periodeSelectModel=$scope.periodeJson.data.periodes;
$scope.periodeSelect=$scope.periodeSelectModel[1];
});
}
</script>
</head>
<body>
<div ng-app='mod_periodes'>
<div id="appChoixPeriode" ng-controller='CtrlChoixPeriode'>
test:{{test}}<hr>
json periodes:{{periodeJson}}<hr>
Select periode:
<select ng-model="periodeSelect" ng-options="item.name for item in periodeSelectModel" ng-change="changePeriodeUtilisee()"></select>
</div><!--appChoixPeriode-->
</div>
</body>