0

I am trying to use autocomplete jquery ui to insert a value into angularjs form. The problem is that when I send the data with $http the value that comes from autocomplete input does not update ng-model! I read some post about the using of $scope.apply() but i dont understand how to use it and where! This is the jquery code for autocomplete:

$(document).ready(function(){
        $("#tags").autocomplete({
             source: '/contabilita/include/autocomplete.asp',
             minChars: 3,
             autoFill: true,
             mustMatch:false,
             cacheLength: 1,
             select: function(event, ui) {
                event.preventDefault();
                $("#tags").val(ui.item.label);
                $("#selected-tags").val(ui.item.label);
                $("#PIVA").val(ui.item.piva);
                $("#CFISCA").val(ui.item.cf);
                $("#CAP").val(ui.item.cap);
                $("#CITY").val(ui.item.city);
                $("#INDIRIZZO").val(ui.item.indi).trigger('change');
                $("#INDIRIZZO").trigger('input');
                $("#PR").val(ui.item.pr).closest('.select-wrapper')
                .find('li').removeClass("active").closest('.select-wrapper')
                .find('.select-dropdown').val(ui.item.pr)
                .find('span:contains(' + ui.item.pr + ')')
                .parent().addClass('selected active');
                $("#PR").select();
            },
        });

And this is the html:

    <div class="row">
        <div class="col s1">
          <label>N°:
            <input type="text" ng-model="reg.NUMERO">
          </label>
        </div>
        <div class="col s5">
          <label>Intestazione:
            <input ng-model="reg.INTESTAZIONE" id="tags" type="text">
          </label>
        </div>
<div class="row">
        <div class="col s4">
          <label>Indirizzo:
            <input ng-model="reg.INDIRIZZO" id="INDIRIZZO" type="text" ng-model-options="{updateOn: 'change input'}">
          </label>
        </div>
        <div class="col s3">
          <label>Città:
            <input ng-model="reg.CITY" id="CITY" type="text">
          </label>
        </div>
        <div class="col s3">
          <label>CAP:
            <input ng-model="reg.CAP" id="CAP" type="text">
          </label>
        </div>

This is the angular controller:

    .controller('gestFat', function($scope,$http,$location,$rootScope) {
    $scope.reg = {};
    $scope.reg.GIORNO = $rootScope.today;
    $scope.reg.RF = "RF01";
    $scope.reg.T_IVA = "10";
    console.log("cerco la fattura");
    var com = $location.search().COM_ID;


    $scope.stampaFat = function(){
            $http.post('/contabilita/include/insert_fattura.php', $scope.reg)
            .success(function(data){
                M.toast({html:'Intervento inserito...'+data, inDuration:1500});
                })
            .error(function(status){alert("Errore di connessione!" + status)});

    };
})

SOLUTION:

this is the final solution

.directive('ngautocomplete', function () {
return function link(scope, element, attributes) {
    element.autocomplete({
        source: '/contabilita/include/autocomplete.asp',
         minChars: 3,
         autoFill: true,
         mustMatch:false,
         cacheLength: 1,
         select: function(event, ui) {
            event.preventDefault();
                $("#tags").val(ui.item.label);
                $("#PIVA").val(ui.item.piva).trigger('input');;
                $("#CFISCA").val(ui.item.cf).trigger('input');;
                $("#CAP").val(ui.item.cap).trigger('input');;
                $("#CD").val(ui.item.cd).trigger('input');;
                $("#PEC").val(ui.item.pec).trigger('input');;
                $("#CITY").val(ui.item.city).trigger('input');;
                $("#INDIRIZZO").val(ui.item.indi).trigger('input');
                $("#NAZIONE").val(ui.item.nazione).closest('.select-wrapper').find('li').removeClass("active").closest('.select-wrapper').find('.select-dropdown').val(ui.item.nazione).find('span:contains(' + ui.item.nazione + ')').parent().addClass('selected active');
                $("#NAZIONE").select().trigger('change');;
                $("#PR").val(ui.item.pr).closest('.select-wrapper').find('li').removeClass("active").closest('.select-wrapper').find('.select-dropdown').val(ui.item.pr).find('span:contains(' + ui.item.pr + ')').parent().addClass('selected active');
                $("#PR").select().trigger('change');;
            }
            });
};

})

Community
  • 1
  • 1
DigitalXP
  • 179
  • 5
  • 18
  • can you please put the html part of the code, also the $http part would be better! – Muho Dec 17 '18 at 14:07
  • did you try to write $scope.$apply(); just after $("#PR").select(); ? – Muho Dec 17 '18 at 14:46
  • Yes, but obiuvsly dont work because is jquery code, not angular – DigitalXP Dec 17 '18 at 14:49
  • The `.success` and `.then` methods are deprecated and should not be used in new code. See [Why are AngularJS $http success/error methods deprecated?](https://stackoverflow.com/questions/35329384/why-are-angularjs-http-success-error-methods-deprecated-removed-from-v1-6/35331339#35331339) – georgeawg Dec 17 '18 at 17:50

3 Answers3

0

AngularJS has a "digest cycle" which is roughly analogous to the browser's event loop. Each cycle, Angular decides what parts of the DOM need updating. Your jQuery code does not reference Angular in any way, so it has no way of knowing your autocomplete data has changed, or that the screen needs to update.

The general way to solve this is to write your jQuery code inside an Angular-based function, like a component or directive. There are many tutorials on how to do this. This is also why the $scope.$apply() thing comes up, that's another way of telling Angular that an update needs to happen.

I suggest you create a directive. Within that directive you'll have a reference to a jQuery-wrapped version of the current element, and you can call your autocomplete function on that. Then use the directive in the #tags element.

I pulled this from an old project. It's a bit antiquated, but it should show the idea. Reference those angular docs for the real deal.

myApp.directive('autocomplete', function () {
    return function link(scope, element, attributes) {
        element.autocomplete({/* options here */});
    };
});

Then, in your html:

<div id="tags" autocomplete></div>
Chris
  • 6,805
  • 3
  • 35
  • 50
  • Return this error: TypeError: element.autocomplete is not a function at http://admin-pc/js/app.js:1001:17: element.autocomplete({ – DigitalXP Dec 17 '18 at 15:15
  • Yes, you need to make sure that the autocomplete plugin or whatever you're using is loaded before this code runs, and you may need to wrap `element` in some way. I'm providing an explanation and a technique, not line-by-line debugging. – Chris Dec 17 '18 at 15:47
0

You should wrap the autocomplete in a directive because i see it makes many changes to the ui outside of the angular application and I think many of this changes affect the html generated by angular.

    angular.directive(‘gest-fat-autocomplete’, function() {
        return {
            require: 'ngModel',
            controller: function($http, $location, $rootScope) {
                this.reg = {};
                this.reg.GIORNO = $rootScope.today;
                this.reg.RF = "RF01";
                this.reg.T_IVA = "10";
                console.log("cerco la fattura");
                var com = $location.search().COM_ID;


                this.stampaFat = function() {
                    $http.post('/contabilita/include/insert_fattura.php', this.reg)
                        .success(function(data) {
                            M.toast({
                                html: 'Intervento inserito...' + data,
                                inDuration: 1500
                            });
                        })
                        .error(function(status) {
                            alert("Errore di connessione!" + status)
                        });

                };
            }

            link: function(scope, element, attr, ctrl) {
                element.autocomplete({
                        source: '/contabilita/include/autocomplete.asp',
                        minChars: 3,
                        autoFill: true,
                        mustMatch: false,
                        cacheLength: 1,
                        select: function(event, ui) {
                            event.preventDefault();
                            scope.$applyAsync(function() {
                                // Update your model here
                                // use ctrl.reg to access this.reg in the controller
                            });
                        },


                    }
                };
            })

You still need to add a template to the directive. I didn't add it because is not clear to me the HTML of your app. You can access the controller instance in the template too so no need to use $scope inside the controller

Raulucco
  • 3,406
  • 1
  • 21
  • 27
  • The `.success` and `.then` methods are deprecated and should not be used in new code. See [Why are AngularJS $http success/error methods deprecated?](https://stackoverflow.com/questions/35329384/why-are-angularjs-http-success-error-methods-deprecated-removed-from-v1-6/35331339#35331339) – georgeawg Dec 17 '18 at 17:50
  • @DigitalXP you can intertpolate values from the scope in the directive into the template with `{{propertyNameInScope}}` syntax – Raulucco Dec 17 '18 at 17:58
  • @georgeawg I wanted to answer his question, which is about updating the scope. – Raulucco Dec 17 '18 at 18:03
0

To integrate an external library with AngularJS ng-model use a custom directive:

app.directive("xngAutocomplete", function() {
     return {
         require: "ngModel",
         link: postLink,
     };
     function postLink(scope,elem,attrs,ngModel) {
         ̶$̶(̶d̶o̶c̶u̶m̶e̶n̶t̶)̶.̶r̶e̶a̶d̶y̶(̶f̶u̶n̶c̶t̶i̶o̶n̶(̶)̶{̶ ̶$̶(̶"̶#̶t̶a̶g̶s̶"̶)̶.̶a̶u̶t̶o̶c̶o̶m̶p̶l̶e̶t̶e̶(̶{̶
         elem.autocomplete({
             source: '/contabilita/include/autocomplete.asp',
             minChars: 3,
             autoFill: true,
             mustMatch:false,
             cacheLength: 1,
             select: function(event, ui) {
                event.preventDefault();
                ̶$̶(̶"̶#̶t̶a̶g̶s̶"̶)̶.̶v̶a̶l̶(̶u̶i̶.̶i̶t̶e̶m̶.̶l̶a̶b̶e̶l̶)̶;̶
                ngModel.$setViewValue(ui.item.label);
            }
         });
     }
})

Usage:

<input xng-autocomplete ng-model="reg.INTESTAZIONE" id="tags" type="text">

The directive injects the code into elements which have an xng-autocomplete attribute. The directive exposes the jQuery element to the code as the second parameter of the postLink function.

For more information, see

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • The script dont generate errors, but dont fill the other input ng-model – DigitalXP Dec 17 '18 at 15:21
  • How I could change other input?! – DigitalXP Dec 17 '18 at 15:28
  • I am glad that the answer has shown you how to get jquery-ui-autocomplete autocomplete to update the ng-model. If you need help with something else, [ask another question](https://stackoverflow.com/questions/ask). – georgeawg Dec 17 '18 at 15:38
  • I need to update each input and not only tags... the json result obtain from call contain name, company, city etc. – DigitalXP Dec 17 '18 at 16:41
  • See [Etiquette for Russian Doll Questions](https://meta.stackexchange.com/questions/188625/etiquette-for-russian-doll-questions). – georgeawg Dec 17 '18 at 17:24