Why angularJS´s ui-view breaks Bootstrap Formhelpe

2019-08-04 05:52发布

问题:

I'am using Angular JS for a multipage registration site. (With this great tutorial: http://scotch.io/tutorials/javascript/angularjs-multi-step-form-using-ui-router)

I now want to use the Bootstrap Formhelper country picker in one of the pages (http://bootstrapformhelpers.com/country/#jquery-plugins) like this:

<div ui-view>
    ...
    <div class="bfh-selectbox bfh-countries" data-country="AT" data-flags="true"></div>
    ...
</div>

But it doesn't work. The country picker doesn't show up (height: 0).

If i put the country picker outside of de ui-view div the picker works. But i want to have it inside the ui-view div:

<div class="bfh-selectbox bfh-countries" data-country="AT" data-flags="true"></div>
<div ui-view>
   ...
</div>

For some reason the angularJS directive ui-view is breaking the country picker.

Has anyone an idea why this happens?

Best regards Yannick

EDIT:

My imports:

<link rel="stylesheet" href="assets/css/bootstrap.min.css">
<link rel="stylesheet" href="style.css">
<script src="assets/js/angular.js"></script>
<script src="assets/js/angular-animate.js"></script>
<script src="assets/js/angular-ui-router.min.js"></script>
<script src="assets/js/angular-animate.js"></script>
<script src="app.js"></script>
<!-- Bootstrap -->
<script src="assets/js/jquery-1.11.1.min.js"></script>
<link rel="stylesheet" href="assets/css/bootstrap-theme.min.css">
<script src="assets/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="assets/css/bootstrap-formhelpers.min.css">
<script src="assets/js/bootstrap-formhelpers.min.js"></script>
<script src="assets/js/bootstrap-formhelpers-states.js"></script>
<script src="assets/js/bootstrap-formhelpers-countries.de-DE.js"></script>
<script src="assets/js/bootstrap-formhelpers-countries.js"></script>

EDIT2:

i now use a directive for the initialization. strange think is that this code works:

angular.module("formApp").directive('nationpicker', function ($parse) {
return {
    restrict: 'A',
    replace: false,
    transclude: false,
    link: function (scope, element, attrs) {
        element.append('<select id="countries1" class="form-control"></select>');
        $('#countries1').bfhcountries({flags:true})
    }
};

But this code doesnt work:

angular.module("formApp").directive('nationpicker', function ($parse) {
return {
    restrict: 'A',
    replace: false,
    transclude: false,
    link: function (scope, element, attrs) {
        element.append('<div id="country" class="bfh-selectbox bfh-countries" data-country="AT" data-flags="true"></div>');
        $('#country').bfhcountries({flags:true})
    }
};

In fact these are only the different examples from the Bootstrap Formhelper (see link at the top)

the first one is the 3. example the second code which doesn't work is from my prefered 4.Example.

Can anyone please tell me why this is not working for the 4.Example?

回答1:

Thank you for reaching out to me via email. I have looked over your code and made a few changes to resolve the problem. For sake of others who may have a similar problem I will follow up here so that we can close this issue.

The problem is not with the ui-view directive, but in the nationpicker directive. Here is the updated directive code:

angular.module("formApp").directive('nationpicker', function ($parse) {

    // The directive has been rewritten to work properly
    return {
        restrict: 'A', // It is an attribute
        require: '?ngModel', // It uses ng-model binding
        scope: {
          ngModel: '='
        },
        link: function (scope, elem, attrs, ctrl) {
            // Add the required classes
            elem.addClass('bfh-countries');
            elem.addClass('bfh-selectbox');

            // First we initialize the selectbox with the desired options
            elem.bfhselectbox({
                filter: (elem.attr('data-filter') == 'true') ? true : false
            }).on('change.bfhselectbox', function() {
                // Listen for the change event and update the bound model
                return scope.$apply(function () {
                    return scope.ngModel = elem.val();
                });
            });

            // Initialize the countries with the desired options
            return elem.bfhcountries({
                flags: (elem.attr('data-flags') == 'true') ? true : false,
                country: scope.ngModel || 'AT'
            });
        }
    };
});

In order for this to work a couple of changes had to be made to your element in the HTML

<div nationpicker id="country" data-flags="true" data-filter="true" ng-model="nation"></div>

Notice that the classes are no longer included. This is handled by the directive now. I also opted to bind the country in the controller scope rather than using the data-country attribute. This was as simple as adding $scope.nation = 'AT'; in the controller.

I also had to move all of your angular related script tags below the bootstrap and form-helper scripts.

<!-- Bootstrap -->
<script src="assets/js/jquery-1.11.1.min.js"></script>
<link rel="stylesheet" href="assets/css/bootstrap-theme.min.css">
<script src="assets/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="assets/css/bootstrap-formhelpers.min.css">
<script src="assets/js/bootstrap-formhelpers.min.js"></script>
<script src="assets/js/bootstrap-formhelpers-states.js"></script>
<script src="assets/js/bootstrap-formhelpers-countries.de-DE.js"></script>
<!--<script src="assets/js/bootstrap-formhelpers-countries.js"></script>-->


<!-- moved all angular related scripts to here -->
<script src="assets/js/angular.js"></script>
<script src="assets/js/angular-animate.js"></script>
<script src="assets/js/angular-ui-router.min.js"></script>
<script src="assets/js/angular-animate.js"></script>
<script src="app.js"></script>
<script src="directive.js"></script>

I will also follow up with you via email and I hope this helps you continue with your project.



回答2:

To have two-way binding work you can modify directive code as below:

myApp.directive('nationpicker', function ($parse) {
    return {
        restrict: 'A', // It is an attribute
        require: '?ngModel', // It uses ng-model binding
        scope: {
            ngModel: '='
        },
        link: function (scope, elem, attrs, ctrl) {
            // Add the required classes
            elem.addClass('bfh-countries');
            elem.addClass('bfh-selectbox');
            // First we initialize the selectbox with the desired options
            elem.bfhselectbox({
                filter: (elem.attr('data-filter') == 'true') ? true : false
            }).on('change.bfhselectbox', function () {
                // Listen for the change event and update the bound model
                return scope.$apply(function () {
                    return scope.ngModel = elem.val();
                });
            });
            scope.$watch('ngModel', function (newVal, oldVal) {
                if (newVal != oldVal) {
                    elem.find("input").val(newVal);
                    elem.find("span.bfh-selectbox-option").empty().append('<i class="glyphicon bfh-flag-' + newVal + '"></i>' + BFHCountriesList[newVal]);
                }
            });


            // Initialize the countries with the desired options
            return elem.bfhcountries({
                flags: (elem.attr('data-flags') == 'true') ? true : false,
                country: scope.ngModel
            });
        }
    };
});