لیست ویدیوهای AngularConnect
This article provides an introduction to a lightweight API for ASP.NET web services provided by DotNetify.SignalR NuGet package that can reduce a lot of boilerplate code when implementing real-time updates, and provide an example of how to create a real-time web component with VueJS without using Node.JS build.
bower install angular-route --save
<script src="bower_components/angular-route/angular-route.min.js" type="text/javascript"></script>
<div class="col-md-9"> <ng-view></ng-view> </div>
model.panel = { title: "Panel Title", items: [ { title: "Home", url: "#/home" }, { title: "Articles", url: "#/articles" }, { title: "Authors", url: "#/authors" } ] };
var module = angular.module("dntModule", ["ngRoute"]);
module.config(function ($routeProvider) { $routeProvider .when("/home", { template: "<app-home></app-home>" }) .when("/articles", { template: "<app-articles></app-articles>" }) .when("/authors", { template: "<app-authors></app-authors>" }) .otherwise({ redirectTo: "/home" }); });
module.component("appHome", { template: ` <hr><div> <div>Panel heading = HomePage</div> <div> HomePage </div> </div>` }); module.component("appArticles", { template: ` <hr><div> <div>Panel heading = Articles</div> <div> Articles </div> </div>` }); module.component("appAuthors", { template: ` <hr><div> <div>Panel heading = Authors</div> <div> Authors </div> </div>` });
اکنون توسط لینکهای تعریف شده میتوانیم به راحتی درون تمپلیتها، پیمایش کنیم. همانطور که عنوان شد تا اینجا مسیریاب پیشفرض Angular هیچ اطلاعی از کامپوننتها ندارد؛ بلکه آنها را با کمک template، به صورت غیر مستقیم، درون صفحه نمایش دادهایم.
معرفی Component Router
مزیت این روتر این است که به صورت اختصاصی برای کار با کامپوننتها طراحی شده است. بنابراین دیگر نیازی به استفاده از template درون route configuration نیست. برای استفاده از این روتر ابتدا باید پکیج آن را نصب کنیم:
bower install angular-component-router --save
سپس وابستگی فوق را با روتر پیشفرضی که در مثال قبل بررسی کردیم، جایگزین خواهیم کرد:
<script src="bower_components/angular-component-router/angular_1_router.js"></script>
همچنین درون فایل module.js به جای وابستگی ngRoute از ngComponentrouter استفاده خواهیم کرد:
var module = angular.module("dntModule", ["ngComponentRouter"]);
در ادامه به جای تمامی route configurations قبلی، اینبار یک کامپوننت جدید را به صورت زیر ایجاد خواهیم کرد:
module.component("appHome", { template: ` <hr> <div> <div>Panel heading = HomePage</div> <div> HomePage </div> </div>` });
همانطور که مشاهده میکنید برای پاسخگویی به تغییرات URL، مقدار routeConfig$ را مقداردهی کردهایم. در اینجا به جای بارگذاری تمپلیت، خود کامپوننت، در هر یک از ruleهای فوق بارگذاری خواهد شد. برای حالت otherwise نیز از سینتکس **/ استفاده کردهایم.
تمپلیت کامپوننت فوق نیز به صورت زیر است:
<div class="container"> <div class="row"> <div class="col-md-3"> <hr> <dnt-widget></dnt-widget> </div> <div class="col-md-9"> <ng-outlet></ng-outlet> </div> </div> </div>
لازم به ذکر است دیگر نباید از دایرکتیو ng-view استفاده کنیم؛ زیرا این دایرکتیو برای استفاده از روتر اصلی طراحی شده است. به جای آن از دایرکتیو ng-outlet استفاده شده است. این کامپوننت به عنوان یک کامپوننت top level عمل خواهد کرد. بنابراین درون صفحهی index.html از کامپوننت فوق استفاده خواهیم کرد:
<html ng-app="dntModule"> <head> <meta charset="UTF-8"> <title>Using Angular 1.5 Component Router</title> <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"> <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css"> </head> <body> <dnt-app></dnt-app> <script src="bower_components/angular/angular.js" type="text/javascript"></script> <script src="bower_components/angular-component-router/angular_1_router.js"></script> <script src="scripts/module.js" type="text/javascript"></script> <script src="scripts/dnt-app.component.js"></script> <script src="scripts/dnt-widget.component.js"></script> </body> </html>
در نهایت باید جهت فعالسازی سیستم مسیریابی جدید، سرویس زیر را همراه با نام کامپوننت فوق ریجستر کنیم:
module.value("$routerRootComponent", "dntApp");
اکنون اگر برنامه را اجرا کنید خواهید دید که همانند قبل، کار خواهد کرد. اما اینبار از روتر جدید و سازگار با کامپوننتها استفاده میکند.
کدهای این قسمت را نیز از اینجا میتوانید دریافت کنید.
کتابخانه fontfaceonload
A simple utility to execute a callback when a webfont loads. Demo
This uses the CSS Font Loading Module when available (currently in Chrome 35+ and Opera 22+). When that isn’t available, it uses a very similar approach to the one used in the TypeKit Web Font Loader (which is currently 7.1KB GZIP).
Basically, it creates an element with a font stack including the web font and a default serif/sans-serif typeface. It then uses a test string and measures the dimensions of the element at a certain interval. When the dimensions are different than the default fallback fonts, the font is considered to have loaded successfully.
If you’d like a full polyfill for the CSS Font Loading Module, follow along with Bram Stein’s Font Loader. I believe the specification has changed since he launched this polyfill, but he’s working on an updated version.
روش جاری شباهت زیادی به استفاده از Context در React دارد:
Context provides a way to pass data through the component tree without having to pass props down manually at every level
// parent component providing 'foo' var Provider = { provide: { foo: 'bar' }, // ... }
// child component injecting 'foo' var Child = { inject: ['foo'], created () { console.log(this.foo) // => "bar" } // ... }
Using an injected value as the default for a prop //دریافت میکنیم props در قسمت child را در کامپوننت foo مقدار const Child = { inject: ['foo'], props: { bar: { default () { return this.foo } } } } Using an injected value as data entry //دریافت میکنیم data در قسمت child را در کامپوننت foo مقدار const Child = { inject: ['foo'], data () { return { bar: this.foo } } }
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <title>Dependency injection</title> </head> <body> <div id="app"> <button @click="counter++">Increment counter</button> <h2>Parent</h2> <p>{{counter}}</p> <div> <h3>Child</h3> <child></child> </div> </div> <script> const Child = { inject: ['counter_in_child'], template: `<div>Counter Child is:{{ counter_in_child }}</div>` }; new Vue ({ el: "#app", components: { Child }, provide() { return { counter_in_child: this.counter }; }, data() { return { counter: 0 }; } }); </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <title>Dependency injection</title> </head> <body> <div id="app"> <button @click="counter++">Increment counter</button> <h2>Parent</h2> <p>{{counter}}</p> <div> <h3>Child</h3> <child></child> </div> </div> <script> const Child = { inject: ['counter_in_child'], template: `<div>Counter Child is:{{ counter_in_child.counter }}</div>` }; new Vue ({ el: "#app", components: { Child }, provide() { const counter_in_child={}; Object.defineProperty(counter_in_child,'counter',{ enumerable:true, get:()=>this.counter }) return { counter_in_child }; }, data() { return { counter: 0 }; } }); </script> </body> </html>
provide
and inject
are primarily provided for advanced plugin / component library use cases. It is NOT recommended to use them in generic application code با استفاده از YAML، دستورالعملهای سرویسهای مختلف را نوشته و با یک دستور همهی آنها را با هم اجرا مینماییم. از compose در تمامی مراحل production, staging, development, testing و همچنین CI workflow استفاده میشود.
برای استفاده از compose سه عمل زیر باید انجام شود:
1- ساخت و تعریف dockerfile برای هر سرویس.
2- ساخت و تعریف docker-compose.yml. بنابراین هر سرویس میتواند در محیط ایزولهی خود اجرا شود.
3- اجرای دستور docker-compose up.
در قسمتهای قبلی مراحل ساخت و اجرای imageها درون کانتینر و همچنین متصل کردن آنها را به شبکه، بررسی کرده ایم؛ اما در این قسمت میخواهیم با استفاده از docker-compose مدیریت build و اجرای همهی imageها را بر عهده بگیریم.
عملا با این ساختار، قابلیت ایجاد شبکه، volume و غیره را خواهیم داشت. بنابراین با استفاده از این config توانایی توزیع برنامه را فقط با یک فایل YAML، خواهیم داشت.
ایجاد پروژه:
فرض کنید نرم افزار ما از دو سرویس Nodejsی همچنین یک دیتابیس Mongo تشکیل شده است. در نهایت باید به چیزی شبیه به تصویر زیر برسیم:
دایرکتوری root این پروژه از دو پوشه به نامهای nodeapp1 و nodeapp2 تشکیل شده است که داخل هر کدام یک فایل index.js و همچنین package.json و dockerfile وجود دارد؛ همانند مطالب پیشین.
اما چیزی که اینجا اضافه شده است، فایل docker-compose.yml جهت مدیریت و اجرای این برنامه میباشد که حاوی ساختار زیر است:
version: '3' networks: shared-network: services: nodeapp1: image: nodeapp1 build: context: nodeapp1 dockerfile: dockerfile ports: - "8181:80" networks: - shared-network nodeapp2: image: nodeapp2 build: context: nodeapp2 dockerfile: dockerfile ports: - "8182:80" networks: - shared-network mongo: image: mongo ports: - "27017:27017" networks: - shared-network
2) برای مشخص کردن سرویسهای این برنامه از services استفاده کرده و آنها را تعریف مینماییم.
3) سرویس nodeapp1 که قرار است تصویری به نام nodeapp1 را ایجاد کند (هدف آن build کردن اولین سرویس میباشد. همانطور که مشخص است context برنامه، اسم پوشهی nodeapp1 درون ریشهی پروژه است. ضمن اینکه نام dockerfile را هم درون آن پوشه بدان اضافه کردهایم).
4) پورت 8181 را بر روی پورت 80 درون این کانتینر هدایت میکنیم.
5) این سرویس، درون شبکهی ایجاد شدهی shared-network قرار میگیرد.
5) سرویس nodeapp2 را هم به همین شکل اضافه میکنیم.
6) سرویس mongo قرار نیست هیچ کدی را build کند و هدف، فقط اجرای mongo درون شبکهی shared-network است که بقیه سرویسها بتوانند بدان وصل شوند.
برای ساخت و اجرا نیز در ریشهی این پروژه، ترمینال خود را باز کرده و دستورات زیر را وارد مینماییم:
برای build کردن:
docker-compose build
docker-compose up
docker-compose down
docker-compose stop
docker-compose start
و اگر بخواهیم بعد از build کردن، بصورت خودکار نیز اجرا شود، از دستور زیر استفاده میکنیم:
docker-compose run --build
dockerfile هر دو سرویس نیز بصورت ساده همانند مطالب پیشین در نظر گرفته شدهاست:
FROM node COPY . /var/www WORKDIR /var/www RUN npm i EXPOSE 80 ENTRYPOINT node index
در صورتیکه بخواهیم نگاهی هم به کدهای نوشته شده بیندازیم، نکتهی جالبی مد نظر قرار میگیرد؛ بطور مثال از آنجائیکه همهی کانتینرهای اجرا شده، درون یک شبکه هستند، برای فراخوانی سرویسهای دیگر کافیست با نامشان صدا زده شوند. بطور مثال در nodeapp1 برای فراخوانی nodeapp2 به راحتی با نام صدا زده شده است و احتیاجی به فراخوانی با ip نیست. کدهای زیر مربوط به فایل index.js در سرویس nodeapp1 میباشند (بدلیل اینکه روی پورت 80 درون کانتینر قرار گرفتهاست، دیگر لازم به وارد نمودن پورت نبودیم؛ در غیر اینصورت بطور مثال باید درخواستی بصورت http://nodeapp2:5000 را ارسال مینمودیم):
const express = require('express'); const fetch = require('node-fetch'); const app = express(); app.get('/', async (req, res) => { let response = await fetch("http://nodeapp2/"); text = await response.text(); res.send(text); }) app.listen(80, () => console.log(`listening on port 80!`))
نکته: override کردن composeها نیز قابل انجام است. بدین معنا که شما یک نسخه برای build و استفاده در محیط development و نسخههای دیگری بطور مثال برای production خود تعریف مینمایید؛ مثلا روی پروداکشن، environment variablesهای متفاوتی را در نظر میگیرید. YAML زیر را مشاهده کنید:
version: '3' services: nodeapp1: environment: - PRODUCTION: 'true' nodeapp2: environment: - PRODUCTION: 'true'
فرض کنید که قرار است YAML فوق بر روی فایل قبلی، بازنویسی شود؛ با استفاده از دستور زیر:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
تمام کدهای فوق از اینجا «node.rar» قابل دریافت میباشد.
Component architectures are an important part of ever modern front-end framework. In this article, I’m going to dissect Polymer, React, Rio.js, Vue.js, Aurelia and Angular 2 components. The goal is to make the commonalities between each solution obvious. Hopefully, this will convince you that learning one or the other isn’t all that complex, given that everyone has somewhat settled on a component architecture.