AngularJS #2
CoffeeScript #1
میخواهید پروژهای را انجام دهید که شامل جداول زیر است:
مقالات، اخبار، گالری تصاویر، گالری ویدیو، اسلایدشو، تبلیغات و ... و تمامی این جداول حداقل شامل یک فایل پیوست (عکس، فیلم، ...) میباشند. به طور مثال جدول مقالات دارای یک عکس نیز میباشد. قصد داریم تمام فایلها را بر روی هاست ذخیره کرده و فقط آدرس و نام فایل را در دیتابیس ذخیره نمایم.
روش اول : استفاده از یک فیلد در هر جدول برای نگه دارای اسم فایل
مثال:
public class Article { public int Id { get; set; } public string Title { get; set; } public string Body { get; set; } public string RegisterDate { get; set; } public string FileName { get; set; } }
این روش فقط در صورتی پاسخگو میباشد که هر رکورد فقط شامل یک فایل باشد. به طور مثال ممکن است برای یک مقاله، چندین عکس و فایل را ضمیمهی آن کنیم. در این حالت این روش پاسخ گو نمیباشد؛ ولی میتوانیم به صورت زیر نیز عمل کنیم:
ایجاد جدولی برای نگهداری فایلهای هر رکورد از مقاله :
public class ArticleFiles { public int Id { get; set; } public string FielName { get; set; } public string FileExtension { get; set; } public Article Article { get; set; } public int FileSize { get; set; } }
میتوانیم جدولی را به نام Attachment ایجاد کرده و هر فایلی را که آپلود میکنیم، مشخصات آن را در این جدول ذخیره کنیم و هر جدول هم که نیازی به فایل داشت، رابطهای با این جدول برقرار کند. در این حالت خواهیم داشت:
public class Attachment { public int Id { get; set; } public string Title { get; set; } public string FileName { get; set; } public string Extension { get; set; } public DateTime RegisterDate { get; set; } public int Size { get; set; } public ICollection<Article> ArticleFiles { get; set; } public ICollection<News> NewsFiles { get; set; } public int Viewed { get; set; } }
روش سوم : جدولی برای نگه داری اسم فایلها، بدون رابطه
جدول Attachment در این روش، همانند روش دوم میباشد؛ با دو تفاوت:
1- با هیچ جدولی رابطهای ندارد.
2- دو فیلد به عنوان نام جدول و Id رکورد به آن اضافه شده است.
تفاوت نسبت به روش دوم:
در روش دوم، ثبت یک رکورد، وابستهی به ثبت رکورد در جدول Attachment بود و ابتدا میبایستی فایل در Attachment ذخیره میشد و بعد از بدست آوردن Id آن، رکورد مورد نظر (مقاله) را درج میکردیم. ولی در این روش ابتدا مقاله درج شده و بعد از آن فایل را با اسم جدول و ID رکورد مورد نظر ذخیره میکنیم:
public class Attachment { public int Id { get; set; } public string Title { get; set; } public string FileName { get; set; } public string Extension { get; set; } public DateTime RegisterDate { get; set; } public int Size { get; set; } public string TableName { get; set; } public int RowId { get; set; } public int Viewed { get; set; } }
ایجاد یک کلاس پایه و ارث بری سایر کلاسها از کلاس پایه و ایجاد رابطهای بین کلاس پایه و کلاسهای مشتق شده.
نظراتی پیرامون حالتهای مختلف:
1- داشتن یک جدول الحاقات برای هر جدول
- اضافه کردن یک فیلد: بعضیها این
روش را ترجیح میدهند. به این دلیل که هر جدول، یک جدول attachment مختص به
خود دارد؛ با توجه به فیلدهایی که لازم است. به طور مثال ممکن است بعد از
گذشت مدتی، نیاز باشد تا دو فیلد برای فایلهای هر مقاله اضافه شوند که در
این حالت فقط به جدول attachment مقاله اضافه خواهند شد.
2- داشتن یک جدول پایه که کل فایلها در آن ذخیره شوند (روشهای دوم و سوم)
- متمرکز شدن کل فایلها در یک جدول: بیشتر پروژهها و یا برنامه نویسان (طبق تجربهی بنده) یک جدول پایه را برای این منظور دوست دارند. به دلیل اینکه تمام اطلاعات یکجا باشد.
- عدم آپلود چندین بارهی یک فایل: در این حالت میتوان از یک فایل چندین بار در چند جای مختلف استفاده نمود و در فضای هاست صرفه جویی میشود. این روش مدیریت سختی دارد و نیازمند کوئریهای بیشتری میباشد.
- وجود فیلدهای زیاد null در جدول: در این حالت ممکن است ردیفهایی با ستونهای مقدار null در جدول زیاد شوند. فرض کنید دو فیلد در جدول attachment وجود دارند که فقط توسط جدول مقالات مورد استفاده قرار میگیرند و در بقیهی جداول بدون استفاده میباشند.
از کدام روش استفاده کنیم؟
نمی توان پیشنهاد کرد که الزاما از کدامیک از روشهای بالا باید استفاده کنیم؛ چون نیازمندهایهای هر پروژه با هم متفات است و نمیتوان نسخهای خاص را برای همه تجویز کرد.
هرچند ارتقاء به HttpClient الزامی نیست و کدهای پیشین، هنوز هم به خوبی کار میکنند؛ اما طراحی جدید آن شامل ویژگیهای توکاری است که به سختی توسط HTTP Module پیشین قابل پیاده سازی هستند.
به روز رسانی وابستگیهای پروژه
پیش از هرکاری نیاز است وابستگیهای پروژه را به روز رسانی کرد و یکی از روشهای سادهی یافتن شماره نگارشهای جدید بستههای تعریف شدهی در فایل package.json برنامه، استفاده از بستهی npm-check-updates است:
npm install npm-check-updates -g ncu
در اینجا شماره نگارشهای جدید مشخص شدهاند و همچنین روش سریع ارتقاء به آنها نیز ذکر شدهاست. فقط کافی است دستورات ذیل را صادر کنیم تا این به روز رسانیها توسط ncu انجام شوند:
ncu -a npm update
تغییرات مورد نیاز جهت معرفی ماژول HttpClient
این ماژول جدید از طریق اینترفیس HttpClientModule ارائه میشود. بنابراین اولین تغییر در جهت ارتقاء به نگارش 4.3، اصلاح importهای لازم است:
از:
import { HttpModule } from '@angular/http';
import { HttpClientModule } from '@angular/common/http';
پس از آن، این HttpClientModule را به لیست imports ماژول اصلی برنامه اضافه میکنیم؛ تا در کل برنامه قابل دسترسی شود:
@NgModule({ imports: [ // ... HttpClientModule, // ... ], declarations: [ ... ], providers: [ ... ], exports: [ ... ] }) export class AppModule { }
تغییرات مورد نیاز در سازندهها و تزریق وابستگیها
پس از تغییرات فوق، دیگر دسترسی به HttpModule پیشین را نداریم. بنابراین نیاز است هر جائی را که سرویس Http به سازندهی کلاسی تزریق شدهاست، یافته و به صورت ذیل تغییر دهیم:
از:
constructor(private http: Http) { }
import { HttpClient } from '@angular/common/http'; // ... constructor(private http: HttpClient) { }
تغییرات مورد نیاز در کدهای سرویسها جهت کار با HTTP Verbs
یکی از اهداف HTTP Client جدید، سادگی کار با اطلاعات دریافتی از سرور است. برای مثال در HTTP Module پیشین، روش دریافت اطلاعات از سرور به صورت ذیل است:
public get(): Observable<MyType> => { return this.http.get(url) .map((response: Response) => <MyType>response.json()); }
در HTTP Client جدید دیگر نیازی نیست تا متد ()json. فراخوانی شود. در اینجا به صورت پیشفرض نوع بازگشتی از سرور JSON فرض میشود. همچنین اکنون متدهای get/put/post و امثال آن برخلاف HTTP Client قبلی، جنریک هستند. یعنی در همینجا میتوان نوع بازگشتی را هم مشخص کرد. به این ترتیب، قطعه کد قدیمی فوق، به کد سادهی ذیل تبدیل میشود که در آن خبری از map و همچنین یک cast اضافی نیست:
get<T>(url: string): Observable<T> { return this.http.get<T>(url); }
post<T>(url: string, body: string): Observable<T> { return this.http.post<T>(url, body); }
نکته 1: در اینجا اگر خروجی از سرور، نوع دیگری را داشت، نیاز است responseType را به صورت صریحی به شکل ذیل مشخص کرد:
getData() { this.http.get(this.url, { responseType: 'text' }).subscribe(res => { this.data = res; }); }
نکته 2: ممکن است اطلاعات بازگشتی از سمت سرور، داخل یک فیلد محصور شده باشند:
{ "results": [ "Item 1", "Item 2", ] }
this.http.get('/api/items').subscribe(data => { this.results = data['results']; });
نکاتی را که باید حین کار با یک RxJS Observable-based API در نظر داشت
این API جدید نیز همانند قبل مبتنی بر RxJS Observables است. بنابراین نکات ذیل در مورد آن نیز صادق است:
- اگر متد subscribe بر روی این observables فراخوانی نشود، اتفاقی رخ نخواهد داد.
- اگر چندین بار مشترک این observables شویم، چندین درخواست HTTP صادر میشوند.
- این نوع خاص از observables، تنها یک مقدار را بازگشت میدهند. اگر درخواست HTTP موفقیت آمیز باشد، این observables یک نتیجه را بازگشت داده و سپس خاتمه پیدا میکنند.
- این observables اگر در حین درخواست HTTP با خطایی مواجه شوند، سبب صدور استثنایی میشوند.
تغییرات مورد نیاز در کدهای سرویسها جهت کار با HTTP Headers
در اینجا برای تعریف headers میتوان به صورت ذیل عمل کرد:
import { HttpHeaders } from "@angular/common/http"; const headers = new HttpHeaders({ "Content-Type": "application/json" });
const headers = new HttpHeaders().set("Accept", "application/json").set('Content-Type', 'application/json');
سپس آنرا به عنوان پارامتر سوم، به متدهای http ارسال میکنیم. یک مثال:
updateAppProduct(id: number, item: AppProduct): Observable<AppProduct> { const header = new HttpHeaders({ "Content-Type": "application/json" }); return this.http .put<AppProduct>( `${this.baseUrl}/UpdateProduct/${id}`, JSON.stringify(item), { headers: header } ) .map(response => response || {}); }
تعریف پارامتر options اینبار به صورت یک شیء دارای چندین خاصیت درآمدهاست. به همین جهت است که در اینجا یک {} را نیز مشاهده میکنید:
(method) HttpClient.post(url: string, body: any, options?: { headers?: HttpHeaders; observe?: "body"; params?: HttpParams; reportProgress?: boolean; responseType?: "json"; withCredentials?: boolean; }): Observable<Object>
یک نکته: شیء HttpHeaders به صورت immutable طراحی شدهاست. یعنی اگر آنرا به صورت ذیل فراخوانی کنیم:
const headers = new HttpHeaders(); headers = headers.set('Content-Type', 'application/json'); headers = headers.set('Accept', 'application/json');
const headers = new HttpHeaders() .set('Content-Type', 'application/json') .set('Accept', 'application/json') ;
امکان تعریف HttpParams
اگر به شیء options در تعریف فوق دقت کنید، دارای خاصیت اختیاری params نیز هست. از آن میتوان جهت تعریف کوئری استرینگها استفاده کرد. برای مثال درخواست ذیل:
http .post('/api/items/add', body, { params: new HttpParams().set('id', '3'), }) .subscribe();
/api/items/add?id=3
یک نکته: شیء HttpParams به صورت immutable طراحی شدهاست. یعنی اگر آنرا به صورت ذیل فراخوانی کنیم:
const params = new HttpParams(); params.set('orderBy', '"$key"') params.set('limitToFirst', "1");
const params = new HttpParams() .set('orderBy', '"$key"') .set('limitToFirst', "1");
const params = new HttpParams({fromString: 'orderBy="$key"&limitToFirst=1'});
تغییرات مورد نیاز در کدهای سرویسها جهت مدیریت خطاها
در اینجا اینبار خطای بازگشتی، از نوع ویژهی HttpErrorResponse است که شامل اطلاعات شماره کد و متن خطای حاصل میباشد:
import { HttpClient, HttpHeaders, HttpErrorResponse } from "@angular/common/http"; postData() { this.http.post(this.url, this.payload).subscribe( res => { console.log(res); }, (err: HttpErrorResponse) => { console.log(err.error); console.log(err.name); console.log(err.message); console.log(err.status); if (err.error instanceof Error) { console.log("Client-side error occured."); } else { console.log("Server-side error occured."); } } ); }
امکان سعی مجدد در اتصال توسط HTTP Client
ممکن است در اولین سعی در اتصال به سرور، خطایی رخ دهد و یا سرور در دسترس نباشد. در اینجا توسط متد retry میتوان درخواست سعی مجدد در اتصال را صادر کرد.
برای این منظور ابتدا عملگر retry مربوط به RxJS را import میکنیم:
import 'rxjs/add/operator/retry';
http .get<ItemsResponse>('/api/items') .retry(3) .subscribe(...);
امکان درخواست کل Response بجای Body
اگر به امضای پارامتر اختیاری options دقت کنید، خاصیت observe آن به صورت پیش فرض به body تنظیم شدهاست. به این معنا که تنها body یک response را تبدیل به یک شیء JSON میکند:
(method) HttpClient.post(url: string, body: any, options?: { headers?: HttpHeaders; observe?: "body"; params?: HttpParams; reportProgress?: boolean; responseType?: "json"; withCredentials?: boolean; }): Observable<Object>
http .get<MyJsonData>('/data.json', {observe: 'response'}) .subscribe(resp => { console.log(resp.headers.get('X-Custom-Header')); console.log(resp.body.someField); });
یک نکتهی تکمیلی: کدهای سری کار با فرمها در Angular را اگر به HttpClient ارتقاء دهیم، خلاصهی تغییرات آنها به این صورت خواهند بود.
تمام مسیریابیهای این سری به نگارش سوم روتر AngularJS 2.0 به روز رسانی شدند.
ریز جزئیات تغییرات
توضیحات:
ابتدا نیاز است وابستگیهای روتر جدید را به نحو ذیل به فایل package.json اضافه کنید:
"dependencies": { // ... "@angular/router": "^3.0.0-alpha.7", // ... },
یک فایل جدید را به نام app.routes.ts به ریشهی پروژه اضافه کنید، با این محتوا
import { provideRouter, RouterConfig } from '@angular/router'; import { ProductListComponent } from './products/product-list.component'; import { WelcomeComponent } from './home/welcome.component'; import { ProductDetailComponent } from './products/product-detail.component'; import { ProductFormComponent } from './products/product-form.component'; import { SignupFormComponent } from './users/signup-form.component'; import { TypedShaComponent } from './using-third-party-libraries/typed-sha.component'; import { UnTypedShaComponent } from './using-third-party-libraries/untyped-sha.component'; import { UsingJQueryAddonsComponent } from './using-jquery-addons/using-jquery-addons.component'; export const routes: RouterConfig = [ { path: '', component: WelcomeComponent }, { path: 'welcome', component: WelcomeComponent }, { path: 'products', component: ProductListComponent }, { path: 'product/:id', component: ProductDetailComponent }, { path: 'addproduct', component: ProductFormComponent }, { path: 'adduser', component: SignupFormComponent }, { path: 'typedsha', component: TypedShaComponent }, { path: 'untypedsha', component: UnTypedShaComponent }, { path: 'usingjquery', component: UsingJQueryAddonsComponent } ]; export const APP_ROUTER_PROVIDERS = [ provideRouter(routes) ];
در سیستم مسیریابی جدید، خاصیتهای name و useAsDefault وجود ندارند و حذف شدهاند. همچنین مسیریابیها نباید با / شروع شوند.
به علاوه در فایل index.html، مسیر ریشه به نحو ذیل مشخص میشود:
<base href=".">
// ... import { APP_ROUTER_PROVIDERS } from './app.routes'; // ... bootstrap(AppComponent, [ // ... APP_ROUTER_PROVIDERS ]) .catch(err => console.error(err));
البته باید دقت داشت که فایل systemjs.config.js هم کمی نیاز است جهت بارگذاری این مسیریاب جدید اصلاح شود.
در ادامه، در فایل app.component.ts، دایرکتیوهای مرتبط با مسیریابی که در ROUTER_DIRECTIVES قرار دارند، اینبار از ماژول ذیل تامین میشوند:
import { ROUTER_DIRECTIVES } from '@angular/router';
سپس لینکهای مسیریابی، اینبار بجای نام مسیریابی که در نگارش سوم روتر، حذف شدهاست، به همان مسیر متناظر اشاره میکند:
<a [routerLink]="['/welcome']">Home</a>
this._router.navigate(['/products']);
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; @Component({ templateUrl: 'app/products/product-detail.component.html' //template: require('./product-detail.component.html')//for webpack }) export class ProductDetailComponent implements OnInit, OnDestroy { private sub: any; pageTitle: string = 'Product Detail'; constructor(private _route: ActivatedRoute, private _router: Router) { } ngOnInit(): void { this.sub = this._route.params .subscribe(params => { let id = +params['id']; // (+) converts string 'id' to a number this.pageTitle += `: ${id}`; }); } ngOnDestroy(): void { this.sub.unsubscribe(); // we must unsubscribe before Angular destroys the component. Failure to do so could create a memory leak. } onBack(): void { this._router.navigate(['/products']); } }
یک اصلاح دیگر هم در اینجا داریم. لینک به صفحهی جزئیات هر محصول اینبار به صورت زیر ویرایش میشود (در فایل product-list.component.html):
<a [routerLink]="['/product', product.productId]"> {{product.productName}} </a>
$.get('http://site-url', function(data) { //این تابع پس از پایان کار عملیات ایجکسی در آینده فراخوانی خواهد شد });
$.get('http://site-url/0', function(data0) { // callback #1 $.get('http://site-url/1', function(data1) { // callback #2 $.post('http://site-url/2', function(data2) { // callback #3 }); }); });
روشهای زیادی برای حل این مساله ارائه شدهاست و در حال حاضر کار کردن با promiseها متداولترین روش حل مدیریت فراخوانی کدهای همزمان جاوا اسکریپتی است. برای نمونه اگر از AngularJS استفاده کنید، سرویسهای آن برای دریافت اطلاعات از سرور، از یک چنین مفهومی استفاده میکنند.
Promise در جاوا اسکریپت چیست؟
شیء Promise، نمایانگر قراردادی است که در آینده میتواند مورد قبول واقع شود، یا رد گردد. بررسی این قرارداد، تنها یکبار میتواند رخ دهد (پذیرش یا رد آن). هنگامیکه این بررسی صورت گرفت (رد یا پذیرش آن و نه هردو)، یک callback برای اطلاع رسانی فراخوانی میگردد. سپس این callback میتواند یک Promise دیگر را سبب شود. به این ترتیب میتوان Promiseها را زنجیر وار به یکدیگر متصل کرد. برای نمونه jQuery به صورت توکار از promises پشتیبانی میکند:
// returns a promise $.get('http://site-url/0') .then(function(data) { // callback 1 // returns a promise return $.get('http://site-url/1'); }) .then(function(data) { // callback 2 // returns a promise return $.post('http://site-url/2'); }) .then(function(data) { // callback 3 });
در این حالت، هر callback حداقل سه کار را میتواند انجام دهد:
الف) یک promise دیگر را بازگشت دهد. نمونه آنرا با return $.get در کدهای فوق ملاحظه میکنید.
ب) خاتمه عادی. همینجا کار promise با مقدار بازگشت داده شده، پایان مییابد.
ج) صدور یک استثناء. سبب برگشت خوردن و عدم پذیرش promise میشود.
استفاده از Promises در سایر کتابخانهها
jQuery پیاده سازی توکاری از promises دارد؛ اما سایر کتابخانهها، مانند AngularJS ایی که مثال زده شده چطور عمل میکنند؟
استانداردی به نام +Promises/A جهت یک دست سازی پیاده سازیهای promise در جاوا اسکریپت پیشنهاد شدهاست. jQuery نیمی از آنرا پیاده سازی کردهاست؛ اما کتابخانهی دیگری به نام Q Library، پیاده سازی نسبتا مفصلتری را از این استاندارد ارائه میدهد. فریم ورک AngularJS نیز در پشت صحنه از همین کتابخانه برای پیاده سازی promises استفاده میکند.
آشنایی با کتابخانه Q
استفاده مقدماتی از Q همانند مثالی است که از jQuery ملاحظه کردید.
Q.fcall(callback1) .then(callback2);
Q.fcall(function() { return $.get('http://my-url'); }) .then(callback3);
function waitForClick() { var deferred = Q.defer(); $('#okButton').click(function() { deferred.resolve(); }); $('#cancelButton').click(function() { deferred.reject(); }); return deferred.promise; } Q.fcall(waitForClick) .then(function() { // ok button was clicked }, function() { // cancel button was clicked });
در ادامه کار، اینبار متد then، دو callback را قبول میکند. Callback اول پس از پذیرش قرار داد و Callback دوم پس از رد قرار داد، فراخوانی خواهد گردید.
در رنجیره تعریف شده، اگر معادلی برای reject درنظر گرفته نشده باشد، مانند مثال ذیل:
Q.fcall(myFunction1) .then(success1) .then(success2, failure1);
همچنین اگر نتیجهی success1 با شکست مواجه شود نیز failure1 فراخوانی میگردد. اما باید درنظر داشت که شکست success2، توسط failure1 مدیریت نمیشود.
Promises در AngularJS
در AngularJS امکانات کتابخانه Q توسط پارامتری به نام q$ در اختیار سرویسهای برنامه قرار میگیرد (تزریق میشود):
var app = angular.module("myApp", []); app.factory('dataSvc', function($http, $q){ var basePath="api/books"; getAllBooks = function(){ var deferred = $q.defer(); $http.get(basePath).success(function(data){ deferred.resolve(data); }).error(function(err){ deferred.reject("service failed!"); }); return deferred.promise; }; return{ getAllBooks:getAllBooks }; }); app.controller('HomeController', function($scope, $window, dataSvc){ function initialize(){ dataSvc.getAllBooks().then(function(data){ $scope.books = data; }, function(msg){ $window.alert(msg); }); } initialize(); });
اکنون در کنترلری که قرار است از این سرویس استفاده کند، متد then کتابخانه Q را ملاحظه میکنید که دو Callback متناظر resolve و reject مدیریت promise بازگشت داده شده را به همراه دارد. اگر عملیات Ajaxایی موفقیت آمیز باشد، شیء books را مقدار دهی میکند و اگر خیر، پیامی را به کاربر نمایش خواهد داد.
پشتیبانی مرورگرهای جدید از استاندارد Promise
در حال حاضر کروم 32 و نگارشهای شبانه فایرفاکس، Promise را که جزئی از استاندارد JavaScript شدهاست، به صورت توکار و بدون نیاز به کتابخانههای جانبی، پشتیبانی میکنند.
if (window.Promise) { // Check if the browser supports Promises var promise = new Promise(function(resolve, reject) { //asynchronous code goes here }); }
if (window.Promise) { console.log('Promise found'); var promise = new Promise(function(resolve, reject) { // async if (result) { resolve(data); } else { reject('error'); } }); promise.then(function(data) { console.log('Promise fulfilled.'); }, function(error) { console.log('Promise rejected.'); }); } else { console.log('Promise not available'); }
در صورتیکه طراحی شما بر اساس MVC صورت گرفته است، در کمتر از چند دقیقه و در سه مرحله میتوانید پرونده Rss و Sitemap را برای همیشه ببندید.
پیش از تشریح مراحل، به ساختار این دو فایل توجه کنید.
مراحل کار :
مرحله 1. ایجاد نوع(Type) مورد نیاز برای ایجاد Xml های فوق
مرحله 2 . ایجاد کنترلر XML
مرحله 3. ایجاد مسیریابی(Routing)
مرحله 1 : ابتدا یک کلاس به منظور شکل دهی به اطلاعات، بر اساس خواستههای xml مرتبط با RSS و Sitemap تشکیل دهید:
public class PostToXml { public int PostId { get; set; } public string title { get; set; } public string link { get; set; } public string description { get; set; } public Nullable<DateTime> pubDate { get; set; } }
مرحله 2 : یک کنترلر به نام xml ایجاد کنید و اکشن متدهای زیر را درون آن قرار دهید :
public ContentResult RSS() { var items = GetRssFeed(); var rss = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("rss", new XAttribute("version", "2.0"), new XElement("channel", new XElement("title", "آخرین مطالب سایت"), new XElement("link", "http://" + Request.Url.Host+"/rss"), new XElement("description", "آخرین مطالب سایت من"), new XElement("copyright","(c)" + DateTime.Now.Year + ", نام سایت من.تمامی حقوق محفوظ است"), from item in items select new XElement("item", new XElement("title", item.title), new XElement("description", item.description), new XElement("link", item.link), new XElement("pubDate", item.pubDate) ) ) ) ); return Content(rss.ToString(), "text/xml"); } public ContentResult Sitemap() { XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9"; var items = GetLinks(); var sitemap = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement(ns + "urlset", from item in items select new XElement("url", new XElement("loc", item.link), new XElement("changefreq", "monthly"), new XElement("priority", "0.5") ) ) ); return Content(sitemap.ToString(), "text/xml"); } public IEnumerable<PostToXml> GetRssFeed() { // یک کوئری که لیستی از تایپ مشخص شده به ما بدهد } public IEnumerable<PostToXml> GetLinks() { // یک کوئری که لیستی از تایپ مشخص شده به ما بدهد }
این کنترلر دارای دو اکشن متد Rss و Sitemap است و این اکشنها وظیفهی ایجاد فایلهای Xml را به عهده دارند. مواد اولیه این xml ها از دو متد GetRssFeed و GetLinks تهیه میشوند. ما این مواد را در تمپلیت Rss و Sitemap جایگذاری خواهیم کرد. (به کمک دو اکشن متد Rss و Sitemap )
کافیست لیستی از مواردی را که میخواهیم در Rss یا Sitemap ثبت شوند، تهیه کنیم. این لیست بر اساس شکل تنظیم دیتابیس و مسیریابی سایت، میتواند پیچیده و یا ساده باشد. (به کمک کوئری گرفتن با linq و یا اضافه کردن مستقیم آدرسها به لیست و یا ترکیبی از هر دو مورد) برای درک بهتر موضوع، لطفا تصویر موجود در ابتدای مقاله را مشاهده نمایید.
مرحله 3 : در مرحله آخر کافیست دو مورد زیر را به فایل RoutConfig.cs بیافزایید:
routes.MapRoute( "Sitemap", "sitemap", new { controller = "XML", action = "Sitemap" }); routes.MapRoute( "RSS", "rss", new { controller = "XML", action = "RSS" });
به کمک آدرسهای زیر میتوانید به آنچه که تهیه کردهاید دسترسی داشته باشید :
http://domain.com/rss http://domain.com/sitemap
فایل پروژه را دریافت کنید :
MVC_RSS_Sitemap-43ad3c6681734b34b91deaaabcdba871.rar
داشبورد Angular رایگان ng2-admin
ویژگیها :
- Angular 2+
- Angular CLI
- Bootstrap 4 CSSFramework
- SASS
- Smart Table
- Forms
- Editors
- Charts (amChart, Chartist, Chart.js,Morris)
- Maps (Google, Leaflet,amMap)
- Responsive layout
- High resolution
- and many more!
ویرایشگر متن TinyMce نسخه Angular Js
ویرایشگر TinyMce یک ویرایشگر معروف رایگان است که تقریبا تمام فریم ورکهای معروف جاوا اسکریپتی را پشتیبانی میکند.
این ویرایشگر را میتوانید با تنظیمات مختصری به صفحههای انگیولاری خود اضافه کنید. در ضمن این ویرایشگر با هر دو نسخه Angular 1.X و Angular 2.X به خوبی کار میکند. توضیحات تکمیلی را میتوانید در مستندات TinyMce مشاهده کنید.