0

Я новичок в Angularjs и желаю, чтобы я не реализовал анти-шаблон.Обработка событий модели в Angularjs

У меня есть Global controller, который получает модель через factory. Эта модель поставляет данные примерно в dozen directives, которые находятся внутри области глобального контроллера.

Одна из директив, которые находятся внутри глобального контроллера, является картой google и должна быть инициализирована после получения данных lat/long с сервера. Я объявляю о прибытии модели, используя $rootScope.$broadcast("$site:loaded") от функции .success() в глобальном контроллере, а затем я слушаю ее с использованием $rootScope.$on("$site:loaded") в директиве.

Я реализовал анти-шаблон или это A.O.K.? Сначала я подумал, что уже есть метод, который я мог бы использовать, чтобы «знать», когда прибыла модель, что-то вроде события Backbone onModelChanged.

Любые советы об этом или о чем-либо еще, что вы видите в моем коде? (Если он выглядит хорошо, я буду принимать ответы, которые так говорят, и немного объясните, почему это хорошо, если можно).

Вот код, начиная с базового шаблона:

<!-- Global controller implemented as global. --> 
<!DOCTYPE html> 
<!--[if lte IE 8]><html class="ie8"><![endif]--> 
<!--[if !IE]><!--><html ng-app="juniper" ng-controller="Global as glb" ng-model="glb.site"><!--<![endif]--> 
    <head lang="en-US"> 
     <base href="/ng-test/"> 
     <meta charset="utf-8" /> 
     <title></title> 

     {% include "DriverSideSiteBundle:Juniper:stylesheets.html.twig" %} 
     {% include "DriverSideSiteBundle:Juniper:javascript.html.twig" %} 

     {% block head %} 
     {% endblock %} 
    </head> 
    <body vocab="http://schema.org/" itemscope itemtype="http://schema.org/AutomotiveBusiness"> 
     {# 
      These twig files contain <jn-header> 
      The scaffolding HTML changes depending on whether the nav is above, below or not 
      part of the header 
     #} 
     {% include 'DriverSideSiteBundle:Juniper:foundation/header.html.twig' with template %} 

     <a href="/ng-test/somplace/">create angular 404 page to handle broken links.</a> 

     {# 
      These twig files contain <jn-sidebar> and <ng-view> 
      The scaffolding HTML changes depending on where the nav is and whether there are sidebars 
     #} 
     {% if template.content.columns == 2 %} 
      {% include 'DriverSideSiteBundle:Juniper:foundation/two-column.content.html.twig' with template %} 
     {% endif %} 

     {% if template.content == 1 %} 
      {% include 'DriverSideSiteBundle:Juniper:foundation/one-column.html.twig' with template %} 
     {% endif %} 

     {# Todo: minify and concatenate into one request. #} 
     {% javascripts 
      "@DriverSideSiteBundle/Resources/public/vendor/modernizr/modernizr.js" 
      "@DriverSideSiteBundle/Resources/public/vendor/angular/angular.js" 
      "@DriverSideSiteBundle/Resources/public/vendor/angular-route/angular-route.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/app.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/site.factory.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/global/global.controller.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/homepage/homepage.controller.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/site-description/site-description.controller.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/site-description/site-description.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/my-garage/my-garage.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/services/services.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/coupons/coupons.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/contact-info/contact-info.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/payments/payments.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/certifications/certifications.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/amenities/amenities.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/jn-map/jn-map.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/jn-nav/jn-nav.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/store-hours/store-hours.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/motorev-admin/motorev-admin.directive.js" 
     %} 
      {# "@DriverSideSiteBundle/Resources/public/juniper/js/libs/underscore.module.js" #}   
      {# "@DriverSideSiteBundle/Resources/public/juniper/js/header/header.directive.js" 
      "@DriverSideSiteBundle/Resources/public/juniper/js/footer/footer.directive.js" #} 
      <script src="{{ asset_url }}"></script> 
     {% endjavascripts %} 

     {# Depending on the layout, include this or that version of the angular templates during initial request #} 
     {% javascripts '@DriverSideSiteBundle/Resources/public/juniper/js/homepage/homepage.html' output='bundles/driversidesite/juniper/homepage/homepage.html'%} 
     {% endjavascripts %} 
     {% block angularTemplates %} 
     {% endblock %} 

    <script type="text/javascript"> 
     // Trigger foundation's js 
     jQuery(document).ready(function() { 
      $(document).foundation(); 
     }); 
    </script> 

    <!-- Replace key with dynamic key --> 
    <script type="text/javascript" 
     src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDyEYqzoex1QGMLK1YXDye8vIs0o-lQbLQ"> 
    </script> 
    </body> 
    </html> 

Site завод введен в глобальный контроллер, который вызывает метод getSite(:siteId) сайта. Этот метод делает запрос $ http. Возвращенные данные устанавливаются на фабрику Site; Я видел эту практику где-то и думал, что это выглядит хорошо.

/** 
* global.controller.js 
*/ 
(function() { 
    'use strict'; 

    var juniper = angular.module('juniper'); 

    juniper.controller('Global', [ '$rootScope', 'Site', function($rootScope, Site){ 
     var self = this; 

     // Assign the site model to this controller's `site` attr 
     this.site = Site; 
     // Use the site model's get method to retrieve the site info 
     var promise = this.site.getSite(1409); // Todo: Include the Id dynamically 


     promise 
      .success($.proxy(function(data, status, headers, config){ 
       // Bind to the Site obj 
       angular.extend(this, data.data); 

       // Broadcast when the data arrives so that other directives and controllers can do something when it arrives 
       $rootScope.$broadcast("$site:loaded"); 

      }, Site)) // Pass in the Site object for the data to bind to 
      .error(function(data, status, headers, config) { 
       var error = 'error'; 
       error += '/n data: ' + data; 
       error += '/n status: ' + status; 
       error += '/n headers: ' + headers; 
       error += '/n config: ' + config; 

       console.log(error); // Log error 
      }); 
    }]); 

}()); 

Site factory имеет объект сайта, который удерживает геттер и сеттер функции, а также все данные, возвращаемые с сервера:

/** 
* site.factory.js 
*/ 
(function() { 
    'use strict'; 

    var juniper = angular.module('juniper'); 

    juniper.factory('Site', function($http){ 

     /** 
     * Site obj is passed to whichever controller needs the site model. 
     * The site obj contains all the methods for getting/setting data as well 
     * as the data itself. 
     * 
     * In this case, the site obj is set to the global controller which passes 
     * a reference thru inheritance to all other controllers. 
     */ 
     var Site = { 

      /** 
      * Get the site 
      * siteId (int) is the integer id of the site to be retrieved 
      */ 
      getSite: function(siteId) { 

       var url = 'http://localhost:8000/api/site/' + siteId; 

       return $http({ 
        method: 'GET', 
        url: url 
       }); 
      } 
     }; 

     // Return the site obj. 
     // You'll need to call the site obj methods once assigning it to a controller's attr. 
     return Site; 

    }); 

}()); 

Так вот Глобальный контроллер, который содержит модель, получаемые в Сайт завода. Вот карта директивы и шаблон карты:

<!-- I didn't specify an ng-model. I didn't see a reason to insert an ng-model since the map directive uses data from the Global controller and nothing else. Is there a way for me to specify that this directive's model is just a portion of the Global model, like by glb.site.location here or by setting the `scope` in the directive declaration ...? Is there a value to doing either of these? --> 
<!-- Also didn't specify an ng-controller because I could stick the functions handling this directive's UI in the directive's link function. Is this smart practice, bad practice, or just "a practice"? --> 
<div class="Pod"> 
    <h3 class="Pod__Head show-for-small-only">Location</h3> 
    <div class="Pod__Body"> 
    <div id="map-canvas"></div> 
    </div> 
</div> 

Директива карты. Обратите внимание, что я слушаю трансляцию $ scope. $, Которая поступает от глобального контроллера. Я также установил scope:false что придает ему объем Глобального контроллера:

/** 
* jn-map.directives.js 
*/ 
(function() { 
    'use strict'; 

    // Create template-path variable for easy maintenance 
    var path = '/bundles/driversidesite/juniper/'; 

    var juniper = angular.module('juniper'); 

    juniper.directive("jnMap", function($rootScope) { 
     return { 
     restrict: "E", 
     transclude: false, // Tell where to transclude the element using the ng-transclude attr 
     templateUrl: path + 'jn-map/jn-map.html', 
     scope: false, 
     link: function(scope, elements, attrs) { 

      $rootScope.$on("$site:loaded", $.proxy(function(){ 
       var mapOptions = { 
        center: { 
        lat: parseFloat(this.site.locations[0].latitude), 
        lng: parseFloat(this.site.locations[0].longitude) 
        }, 
        zoom: 8 
       }; 
       var map = new google.maps.Map(document.getElementById('map-canvas'), 
        mapOptions); 
      }, scope.glb)); // Pass in the Global controller's model 
     } 
     } 
    }); 

}()); 

ответ

1

Это действительно выглядит как анти-шаблон для меня. Я просто добавлю только директиву карты в DOM, как только сайт будет доступен.

В контроллере:

promise.success(function(data) { 
    $scope.loadedSite = data; 
} 

В шаблоне:

<jn-map site="loadedSite" ng-if="loadedSite"></jn-map> 

директива будет иметь объем, содержащий сайт, и будет только когда-либо будет называться после того, как сайт загружается благодаря ng-if:

juniper.directive("jnMap", function($rootScope) { 
    return { 
    restrict: "E", 
    templateUrl: path + 'jn-map/jn-map.html', 
    scope: { 
     site: '=' 
    }, 
    link: function(scope, element, attrs) { 
      var mapOptions = { 
       center: { 
       lat: parseFloat(scope.site.locations[0].latitude), 
       lng: parseFloat(scope.site.locations[0].longitude) 
       }, 
       zoom: 8 
      }; 
      new google.maps.Map(element[0], mapOptions); 
      } 
    }; 
}); 

Обратите внимание, что эта директива отображает карту в di rective, а не в другом, несвязаном элементе.element - объект оболочки типа jQuery, а element[0] - это, таким образом, необработанный элемент DOM.

+0

Итак, сначала вы создадите свойство в области «Глобальный контроллер» под названием «loadSite.». Это создается, когда $ site obj поступает с сервера. Затем вы добавили свойство 'site' в' jn-map directive', для которого установлено 'loadedSite.' (часть' ng-if' проста). Затем в моей директиве 'jn-map' вы устанавливаете свойство' site' для работы с двусторонней привязкой ('' = "'), что делает ее доступной в функции ссылки. У меня все получилось? ... Должен ли я привязывать 'loadedSite' к' $ scope' или я могу привязать его к 'this'? (Я пробовал до сих пор, но не смог). И разве не так хорошо устанавливать «scope: false»? –

+0

(btw, ваше решение работает, и я планирую принять его, как только я его понимаю, так спасибо!) –

+0

Одна из причин, по которой я спрашиваю о привязке 'loadedSite' к' this', а не '$ scope', это то, что мне нравится используйте синтаксис «Контроллер как ...». Чтобы мои свойства модели были напечатаны на другие части страницы при использовании вашего ответа, я обнаружил, что мне нужно связать «данные» как с «этим», так и с «$ scope». Однако это кажется неуклюжим. –

Смежные вопросы