AngularJS के साथ शुरुआत करना कठिन नहीं है। दस्तावेज़ीकरण वहां से कुछ सर्वश्रेष्ठ में से है और इसके ट्यूटोरियल काफी सरल हैं।
लेकिन चीजें मुश्किल हो जाती हैं जब आप प्रौद्योगिकियों को जोड़ना शुरू करते हैं।
यदि आप सीधे JavaScript के बजाय CoffeeScript का उपयोग कर रहे हैं, तो आपको प्रीप्रोसेसिंग चिंताओं को ध्यान में रखना होगा - साथ ही स्पष्ट सिंटैक्स अंतर। ये अपने आप में मामूली मुद्दे हैं, लेकिन क्या होगा यदि आप मिश्रण में Ruby on Rails, Jasmine और Karma डाल दें? यह आश्चर्यजनक रूप से पेचीदा हो जाता है।
यह ठीक वही स्टैक है जिसका हम इस ट्यूटोरियल में उपयोग करने जा रहे हैं। इसलिए नहीं कि हम सजा के भूखे हैं, बल्कि *क्योंकि यह उस तरह का सेटअप है जो आप वास्तविक दुनिया में देखेंगे।*
यह ट्यूटोरियल मानता है कि आप Rails के साथ सहज हैं, लेकिन जरूरी नहीं कि AngularJS के साथ।
एक बेस Rails ऐप बनाना
चूंकि इसमें इतनी सारी प्रौद्योगिकी परतें शामिल हैं, मैं एक सरल एप्लिकेशन बनाने जा रहा हूँ जो बमुश्किल कुछ करता है। हम रेस्तरां के लिए CRUD कार्यक्षमता स्थापित करेंगे - वास्तव में, सिर्फ CR भाग। -UD पाठक के लिए एक अभ्यास के रूप में छोड़ दिया गया है। ;-)
हम एप्लिकेशन को **Restauranteur** कहेंगे।
मैं यहां PostgreSQL और RSpec का उपयोग कर रहा हूँ, लेकिन DBMS और सर्वर-साइड टेस्टिंग फ्रेमवर्क महत्वपूर्ण नहीं हैं। आप जो चाहें उपयोग कर सकते हैं।
प्रारंभिक सेटअप
सबसे पहले प्रोजेक्ट बनाएं:
$ rails new restauranteur --database=postgresql --skip-test-unit
यदि आप Pow का उपयोग कर रहे हैं, तो अपने प्रोजेक्ट को Pow में जोड़ें:
$ ln -s /Users/jasonswett/projects/restauranteur ~/.pow/restauranteur
PostgreSQL डेटाबेस उपयोगकर्ता बनाएं:
$ createuser -P -s -e restauranteur
अपने जेमफाइल में RSpec जोड़ें:
# Gemfile gem "rspec-rails", "~> 2.14.0"
RSpec इंस्टॉल करें:
$ bundle install $ rails g rspec:install
डेटाबेस बनाएं:
$ rake db:create
Restaurant मॉडल बनाना
अब जब हमारे पास अपना प्रोजेक्ट और डेटाबेस बन गया है, तो आइए अपना पहला संसाधन बनाएं। Restaurant संसाधन में केवल एक विशेषता होगी: **name**, जो एक स्ट्रिंग है।
$ rails generate scaffold restaurant name:string
अब, बस इसके बारे में OCD होने के लिए, हम सुनिश्चित करेंगे कि रेस्तरां के नाम अद्वितीय हों।
# db/migrate/[timestamp]_create_restaurants.rb class CreateRestaurants < ActiveRecord::Migration def change create_table :restaurants do |t| t.string :name t.timestamps end # निम्नलिखित पंक्ति जोड़ें add_index :restaurants, :name, unique: true end end
माइग्रेशन चलाएं:
$ rake db:migrate
आइए कुछ स्पेक्स जोड़ें यह सत्यापित करने के लिए कि हम अमान्य रेस्तरां नहीं बना सकते। ध्यान दें कि अद्वितीय विफलता कच्ची त्रुटि देती है।
require 'spec_helper' describe Restaurant do before do @restaurant = Restaurant.new(name: "Momofuku") end subject { @restaurant } it { should respond_to(:name) } it { should be_valid } describe "when name is not present" do before { @restaurant.name = " " } it { should_not be_valid } end describe "when name is already taken" do before do restaurant_with_same_name = @restaurant.dup restaurant_with_same_name.name = @restaurant.name.upcase restaurant_with_same_name.save end it { should_not be_valid } end end
ये वैलिडेटर्स जोड़ने से स्पेक्स पास होंगे:
class Restaurant < ActiveRecord::Base validates :name, presence: true, uniqueness: { case_sensitive: false } end
हम अब आगे बढ़ने के लिए तैयार हैं।
मिश्रण में AngularJS लाना
एक बार में सब कुछ आप पर डालने के बजाय, मैं पहले AngularJS-Rails एप्लिकेशन के सबसे सरल "हैलो, वर्ल्ड" संस्करण को प्रदर्शित करना चाहूंगा और फिर उस पर हमारे रेस्तरां CRUD कार्यक्षमता का निर्माण करूंगा।
हमारे "हैलो, वर्ल्ड" पृष्ठ का किसी विशेष Rails संसाधन से जुड़ा होना आवश्यक नहीं है या होना चाहिए। इस कारण से, हम अपने AngularJS होम पेज को सर्व करने के लिए एक `StaticPagesController` बनाएंगे।
कंट्रोलर बनाएं
$ rails generate controller static_pages index
हमारा रूट रूट अभी सिर्फ "Welcome to Rails" पेज है। आइए इसे हमारे नए `StaticPagesController` के `index` एक्शन पर सेट करें:
# config/routes.rb Restauranteur::Application.routes.draw do # निम्नलिखित पंक्ति जोड़ें root 'static_pages#index' end
Angular डाउनलोड करें
- बाद में हमारे टेस्ट को ठीक से काम करने के लिए, हमें `angular-mocks.js` नामक एक फ़ाइल की आवश्यकता होगी। मुझे नहीं लगता कि Angular डॉक्स में इसका कोई उल्लेख है, लेकिन यह आवश्यक है।
- AngularJS ट्यूटोरियल में, डॉक्स नवीनतम ब्लीडिंग-एज संस्करण सूचीबद्ध करते हैं, लेकिन अगर मुझे सही याद है, तो मुझे नवीनतम संस्करण के लिए `angular.js` और `angular-mocks.js` के बीच संगतता के साथ समस्याएं थीं। मुझे पता है कि संस्करण 1.1.5 एक साथ काम करते थे, इसलिए भले ही वह नवीनतम स्थिर संस्करण नहीं है, वह वह संस्करण है जिसे मैं यहां सूचीबद्ध कर रहा हूँ। बेशक जैसे-जैसे समय बीतता है संगतता स्थिति शायद सुधरेगी।
`angular.js` और `angular-mocks.js` को code.angularjs.org से डाउनलोड करें और फ़ाइलों को `app/assets/javascripts` में ले जाएं।
$ wget https://code.angularjs.org/1.1.5/angular.js \ https://code.angularjs.org/1.1.5/angular-mocks.js $ mv angular* app/assets/javascripts
इसे एसेट पाइपलाइन में जोड़ें
अब हम अपने एप्लिकेशन को AngularJS फ़ाइल की आवश्यकता बताना चाहते हैं, और हम यह सुनिश्चित करना चाहते हैं कि यह उन अन्य फ़ाइलों से पहले लोड हो जो इस पर निर्भर करती हैं। (हम इन निर्भरताओं को प्रबंधित करने के लिए RequireJS जैसी किसी चीज़ का उपयोग कर सकते हैं, और शायद यही मैं एक प्रोडक्शन उत्पाद पर करूंगा, लेकिन इस ट्यूटोरियल के उद्देश्यों के लिए मैं प्रौद्योगिकी स्टैक को यथासंभव पतला रखना चाहता हूँ।)
नोट: Angular और Turbolinks एक दूसरे के साथ संघर्ष कर सकते हैं, इसलिए हम उन्हें यहां अक्षम करते हैं
// app/assets/javascripts/application.js // यह एक मेनिफेस्ट फ़ाइल है जिसे application.js में संकलित किया जाएगा, जिसमें नीचे सूचीबद्ध सभी फ़ाइलें शामिल होंगी। // // इस निर्देशिका, lib/assets/javascripts, vendor/assets/javascripts, // या वेंडर/assets/javascripts के प्लगइन्स, यदि कोई हो, में कोई भी JavaScript/Coffee फ़ाइल को सापेक्ष पथ का उपयोग करके यहां संदर्भित किया जा सकता है। // // सीधे यहां कोड जोड़ना उचित नहीं है, लेकिन यदि आप करते हैं, तो यह संकलित फ़ाइल के नीचे दिखाई देगा। // // समर्थित निर्देशों के बारे में विवरण के लिए Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) पढ़ें // //= require jquery //= require jquery_ujs // निम्नलिखित दो पंक्तियाँ जोड़ें //= require angular //= require main //= require_tree .
लेआउट सेट करें
हम ng-app और ng-view जोड़ेंगे, जो संकेत देते हैं कि हमारे पेज में एक Angular ऐप है। यह भी ध्यान दें कि Turbolinks के उल्लेख हटा दिए गए हैं।
<%= yield %>
Angular कंट्रोलर बनाना
पहले आइए अपने कंट्रोलर्स के लिए एक निर्देशिका बनाएं। आप इसे जो चाहें नाम दे सकते हैं।
$ mkdir -p app/assets/javascripts/angular/controllers
अब कंट्रोलर फ़ाइल ही बनाते हैं। मैं इस कंट्रोलर को "होम कंट्रोलर" कह रहा हूँ, और Angular में कन्वेंशन है कि आप अपने कंट्रोलर फ़ाइलनाम को `Ctrl` के साथ जोड़ते हैं। इस प्रकार हमारा फ़ाइलनाम `app/assets/javascripts/angular/controllers/HomeCtrl.js.coffee` होगा:
# app/assets/javascripts/angular/controllers/HomeCtrl.js.coffee @restauranteur.controller 'HomeCtrl', ['$scope', ($scope) -> # ध्यान दें कि यह कंट्रोलर बॉडी खाली है ]
Angular रूट जोड़ें
अब हम अपने `HomeCtrl` को हमारे "डिफ़ॉल्ट पेज" बनाने के लिए एक रूटिंग निर्देश जोड़ेंगे। यहाँ मैं `app/assets/javascripts/main.js.coffee` में अपनी रूटिंग परिभाषित कर रहा हूँ, लेकिन फिर मुझे नहीं लगता कि फ़ाइलनाम मायने रखता है।
# app/assets/javascripts/main.js.coffee # यह पंक्ति हमारे Angular ऐप से संबंधित है, न कि हमारे # HomeCtrl से विशेष रूप से। यह मूल रूप से है कि हम कैसे # Angular को हमारे एप्लिकेशन के अस्तित्व के बारे में बताते हैं। @restauranteur = angular.module('restauranteur', []) # यह रूटिंग निर्देश Angular को हमारे एप्लिकेशन के लिए डिफ़ॉल्ट # रूट के बारे में बताता है। "अन्यथा" शब्द यहाँ # कुछ अजीब लग सकता है, लेकिन यह अधिक समझ में आएगा जैसे ही हम अपने एप्लिकेशन में अधिक रूट जोड़ते हैं। @restauranteur.config(['$routeProvider', ($routeProvider) -> $routeProvider. otherwise({ templateUrl: '../templates/home.html', controller: 'HomeCtrl' }) ])
Angular टेम्पलेट जोड़ें
हम अपने Angular टेम्पलेट्स को रखने के लिए एक स्थान भी चाहेंगे। मैंने अपने को `public/templates` में रखने का फैसला किया। फिर से, आप उन्हें जहां चाहें रख सकते हैं।
mkdir public/templates
यदि हम कुछ मनमाने कंटेंट के साथ `public/templates/home.html` फ़ाइल बनाते हैं, तो हमें इसे ब्राउज़र में देखना चाहिए।
This is the home page.
अब, यदि आप `https://restauranteur.dev/` (या `https://localhost:3000/` यदि आप Pow का उपयोग नहीं कर रहे हैं) पर जाते हैं तो आपको `home.html` की सामग्री दिखाई देनी चाहिए।
डेटा बाइंडिंग का एक उदाहरण
यह *थोड़ा* दिलचस्प है, लेकिन शायद बहुत प्रभावशाली नहीं। आइए वास्तव में तार के पार कुछ भेजें। अपने `app/assets/angular/controllers/HomeCtrl.js.coffee` को इस तरह संपादित करें:
# app/assets/angular/controllers/HomeCtrl.js.coffee @restauranteur.controller 'HomeCtrl', ['$scope', ($scope) -> $scope.foo = 'bar' ]
यह Rails कंट्रोलर में `@foo = "bar"` कहने के समान है। हम `foo` को टेम्पलेट में डबल-ब्रेस सिंटैक्स का उपयोग करके प्लग कर सकते हैं जैसे:
Value of "foo": {{foo}}
इस बार असली के लिए करना
हमने पहले ही एक सरल हैलो वर्ल्ड ऐप बना लिया है। एक पूर्ण विकसित CRUD एप्लिकेशन बनाना बहुत कठिन नहीं है।
डेटाबेस को सीड करें
यदि हम डेटाबेस में कुछ रेस्तरां के साथ शुरू करते हैं तो हमारे रेस्तरां CRUD के साथ काम करना थोड़ा अधिक सार्थक होगा। यहाँ एक सीड फ़ाइल है जिसका आप उपयोग कर सकते हैं।
# db/seeds.rb Restaurant.create([ { name: "The French Laundry" }, { name: "Chez Panisse" }, { name: "Bouchon" }, { name: "Noma" }, { name: "Taco Bell" }, ])
rake db:seed
रेस्तरां इंडेक्स पेज बनाना
पहले आइए रेस्तरां के लिए एक टेम्पलेट फ़ोल्डर बनाएं:
mkdir public/templates/restaurants
पहला टेम्पलेट जो हम बनाएंगे वह इंडेक्स पेज है:
[index](/#) * {{ restaurant.name }}
मैं एक पल में बताऊंगा कि इन चीजों का क्या मतलब है। पहले आइए कंट्रोलर बनाएं:
# app/assets/javascripts/angular/controllers/RestaurantIndexCtrl.js.coffee @restauranteur.controller 'RestaurantIndexCtrl', ['$scope', '$location', '$http', ($scope, $location, $http) -> $scope.restaurants = [] $http.get('./restaurants.json').success((data) -> $scope.restaurants = data ) ]
अंत में, हम अपने रूटिंग कॉन्फ़िगरेशन को समायोजित करेंगे:
# app/assets/javascripts/main.js.coffee @restauranteur = angular.module('restauranteur', []) @restauranteur.config(['$routeProvider', ($routeProvider) -> $routeProvider. when('/restaurants', { templateUrl: '../templates/restaurants/index.html', controller: 'RestaurantIndexCtrl' }). otherwise({ templateUrl: '../templates/home.html', controller: 'HomeCtrl' }) ])
अब, अंत में, हम URI `/#/restaurants` पर जा सकते हैं और हमें अपने रेस्तरां की सूची देखने में सक्षम होना चाहिए। आगे बढ़ने से पहले आइए एक टेस्ट जोड़ें।
हमारा पहला टेस्ट जोड़ना
JS टेस्ट फ़ोल्डर जोड़ें:
mkdir spec/javascripts
टेस्ट लिखें:
# spec/javascripts/controllers_spec.js.coffee describe "Restauranteur controllers", -> beforeEach module("restauranteur") describe "RestaurantIndexCtrl", -> it "should set restaurants to an empty array", inject(($controller) -> scope = {} ctrl = $controller("RestaurantIndexCtrl", $scope: scope ) expect(scope.restaurants.length).toBe 0 )
कॉन्फ़िग जोड़ें:
// spec/javascripts/restauranteur.conf.js module.exports = function(config) { config.set({ basePath: '../..', frameworks: ['jasmine'], autoWatch: true, preprocessors: { '**/*.coffee': 'coffee' }, files: [ 'app/assets/javascripts/angular.js', 'app/assets/javascripts/angular-mocks.js', 'app/assets/javascripts/main.js.coffee', 'app/assets/javascripts/angular/controllers/RestaurantIndexCtrl.js.coffee', 'app/assets/javascripts/angular/*', 'spec/javascripts/*_spec.js.coffee' ] }); };
Karma इंस्टॉल करें और सर्वर शुरू करें:
sudo npm install -g karma sudo npm install -g karma-ng-scenario karma start spec/javascripts/restauranteur.conf.js
यदि आप `https://localhost:9876/` पर जाते हैं, तो हमारा टेस्ट चलेगा और सफल होगा। यदि आप टेस्ट को विफल होते देखना चाहते हैं, तो `expect(scope.restaurants.length).toBe 0` को `expect(scope.restaurants.length).toBe 1` में बदलें और टेस्ट को फिर से चलाएं।
इस टेस्ट की सार्थकता जो हमने अभी जोड़ी है, स्पष्ट रूप से संदिग्ध है, लेकिन मेरा इरादा यहां आपको यह पता लगाने के काम से बचाना है कि अपने Angular कोड को टेस्ट हार्नेस में कैसे लाया जाए। कुछ चीजें हैं, जैसे CoffeeScript प्रीप्रोसेसर और `angular-mocks.js` समावेश जो पूरी तरह से स्पष्ट नहीं हैं और मुझे सही करने के लिए कई घंटे लगे।
रेस्तरां पेज का निर्माण
आइए अब हमारे रेस्तरां इंडेक्स टेम्पलेट में एक अस्थायी समायोजन करें:
* {{restaurant.name}} ({{restaurant.id}})
यदि आप अब `/#/restaurants` पर फिर से जाते हैं, तो आप देखेंगे कि किसी भी रेस्तरां के पास उनकी IDs नहीं हैं। वे खाली क्यों हैं?
जब आप Rails 4 में स्कैफोल्डिंग जनरेट करते हैं, तो यह आपको कुछ `.jbuilder` फ़ाइलें देता है:
$ ls -1 app/views/restaurants/*.jbuilder app/views/restaurants/index.json.jbuilder app/views/restaurants/show.json.jbuilder
यदि आप `app/views/restaurants/index.json.jbuilder` खोलते हैं, तो आप इसे देखेंगे:
# app/views/restaurants/index.json.jbuilder json.array!(@restaurants) do |restaurant| json.extract! restaurant, :name json.url restaurant_url(restaurant, format: :json) end
जैसा कि आप देख सकते हैं, यह `:name` शामिल कर रहा है लेकिन `:id` नहीं। आइए इसे जोड़ें:
# app/views/restaurants/index.json.jbuilder json.array!(@restaurants) do |restaurant| json.extract! restaurant, :id, :name json.url restaurant_url(restaurant, format: :json) end
यदि आप फ़ाइल सेव करते हैं और `/#/restaurants` रीफ्रेश करते हैं, तो आपको IDs दिखाई देनी चाहिए।
अब आइए टेम्पलेट को वापस वैसे ही बदलें जैसे यह मूल रूप से था:
[index](/#) * {{ restaurant.name }}
आपने देखा होगा कि हम इन चीजों को `viewRestaurant()` नामक किसी चीज़ की ओर इशारा कर रहे हैं लेकिन हमने कभी वास्तव में `viewRestaurant()` नामक कुछ भी परिभाषित नहीं किया। आइए अब ऐसा करें:
# app/assets/javascripts/angular/controllers/RestaurantIndexCtrl.js.coffee @restauranteur.controller 'RestaurantIndexCtrl', ['$scope', '$location', '$http', ($scope, $location, $http) -> $scope.restaurants = [] $http.get('./restaurants.json').success((data) -> $scope.restaurants = data ) # निम्नलिखित पंक्तियाँ जोड़ें $scope.viewRestaurant = (id) -> $location.url "/restaurants/#{id}" ]
Rails में कन्वेंशन है कि `resource_name/:id` एक "शो" पेज पर मैप होता है, और हम यहां वही करेंगे। आइए एक शो टेम्पलेट, रूट और कंट्रोलर बनाएं।
# {{restaurant.name}}
# app/assets/javascripts/main.js.coffee @restauranteur = angular.module('restauranteur', []) @restauranteur.config(['$routeProvider', ($routeProvider) -> $routeProvider. when('/restaurants', { templateUrl: '../templates/restaurants/index.html', controller: 'RestaurantIndexCtrl' }). when('/restaurants/:id', { templateUrl: '../templates/restaurants/show.html', controller: 'RestaurantShowCtrl' }). otherwise({ templateUrl: '../templates/home.html', controller: 'HomeCtrl' }) ])
# app/assets/javascripts/angular/controllers/RestaurantShowCtrl.js.coffee @restauranteur.controller 'RestaurantShowCtrl', ['$scope', '$http', '$routeParams', ($scope, $http, $routeParams) -> $http.get("./restaurants/#{$routeParams.id}.json").success((data) -> $scope.restaurant = data ) ]
अब यदि आप `/#/restaurants` रीफ्रेश करते हैं और एक रेस्तरां पर क्लिक करते हैं, तो आपको उस रेस्तरां के शो पेज पर खुद को ढूंढना चाहिए। याय!
अभी के लिए बस इतना ही
हमने विशेष रूप से प्रभावशाली परिणाम नहीं देखे होंगे, लेकिन मुझे आशा है कि मैंने AngularJS को Rails 4 में प्लग करने में आपका कुछ समय बचाया है। अगली बार मैं ngResource को देखने की सिफारिश कर सकता हूँ, जो CRUD कार्यक्षमता को अधिक DRY बनाने में मदद कर सकता है।
और सीखने में रुचि है?
एडम एंडरसन द्वारा बेहतरीन पोस्ट देखें, जिनकी Bootstrapping an AngularJS app in Rails 4.0 श्रृंखला ने मुझे AngularJS और Rails के साथ शुरुआत करने में मदद की। आप उनके ट्यूटोरियल से भी गुजरना पसंद कर सकते हैं, लेकिन यह ट्यूटोरियल इस मायने में अलग है कि मैं *वास्तव में* आपको सभी विवरण चम्मच से खिलाने की कोशिश करता हूँ, जिससे आपके झाड़ियों में फंसने की संभावना कम हो जाती है।