javascript - angularjs mysql




AngularJS: تهيئة الخدمة بالبيانات غير المتزامنة (7)

أسهل طريقة لجلب أي دليل استخدام دليل ng-init.

ما عليك سوى وضع نطاق div لـ ng-init حيث تريد جلب بيانات init

index.html و

<div class="frame" ng-init="init()">
    <div class="bit-1">
      <div class="field p-r">
        <label ng-show="regi_step2.address" class="show-hide c-t-1 ng-hide" style="">Country</label>
        <select class="form-control w-100" ng-model="country" name="country" id="country" ng-options="item.name for item in countries" ng-change="stateChanged()" >
        </select>
        <textarea class="form-control w-100" ng-model="regi_step2.address" placeholder="Address" name="address" id="address" ng-required="true" style=""></textarea>
      </div>
    </div>
  </div>

index.js

$scope.init=function(){
    $http({method:'GET',url:'/countries/countries.json'}).success(function(data){
      alert();
           $scope.countries = data;
    });
  };

ملاحظة: يمكنك استخدام هذه المنهجية إذا لم يكن لديك نفس الرمز أكثر من مكان واحد.

لدي خدمة AngularJS التي أرغب في تهيئتها باستخدام بعض البيانات غير المتزامنة. شيء من هذا القبيل:

myModule.service('MyService', function($http) {
    var myData = null;

    $http.get('data.json').success(function (data) {
        myData = data;
    });

    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

من الواضح أن هذا لن ينجح لأنه إذا حاول شيء ما استدعاء doStuff() قبل أن يعود myData سأحصل على استثناء مؤشر فارغة. بقدر ما استطيع ان اقول من قراءة بعض الأسئلة الأخرى here here لدي بعض الخيارات ، ولكن أيا منها لا يبدو نظيفا جدا (ربما أفتقد شيئا):

خدمة الإعداد مع "تشغيل"

عند إعداد تطبيقي ، افعل ذلك:

myApp.run(function ($http, MyService) {
    $http.get('data.json').success(function (data) {
        MyService.setData(data);
    });
});

ثم ستبدو خدمتي هكذا:

myModule.service('MyService', function() {
    var myData = null;
    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

هذا يعمل بعض الوقت ولكن إذا كانت البيانات غير المتزامنة تستغرق وقتًا أطول مما تتطلبه كل شيء doStuff() تهيئته ، أحصل على استثناء فارغ عند استدعاء doStuff()

استخدام الكائنات الوعد

هذا من المحتمل أن يعمل. الجانب السلبي الوحيد في كل مكان أسميه MyService سيكون عليّ أن أعرف أن doStuff () يعيد الوعد ، وكل الكود سيكون عندنا للتفاعل مع الوعد. أفضل بدلاً من الانتظار حتى تعود myData قبل تحميل التطبيق الخاص بي.

Bootstrap اليدوي

angular.element(document).ready(function() {
    $.getJSON("data.json", function (data) {
       // can't initialize the data here because the service doesn't exist yet
       angular.bootstrap(document);
       // too late to initialize here because something may have already
       // tried to call doStuff() and would have got a null pointer exception
    });
});

Global Javascript Var يمكنني إرسال JSON مباشرة إلى متغير Javascript عام:

HTML:

<script type="text/javascript" src="data.js"></script>

data.js:

var dataForMyService = { 
// myData here
};

ثم سيكون متاحًا عند تهيئة MyService :

myModule.service('MyService', function() {
    var myData = dataForMyService;
    return {
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

هذا من شأنه أن يعمل أيضا ، ولكن بعد ذلك لدي متغير جافا سكريبت العالمي الذي رائحة سيئة.

هل هذه خياراتي الوحيدة؟ هل أحد هذه الخيارات أفضل من الآخرين؟ أعلم أن هذا سؤال طويل جدًا ، لكنني أردت أن أثبت أنني حاولت استكشاف كل الخيارات المتاحة أمامي. أي توجيه سيكون موضع تقدير كبير.


استنادًا إلى حل مارتن أتكينز ، نقدم لك حل Angry Angular Angular:

(function() {
  var initInjector = angular.injector(['ng']);
  var $http = initInjector.get('$http');
  $http.get('/config.json').then(
    function (response) {
      angular.module('config', []).constant('CONFIG', response.data);

      angular.element(document).ready(function() {
          angular.bootstrap(document, ['myApp']);
        });
    }
  );
})();

يستخدم هذا الحل وظيفة مجهول ذاتي التنفيذ للحصول على خدمة http $ ، وطلب التكوين ، وحقنه في ثابت يسمى CONFIG عندما يصبح متاحًا.

بعد أن ننتظر تمامًا ، ننتظر حتى يصبح المستند جاهزًا ، ثم نفتح تطبيق Angular.

هذا هو تحسين طفيف على حل مارتن ، والذي تأجيل إحضار التكوين حتى بعد المستند جاهز. بقدر ما أعرف ، ليس هناك سبب لتأخير استدعاء http دولار لذلك.

وحدة التجارب

ملاحظة: لقد اكتشفت أن هذا الحل لا يعمل بشكل جيد عند اختبار الوحدة عند تضمين الشفرة في ملف app.js والسبب في ذلك هو أن الشفرة الواردة أعلاه تعمل فورًا عند تحميل ملف JS. هذا يعني أن إطار الاختبار (ياسمين في حالتي) لا تتوفر له فرصة تقديم تطبيق وهمي لـ $http .

الحل الذي لم أكن راضيًا عنه تمامًا ، كان نقل هذا الرمز إلى ملف index.html بنا ، لذلك لا تراه البنية التحتية لاختبار وحدة Grunt / Karma / Jasmine.


لذلك وجدت حلا. لقد أنشأت خدمة angularJS ، وسنطلق عليها MyDataRepository وأنشأت وحدة لها. أقوم بعد ذلك بإعداد ملف javascript هذا من وحدة التحكم على جانب الخادم:

HTML:

<script src="path/myData.js"></script>

خادم الجانب:

@RequestMapping(value="path/myData.js", method=RequestMethod.GET)
public ResponseEntity<String> getMyDataRepositoryJS()
{
    // Populate data that I need into a Map
    Map<String, String> myData = new HashMap<String,String>();
    ...
    // Use Jackson to convert it to JSON
    ObjectMapper mapper = new ObjectMapper();
    String myDataStr = mapper.writeValueAsString(myData);

    // Then create a String that is my javascript file
    String myJS = "'use strict';" +
    "(function() {" +
    "var myDataModule = angular.module('myApp.myData', []);" +
    "myDataModule.service('MyDataRepository', function() {" +
        "var myData = "+myDataStr+";" +
        "return {" +
            "getData: function () {" +
                "return myData;" +
            "}" +
        "}" +
    "});" +
    "})();"

    // Now send it to the client:
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.add("Content-Type", "text/javascript");
    return new ResponseEntity<String>(myJS , responseHeaders, HttpStatus.OK);
}

يمكنني بعد ذلك حقن MyDataRepository أينما أحتاج إليها:

someOtherModule.service('MyOtherService', function(MyDataRepository) {
    var myData = MyDataRepository.getData();
    // Do what you have to do...
}

لقد عمل هذا جيدًا بالنسبة لي ، لكني منفتحة على أي تعليقات إذا كان لدى أي شخص أي. }


لقد استخدمت أسلوبًا مشابهًا للنهج الموصوف من قِبلXMLilley ، ولكن كنت أرغب في امتلاك القدرة على استخدام خدمات AngularJS مثل $http لتحميل التهيئة وإجراء المزيد من التهيئة دون استخدام واجهات برمجة التطبيقات ذات المستوى المنخفض أو jQuery.

لم يكن استخدام resolve على الطرق خيارًا أيضًا لأنني كنت أحتاج إلى أن تكون القيم متاحة module.config() عند بدء تشغيل التطبيق الخاص بي ، حتى في كتل module.config() .

لقد أنشأت تطبيق AngularJS صغيرًا يحمّل التهيئة ، ويعيّنها كثوابت على التطبيق الفعلي ، ويعمل على تشغيلها.

// define the module of your app
angular.module('MyApp', []);

// define the module of the bootstrap app
var bootstrapModule = angular.module('bootstrapModule', []);

// the bootstrapper service loads the config and bootstraps the specified app
bootstrapModule.factory('bootstrapper', function ($http, $log, $q) {
  return {
    bootstrap: function (appName) {
      var deferred = $q.defer();

      $http.get('/some/url')
        .success(function (config) {
          // set all returned values as constants on the app...
          var myApp = angular.module(appName);
          angular.forEach(config, function(value, key){
            myApp.constant(key, value);
          });
          // ...and bootstrap the actual app.
          angular.bootstrap(document, [appName]);
          deferred.resolve();
        })
        .error(function () {
          $log.warn('Could not initialize application, configuration could not be loaded.');
          deferred.reject();
        });

      return deferred.promise;
    }
  };
});

// create a div which is used as the root of the bootstrap app
var appContainer = document.createElement('div');

// in run() function you can now use the bootstrapper service and shutdown the bootstrapping app after initialization of your actual app
bootstrapModule.run(function (bootstrapper) {

  bootstrapper.bootstrap('MyApp').then(function () {
    // removing the container will destroy the bootstrap app
    appContainer.remove();
  });

});

// make sure the DOM is fully loaded before bootstrapping.
angular.element(document).ready(function() {
  angular.bootstrap(appContainer, ['bootstrapModule']);
});

شاهده في الإجراء (باستخدام $timeout بدلاً من $http ) هنا: http://plnkr.co/edit/FYznxP3xe8dxzwxs37hi?p=preview

تحديث

أوصي باستخدام النهج الموضح أدناه من قبل Martin Atkins و JBCP.

تحديث 2

لأنني كنت في حاجة إليها في مشاريع متعددة ، أصدرت للتو وحدة مناجم bower تهتم بهذا: https://github.com/philippd/angular-deferred-bootstrap

مثال لتحميل البيانات من النهاية الخلفية وتعيين ثابت يسمى APP_CONFIG على الوحدة النمطية AngularJS:

deferredBootstrapper.bootstrap({
  element: document.body,
  module: 'MyApp',
  resolve: {
    APP_CONFIG: function ($http) {
      return $http.get('/api/demo-config');
    }
  }
});

هل قمت بإلقاء نظرة على $routeProvider.when('/path',{ resolve:{...} ؟ يمكن أن تجعل الوعد يقترب من نظافة قليلا:

فضح وعد في خدمتك:

app.service('MyService', function($http) {
    var myData = null;

    var promise = $http.get('data.json').success(function (data) {
      myData = data;
    });

    return {
      promise:promise,
      setData: function (data) {
          myData = data;
      },
      doStuff: function () {
          return myData;//.getSomeData();
      }
    };
});

أضف resolve لمسار المسار الخاص بك:

app.config(function($routeProvider){
  $routeProvider
    .when('/',{controller:'MainCtrl',
    template:'<div>From MyService:<pre>{{data | json}}</pre></div>',
    resolve:{
      'MyServiceData':function(MyService){
        // MyServiceData will also be injectable in your controller, if you don't want this you could create a new promise with the $q service
        return MyService.promise;
      }
    }})
  }):

لن يتم إنشاء وحدة التحكم الخاصة بك قبل أن يتم حل كافة التبعيات:

app.controller('MainCtrl', function($scope,MyService) {
  console.log('Promise is now resolved: '+MyService.doStuff().data)
  $scope.data = MyService.doStuff();
});

لقد قدمت مثالا في plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview


يمكن لحالة "bootstrap اليدوي" الوصول إلى الخدمات الزاوي عن طريق إنشاء حاقن يدويًا قبل بدء التشغيل. سيكون هذا الحاقن الأولي قائمًا بذاته (لا يتم ربطه بأي عناصر) ويتضمن فقط مجموعة فرعية من الوحدات التي تم تحميلها. إذا كان كل ما تحتاج إليه هو خدمات Angular الأساسية ، يكفي تحميل ng فقط ، على ng :

angular.element(document).ready(
    function() {
        var initInjector = angular.injector(['ng']);
        var $http = initInjector.get('$http');
        $http.get('/config.json').then(
            function (response) {
               var config = response.data;
               // Add additional services/constants/variables to your app,
               // and then finally bootstrap it:
               angular.bootstrap(document, ['myApp']);
            }
        );
    }
);

يمكنك على سبيل المثال استخدام آلية module.constant لإتاحة البيانات لتطبيقك:

myApp.constant('myAppConfig', data);

يمكن الآن حقن myAppConfig مثل أي خدمة أخرى ، وخصوصًا أنها متوفرة أثناء مرحلة التكوين:

myApp.config(
    function (myAppConfig, someService) {
        someService.config(myAppConfig.someServiceConfig);
    }
);

أو ، بالنسبة إلى تطبيق أصغر ، يمكنك فقط إدخال التكوين العام مباشرةً في خدمتك ، على حساب نشر المعرفة حول تنسيق التهيئة في جميع أنحاء التطبيق.

وبطبيعة الحال ، بما أن عمليات التزامن ستقوم بحجب التمهيد الخاص بالتطبيق ، وبالتالي تحظر تجميع / ربط القالب ، فمن الحكمة استخدام توجيه ng-cloak لمنع ظهور قالب غير منشور أثناء العمل. يمكنك أيضًا تقديم نوع من مؤشرات التحميل في DOM ، من خلال توفير بعض HTML الذي يتم عرضه فقط حتى يتم تهيئة AngularJS:

<div ng-if="initialLoad">
    <!-- initialLoad never gets set, so this div vanishes as soon as Angular is done compiling -->
    <p>Loading the app.....</p>
</div>
<div ng-cloak>
    <!-- ng-cloak attribute is removed once the app is done bootstrapping -->
    <p>Done loading the app!</p>
</div>

قمت بإنشاء مثال كامل وعامل لهذا النهج على Plunker ، تحميل التكوين من ملف JSON ثابت كمثال.


يمكنك استخدام JSONP لتحميل بيانات الخدمة بشكل غير متزامن. سيتم إجراء طلب JSONP أثناء تحميل الصفحة الأولي وستكون النتائج متاحة قبل بدء تشغيل التطبيق. بهذه الطريقة لن تضطر إلى انتفاخ التوجيه الخاص بك مع يقرر زائدة عن الحاجة.

ستبدو html كما يلي:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>

function MyService {
  this.getData = function(){
    return   MyService.data;
  }
}
MyService.setData = function(data) {
  MyService.data = data;
}

angular.module('main')
.service('MyService', MyService)

</script>
<script src="/some_data.php?jsonp=MyService.setData"></script>






angular-promise