SmObjectFactory.Container.GetInstance<MyClass>()
فعال سازی عملیات CRUD در Kendo UI Grid
$("#grid").kendoGrid({ // ... columns: [ { field: "Your Field", title: "Your Field Name", width: "20%", editor: function (container, options) { $('<textarea cols="20" rows="4" data-bind="value: ' + options.field + '"></textarea>').appendTo(container); } }, // ... ] // ... });
EF Code First #12
Program.cs: Application.Run(ObjectFactory.GetInstance<frmMain>());
frmMain: private IContainer _container; private IUnitOfWork _uow; public frmMain(IUnitOfWork uow, IContainer container) { InitializeComponent(); _container = container; _uow = uow; }
زمانی که سیستم عامل های GUI مثل ویندوز به بازار آمدند، یکی از قسمتهای گرافیکی آنها AddressBar نام داشت که مسیر حرکت آنها را در فایل سیستم نشان میداد و در سیستم عاملهای متنی CLI با دستور cd یا pwd انجام میشد. بعدها در وب هم همین حرکت با نام BreadCrumb صورت گرفت که به عنوان مثال مسیر رسیدن به صفحهی یک محصول یا یک مقاله را نشان میداد. در یک پروژهی اندرویدی نیاز بود تا یک ساختار درختی را پیاده سازی کنم، ولی در برنامههای اندروید ایجاد یک درخت، کار هوشمندانه و مطلوبی نیست و روش کار به این صورت است که یک لیست از گروههای والد را نمایش داده و با انتخاب هر آیتم لیست به آیتمهای فرزند تغییر میکند. حالا مسئله این بود که کاربر باید مسیر حرکت خودش را بشناسد. به همین علت مجبور شدم یک BreadCrumb را برای آن طراحی کنم که در زیر تصویر آن را مشاهده میکنید.
از نکات جالب توجه در مورد این ماژول میتوان گفت که قابلیت این را دارد تا تصمیمات خود را بر اساس اندازههای مختلف صفحه نمایش بگیرد. به عنوان مثال اگر آیتمهای بالا بیشتر از سه عدد باشد و در صفحه جا نشود از یک مسیر جعلی استفاده میکند و همهی آیتمها با اندیس شماره 1 تا index-3 را درون یک آیتم با عنوان (...) قرار میدهد که من به آن میگویم مسیر جعلی. به عنوان نمونه مسیر تصویر بالا در صفحه جا شده است و نیازی به این کار دیده نشده است. ولی تصویر زیر از آن جا که مسیر، طول width صفحه نمایش رد کرده است، نیاز است تا چنین کاری انجام شود. موقعیکه کاربر آیتم ... را کلیک کند، مسیر باز شده و به محل index-3 حرکت میکند. یعنی دو مرحله به عقب باز میگردد.
نگاهی به کارکرد ماژول
قبل از توضیح در مورد سورس، اجازه دهید نحوهی استفاده از آن را ببینیم.
این سورس شامل دو کلاس است که سادهترین کلاس آن AndBreadCrumbItem میباشد که مشابه کلاس ListItem در بخش وب دات نت است و دو مقدار، یکی متن و دیگری Id را میگیرد:
سورس:
public class AndBreadCrumbItem { private int Id; private String diplayText; public AndBreadCrumbItem(int Id, String displayText) { this.Id=Id; this.diplayText=displayText; } public String getDiplayText() { return diplayText; } public void setDiplayText(String diplayText) { this.diplayText = diplayText; } public int getId() { return Id; } public void setId(int id) { Id = id; } }
به عنوان مثال میخواهیم یک breadcrumb را با مشخصات زیر بسازیم:
AndBreadCrumbItem itemhome=new AndBreadCrumbItem(0,"Home"); AndBreadCrumbItem itemproducts=new AndBreadCrumbItem(12,"Products"); AndBreadCrumbItem itemdigital=new AndBreadCrumbItem(15,"Digital"); AndBreadCrumbItem itemhdd=new AndBreadCrumbItem(56,"Hard Disk Drive");
AndBreadCrumb breadCrumb=new AndBreadCrumb(this); breadCrumb.AddNewItem(itemhome); breadCrumb.AddNewItem(itemproducts); breadCrumb.AddNewItem(itemdigital); breadCrumb.AddNewItem(itemhdd);
breadCumb.setContext(this);
پس از افزودن آیتم ها، تنظیمات زیر را اعمال نمایید:
LinearLayout layout=(LinearLayout)getActivity().findViewById(R.id.breadcumblayout); layout.setPadding(8, 8, 8, 8); breadCrumb.setLayout(layout); breadCrumb.SetTinyNextNodeImage(R.drawable.arrow); breadCrumb.setTextSize(25); breadCrumb.SetViewStyleId(R.drawable.list_item_style);
حال برای رسم آن متد UpdatePath را صدا میزنیم:
breadCrumb.UpdatePath();
الان اگر برنامه اجرا شود باید breadcrumb از چپ به راست رسم گردد. برای استفادههای فارسی، راست به چپ میتوانید از متد زیر استفاده کنید:
breadCrumb.setRTL(true);
در صورتیکه قصد دارید تنظیمات بیشتری چون رنگ متن، فونت متن و ... را روی هر المان اعمال کنید، از رویداد زیر استفاده کنید:
breadCrumb.setOnTextViewUpdate(new ITextViewUpdate() { @Override public TextView UpdateTextView(Context context, TextView tv) { tv.setTextColor(...); tv.setTypeface(...); return tv; } });
همچنین در صورتیکه میخواهید بدانید کاربر بر روی چه عنصری کلیک کرده است، از رویداد زیر استفاده کنید:
breadCumb.setOnClickListener(new IClickListener() { @Override public void onClick(int position, int Id) { //... } });
آخرین متد موجود که کمترین استفاده را دارد، متد SetNoResize است. در صورتیکه این متد با True مقداردهی گردد، عملیات تنظیم بر اساس صفحهی نمایش لغو میشود. این متد برای زمانی مناسب است که به عنوان مثال شما از یک HorozinalScrollView استفاده کرده باشید. در این حالت layout شما هیچ گاه به پایان نمیرسد و بهتر هست عملیات اضافه را لغو کنید.
نگاهی به سورس
کلاس زیر شامل بخشهای زیر است:
فیلدهای خصوصی
//=-=--=-=-=-=-=-=-=-=-=-=-=- Private Properties -=-=-=-=-=-=-=--=-=-= private List<AndBreadCrumbItem> items=null; private List<TextView> textViews; private int tinyNextNodeImage; private int viewStyleId; private Context context; private boolean RTL; private float textSize=20; private boolean noResize=false; LinearLayout layout; IClickListener clickListener; ITextViewUpdate textViewUpdate; LinearLayout.LayoutParams params ;
اینترفیسها هم با حرف I شروع و برای تعریف رویدادها ایجاد شدهاند. در ادامه از تعدادی متد get و Set برای مقدار دهی بعضی از فیلدهای خصوصی بالا استفاده شده است:
//=-=---=-=-=-=-- Constructor =--=-=-=-=-=--=-=- public AndBreadCrumb(Context context) { this.context=context; params = new LinearLayout.LayoutParams (LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); } //=-=-=--=--=-=-=-=-=-=-=-=- Public Properties --=-=-=-=-=-=--=-=-=-=-=-=- //each category would be added to create path public void AddNewItem(AndBreadCrumbItem item) { if(items==null) items=new ArrayList<>(); items.add(item); } // if you want a pointer or next node between categories or textviews public void SetTinyNextNodeImage(int resId) {this.tinyNextNodeImage=resId;} public void SetViewStyleId(int resId) {this.viewStyleId=resId;} public void setTextSize(float textSize) {this.textSize = textSize;} public boolean isRTL() { return RTL; } public void setRTL(boolean RTL) { this.RTL = RTL; } public void setLayout(LinearLayout layout) { this.layout = layout; } public void setContext(Context context) { this.context = context; } public boolean isNoResize() { return noResize; } public void setNoResize(boolean noResize) { this.noResize = noResize; }
بعد از آن به متدهای خصوصی میرسیم که متد زیر، متد اصلی ما برای ساخت breadcrumb است:
//primary method for render objects on layout private void DrawPath() { //stop here if essentail elements aren't present if (items == null) return ; if (layout == null) return; if (items.size() == 0) return; //we need to get size of layout,so we use the post method to run this thread when ui is ready layout.post(new Runnable() { @Override public void run() { //textviews created here one by one int position = 0; textViews = new ArrayList<>(); for (AndBreadCrumbItem item : items) { TextView tv = MakeTextView(position, item.getId()); tv.setText(item.getDiplayText()); textViews.add(tv); position++; } //add textviews on layout AddTextViewsOnLayout(); //we dont manage resizing anymore if(isNoResize()) return; //run this code after textviews Added to get widths of them TextView last_tv=textViews.get(textViews.size()-1); last_tv.post(new Runnable() { @Override public void run() { //define width of each textview depend on screen width BatchSizeOperation(); } }); } }); }
TextView tv=new TextView(this); tv.getWidth(); //return 0 layout.add(tv); tv.getWidth(); //return 0
TextView tv=new TextView(this); tv.post(new Runnable() { @Override public void run() { tv.getWidth(); //return x } });
باز میگردیم به متد DrawPath و داخل متد post
در اولین خط این پروسه به ازای هر آیتم، یک TextView توسط متد MakeTextView ساخته میشود که شامل کد زیر است:
private TextView MakeTextView(final int position, final int Id) { //settings for cumbs TextView tv=new TextView(this.context); tv.setEllipsize(TextUtils.TruncateAt.END); tv.setSingleLine(true); tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); tv.setBackgroundResource(viewStyleId); /*call custom event - this event will be fired when user click on one of textviews and returns position of textview and value that user sat as id */ tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SetPosition(position); clickListener.onClick(position, Id); } }); //if user wants to update each textviews if(textViewUpdate!=null) tv=textViewUpdate.UpdateTextView(context,tv); if(isRTL()) tv.setRotationY(180); return tv; }
در خطوط اولیه، یک Textview ساخته و متد Ellipsize را با Truncate.END مقداردهی مینماید. این مقدار دهی باعث میشود اگر متن، در Textview جا نشد، ادامهی آن با ... مشخص شود. در خط بعدی Textview را تک خطه معرفی میکنیم. در خط بعدی اندازهی قلم را بر اساس آنچه کاربر مشخص کرده است، تغییر میدهیم و بعد هم استایل را برای آن مقداردهی میکنیم. بعد از آن رویداد کلیک را برای آن مشخص میکنیم تا اگر کاربر بر روی آن کلیک کرد، رویداد اختصاصی خودمان را فراخوانی کنیم.
در خط بعدی اگر rtl با true مقدار دهی شده باشد، textview را حول محور Y چرخش میدهد تا برای زبانهای راست به چپ چون فارسی آماده گردد و در نهایت Textview ساخته شده و به سمت متد DrawPath باز میگرداند.
بعد از ساخته شدن TextViewها، وقت آن است که به Layout اضافه شوند که وظیفهی اینکار بر عهدهی متد AddTextViewOnLayout است:
//this method calling by everywhere to needs add textviews on the layout like master method :drawpath private void AddTextViewsOnLayout() { //prepare layout //remove everything on layout for recreate it layout.removeAllViews(); layout.setOrientation(LinearLayout.HORIZONTAL); layout.setVerticalGravity(Gravity.CENTER_VERTICAL); if(isRTL()) layout.setRotationY(180); //add textviews one by one int position=0; for (TextView tv:textViews) { layout.addView(tv,params); //add next node image between textviews if user defined a next node image if(tinyNextNodeImage>0) if(position<(textViews.size()-1)) { layout.addView(GetNodeImage(), params); position++; } } }
تا به اینجا کار چیدمان به ترتیب انجام شده است ولی از آنجا که اندازهی Layout در هر گوشی و در دو حالت حالت افقی یا عمودی نگه داشتن گوشی متفاوت است، نمیتوان به این چینش اعتماد کرد که به چه نحوی عناصر نمایش داده خواهند شد و این مشکل توسط متد BatchSizeOperation (تغییر اندازه دسته جمعی) حل میگردد. در اینجا هم باز متد post به آخرین textview اضافه شده است. به این علت که موقعیکه همهی textviewها در ui جا خوش کردند، بتوانیم به خاصیتهای ui آنها دستیابی داشته باشیم. حالا بعد از ترسیم باید اندازه آنها را اصلاح کنیم. قدم به قدم متد BatchSizeOperation را بررسی میکنیم:
//set textview width depend on screen width private void BatchSizeOperation() { //get width of next node between cumbs Bitmap tinyBmap = BitmapFactory.decodeResource(context.getResources(), tinyNextNodeImage); int tinysize=tinyBmap.getWidth(); //get sum of nodes tinysize*=(textViews.size()-1); ... }
//get width size of screen(layout is screen here) int screenWidth=GetLayoutWidthSize(); //get sum of arrows and cumbs width int sumtvs=tinysize; for (TextView tv : textViews) { int width=tv.getWidth(); sumtvs += width; }
private int GetLayoutWidthSize() { int width=layout.getWidth(); int padding=layout.getPaddingLeft()+layout.getPaddingRight(); width-=padding; return width; }
private void BatchSizeOperation() { .... //if sum of cumbs is less than screen size the state is good so return same old textviews if(sumtvs<screenWidth) return ; if(textViews.size()>3) { //make fake path MakeFakePath(); //clear layout and add textviews again AddTextViewsOnLayout(); } //get free space without next nodes -> and spilt rest of space to textviews count to get space for each textview int freespace =screenWidth-tinysize; int each_width=freespace/textViews.size(); //some elements have less than each_width,so we should leave size them and calculate more space again int view_count=0; for (TextView tv:textViews) { if (tv.getWidth()<=each_width) freespace=freespace-tv.getWidth(); else view_count++; } if (view_count==0) return; each_width=freespace/view_count; for (TextView tv:textViews) { if (tv.getWidth()>each_width) tv.setWidth(each_width); } }
اگر آیتمها بیشتر از سه عدد باشند، میتوانیم از حالت مسیر جعلی استفاده کنیم که توسط متد MakeFakePath انجام میشود. البته بعد از آن هم باید دوباره viewها را چینش کنیم تا مسیر جدید ترسیم گردد، چون ممکن است بعد از آن باز هم جا نباشد یا آیتمها بیشتر از سه عدد نیستند. در این حالت، حداقل کاری که میتوانیم انجام دهیم این است که فضای موجود را بین آنها تقسیم کنیم تا همهی کاسه، کوزهها سر آیتم آخر نشکند و متنش به ... تغییر یابد و حداقل از هر آیتم، مقداری از متن اصلی نمایش داده شود. پس میانگین فضای موجود را گرفته و بر تعداد المانها تقسیم میکنیم. البته این را هم باید در نظر گرفت که در تقسیم بندی، بعضی آیتمها آن مقدار پهنا را نیاز ندارند و با پهنای کمتر هم میشود کل متنشان را نشان داد. پس یک کار اضافهتر این است که مقدار پهنای اضافی آنها را هم حساب کنیم و فقط آیتمهایی را پهنا دهیم که به مقدار بیشتری از این میانگین احتیاج دارند. در اینجا کار به پایان میرسد و مسیر نمایش داده میشود.
نحوهی کارکرد متد MakeFakePath بدین صورت است که 4 عدد TextView را ایجاد کرده که المانهای با اندیس 0 و 2 و 3 به صورت نرمال و عادی ایجاد شده و همان کارکرد سابق را دارند. ولی المان شماره دو با اندیس 1 با متن ... نمایندهی آیتمهای میانی است و رویدادکلیک آن به شکل زیر تحریف یافته است:
//if elements are so much(mor than 3),we make a fake path to decrease elements private void MakeFakePath() { //we make 4 new elements that index 1 is fake element and has a rest of real path in its heart //when user click on it,path would be opened textViews=new ArrayList<>(4); TextView[] tvs=new TextView[4]; int[] positions= {0,items.size()-3,items.size()-2,items.size()-1}; for (int i=0;i<4;i++) { //request for new textviews tvs[i]=MakeTextView(positions[i],items.get(positions[i]).getId()); if(i!=1) tvs[i].setText(items.get(positions[i]).getDiplayText()); else { tvs[i].setText("..."); //override click event and change it to part of code to open real path by call setposition method and redraw path tvs[i].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int pos = items.size() - 3; int id = items.get(pos).getId(); SetPosition(items.size() - 3); clickListener.onClick(pos, id); } }); } textViews.add(tvs[i]); } }
6- کل مدیریت فایل در فایل index.html میباشد و اگر تمایل به تغییر عنوان، درج لوگوی خودتان و ... را دارید به این فایل مراجعه کنید.
خوب، اکنون فایل Tinymce.html را اجرا کنید و نتیجه را ملاحظه بفرمایید.
استفاده از افزونهی jsTree در ASP.NET MVC
"dataType": "json", "contentType": "application/x-www-form-urlencoded", 'data': function (node) { return { 'id': node.id, recordId: 7885, name: "Test" }; }
مدلهای سمت سرور برنامه
در این مطلب قصد داریم لیست گروهها را به همراه محصولات مرتبط با آنها، توسط دو drop down list نمایش دهیم:
public class Category { public int CategoryId { set; get; } public string CategoryName { set; get; } [JsonIgnore] public IList<Product> Products { set; get; } } public class Product { public int ProductId { set; get; } public string ProductName { set; get; } }
منبع داده JSON سمت سرور
پس از مشخص شدن مدلهای برنامه، اکنون توسط دو اکشن متد، لیست گروهها و همچنین لیست محصولات یک گروه خاص را با فرمت JSON بازگشت میدهیم:
namespace AngularTemplateDrivenFormsLab.Controllers { [Route("api/[controller]")] public class ProductController : Controller { [HttpGet("[action]")] public async Task<IActionResult> GetCategories() { await Task.Delay(500); return Json(CategoriesDataSource.Items); } [HttpGet("[action]/{categoryId:int}")] public async Task<IActionResult> GetProducts(int categoryId) { await Task.Delay(500); var products = CategoriesDataSource.Items .Where(category => category.CategoryId == categoryId) .SelectMany(category => category.Products) .ToList(); return Json(products); } } }
- در اینجا از یک Delay نیز استفاده شدهاست تا بتوان آیکنهای چرخندهی Loading سمت کاربر را در حین کار با عملیاتی زمانبر، بهتر مشاهده کرد.
کدهای سمت کاربر برنامه
کدهای سمت کاربر این مثال در ادامهی همان مطلب «فرمهای مبتنی بر قالبها در Angular - قسمت پنجم - ارسال اطلاعات به سرور» هستند که بر روی آن این دستورات فراخوانی شدهاست:
>ng g m Product -m app.module --routing
>ng g c product/product-group
>ng g cl product/product >ng g cl product/Category >ng g cl product/product-group-form
export class ProductGroupForm { constructor( public categoryId?: number, public productId?: number ) { } } export class Product { constructor( public productId: number, public productName: string ) { } } export class Category { constructor( public categoryId: number, public categoryName: string ) { } }
سپس سرویسی را جهت دریافت اطلاعات دراپ داونها از سرور تهیه کردهایم:
>ng g s product/product-items -m product.module
import { Injectable } from "@angular/core"; import { Http, Response, Headers, RequestOptions } from "@angular/http"; import { Observable } from "rxjs/Observable"; import "rxjs/add/operator/do"; import "rxjs/add/operator/catch"; import "rxjs/add/observable/throw"; import "rxjs/add/operator/map"; import "rxjs/add/observable/of"; import { Category } from "./category"; import { Product } from "./product"; @Injectable() export class ProductItemsService { private baseUrl = "api/product"; constructor(private http: Http) { } private handleError(error: Response): Observable<any> { console.error("observable error: ", error); return Observable.throw(error.statusText); } getCategories(): Observable<Category[]> { return this.http .get(`${this.baseUrl}/GetCategories`) .map(response => response.json() || {}) .catch(this.handleError); } getProducts(categoryId: number): Observable<Product[]> { return this.http .get(`${this.baseUrl}/GetProducts/${categoryId}`) .map(response => response.json() || {}) .catch(this.handleError); } }
پس از این مقدمات اکنون میتوان کدهای ProductGroupComponent را تکمیل کرد.
ابتدا در متد ngOnInit آن کار دریافت لیست آغازین گروههای محصولات را انجام میدهیم:
export class ProductGroupComponent implements OnInit { categories: Category[] = []; model = new ProductGroupForm(); constructor(private productItemsService: ProductItemsService) { } ngOnInit() { this.productItemsService.getCategories().subscribe( data => { this.categories = data; }, err => console.log("get error: ", err) ); }
اکنون چون این خاصیت در دسترس است، میتوان به قالب این کامپوننت مراجعه کرده و قسمت ابتدایی فرم را تکمیل کرد:
<div class="container"> <h3>Cascading Drop-down Lists</h3> <form #form="ngForm" (submit)="submitForm(form)" novalidate> <div class="form-group"> <label class="control-label">Category</label> <span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="categories.length == 0"></span> <select class="form-control" name="categoryCtrl" #categoryCtrl (change)="fetchProducts(categoryCtrl.value)" [(ngModel)]="model.categoryId"> <option value="undefined">Select a Category...</option> <option *ngFor="let category of categories" value="{{category.categoryId}}"> {{ category.categoryName }} </option> </select> </div>
- سپس ngModel به خاصیت categoryId وهلهای از کلاس ProductGroupForm که مدل معادل فرم است، متصل شدهاست.
- همچنین با اتصال به رخداد change، مقدار Id عضو انتخابی به متد fetchProducts ارسال میشود. دسترسی به این Id از طریق یک template reference variable به نام categoryCtrl# انجام شدهاست.
- در آخر، ngFor تعریف شده به ازای هر عضو آرایهی categories، یکبار تگ option را تکرار میکند و در هربار تکرار، مقدار ویژگی value را به categoryId تنظیم میکند و برچسب نمایشی آنرا از categoryName دریافت خواهد کرد.
بنابراین مرحلهی بعدی تکمیل این drop down آبشاری، واکنش نشان دادن به رخداد change و تکمیل متد fetchProducts است:
products: Product[] = []; isLoadingProducts = false; fetchProducts(categoryId?: number) { console.log(categoryId); this.products = []; if (categoryId === undefined || categoryId.toString() === "undefined") { return; } this.isLoadingProducts = true; this.productItemsService.getProducts(categoryId).subscribe( data => { this.products = data; this.isLoadingProducts = false; }, err => { console.log("get error: ", err); this.isLoadingProducts = false; } ); }
- سپس بررسی میکنیم که آیا categoryId دریافتی undefined است یا خیر؟ این مساله دو علت دارد:
الف) اولین عضو drop down انتخاب محصولات را با مقدار undefined مشخص کردهایم:
<option value="undefined">Select a Category...</option>
public categoryId?: number
model = new ProductGroupForm();
- پس از آن همانند قسمت قبل، این categoryId را به سرور ارسال کرده و سپس اطلاعات متناظری را دریافت و به خاصیت عمومی products نسبت دادهایم. همچنین از یک خاصیت عمومی دیگر به نام isLoadingProducts نیز استفاده شدهاست تا مشخص شود چه زمانی کار دریافت اطلاعات از سرور خاتمه پیدا میکند. از آن برای نمایش یک آیکن چرخندهی دیگر استفاده میکنیم:
<div class="form-group"> <label class="control-label">Product</label> <span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="isLoadingProducts"></span> <select class="form-control" name="productCtrl" [(ngModel)]="model.productId"> <option value="undefined">Select a Product...</option> <option *ngFor="let product of products" value="{{product.productId}}"> {{ product.productName }} </option> </select> </div>
/* Spinner */ .spinner { font-size:15px; z-index:10 } .glyphicon-spin { -webkit-animation: spin 1000ms infinite linear; animation: spin 1000ms infinite linear; } @-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-06.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی دستورات
>npm install >ng build --watch
>dotnet restore >dotnet watch run
دریافت اوبونتو
برای دریافت اوبونتو به آدرس ذیل مراجعه نمائید.
نسخه سرور آن GUI ندارد (هرچند بعدا در طی یک بسته 450 مگابایتی قابل نصب است). نسخه دسکتاپ آن به همراه GUI نیز هست. البته برای نصب دات نت بر روی آن این مساله تفاوتی نمیکند. برای نصب آزمایشی و مجازی آن هم میتوانید برای مثال از VMWare workstation استفاده کنید؛ بدون اینکه نیاز داشته باشید این توزیع خاص لینوکس را واقعا بر روی کامپیوتر خود نصب کنید.
در تمام قسمتهای ذیل فرض بر این است که ترمینال خط فرمان لینوکس را گشودهاید و همچنین سیستم به اینترنت وصل است.
دریافت Git و Curl
ابتدا دستور زیر را در خط فرمان لینوکس اجرا کنید تا سیستم بستههای لینوکس به روز شده و همچنین یک سری پیشنیاز مانند git ، curl و امثال آن نصب شوند (کتابخانه curl جهت استفاده در محیطهای برنامه نویسی کاربرد دارد و همچنین برنامه پیشرفتهای است برای کار با وب و دریافت فایلها):
sudo apt-get update && sudo apt-get -y install git-core curl python-software-properties sudo apt-get install build-essential automake checkinstall intltool git
نصب آخرین نگارش Mono و وابستگیهای آن
در ادامه نوبت به نصب آخرین نگارش مونو است. از روش متداول ذیل برای نصب مونو استفاده نکنید :
sudo apt-get install mono-complete
sudo apt-get purge mono-runtime
و یا اگر آدرس فوق برقرار نبود از اینجا: install_mono-3.0-sh
برای نمونه جهت نصب mono نگارش 3 از اسکریپت install_mono-3.0.sh به نحو ذیل استفاده خواهیم کرد (این دستورات را به ترتیب در ترمینال لینوکس اجرا کنید):
mkdir mono-3.0 cd mono-3.0 wget --no-check-certificate https://github.com/nathanb/iws-snippets/raw/master/mono-install-scripts/ubuntu/install_mono-3.0.sh chmod 755 install_mono-3.0.sh ./install_mono-3.0.sh
بعد از اجرای فرمان فوق به خطای ذیل خواهید رسید:
config.status: error: cannot find input file: `po/mcs/Makefile.in.in'
برای رفع آن ابتدا به مسیر ذیل وارد شوید (پوشه build/mono-3.0.10/po)، فایل mcs را حذف (این مورد در اصل یک پوشه است و نه یک فایل) و سپس بسته اصلی mono را از github دریافت کنید. آنرا unzip کرده و کل پوشه mcs داخل آنرا به درون پوشه po جاری کپی کنید. سپس فایل zip دریافت شده را حذف کنید:
cd mono-3.0/build/mono-3.0.10/po rm mcs wget https://github.com/mono/mono/archive/master.zip unzip master.zip mv mcs/ mono-3.0/build/mono-3.0.10/po rm -rf mono-master master.zip
پس از باز سازی پوشه مفقود mcs، باید مرحله «building mono packages» موجود در فایل install_mono-3.0.sh اجرا شود. برای این منظور، فایل final-build-mono-3.0.sh را از آدرس ذیل دریافت و در کنار فایل install_mono-3.0.sh موجود کپی کنید.
سپس در خواص این فایل، مجوز execute را نیز فعال نمائید. اکنون آنرا اجرا کنید:
./final-build-mono-3.0.sh
اکنون مدتی صبر کنید تا کار کامپایل نهایی تمام بستههای دریافت شده پس از اجرای اسکریپت final-build-mono-3.0.sh انجام شود.
آزمایش Mono نصب شده
برای اینکه مطمئن شویم، Mono درست نصب شده است، دستور زیر را در خط فرمان صادر کنید:
/opt/mono-3.0/bin/mono -V
برای اینکه این مسیر را به Path لینوکس اضافه کنیم تا قادر شویم فرمان mono را در هر مسیری اجرا کنیم، ابتدا دستور ذیل را اجرا کرده
sudo nano /etc/environment
:/opt/mono-3.0/bin
بعد ctrl+x را زده، به پیام ذخیره سازی تغییرات پاسخ مثبت دهید. سپس نیاز است یکبار logoff و login کنید تا این تغییرات اعمال شوند.
یک نکته تکمیلی:
اگر به صفحه نگارشهای رسمی Mono 3.x مراجعه کنید، نگارشهای جدیدتری را نیز میتوانید ملاحظه کنید. فایلهای قابل نصب آنها نیر در آدرسهای ذیل قرار دارند:
برای استفاده از اسکریپت install_mono-3.0.sh با این نگارشهای جدیدتر فقط کافی است تعاریف ذیل را بر اساس شماره نگارش بستههای جدید اصلاح کنید:
PACKAGES=("mono-3.0.10" "libgdiplus-2.10.9" "gtk-sharp-2.12.11" "xsp-2.10.2" "mod_mono-2.10") URLS=("http://download.mono-project.com/sources/mono/mono-3.0.10.tar.bz2" "http://download.mono-project.com/sources/libgdiplus/libgdiplus-2.10.9.tar.bz2" "http://download.mono-project.com/sources/gtk-sharp212/gtk-sharp-2.12.11.tar.bz2" "http://download.mono-project.com/sources/xsp/xsp-2.10.2.tar.bz2" "http://download.mono-project.com/sources/mod_mono/mod_mono-2.10.tar.bz2")
10.Visual Studio 2017 15.9 منتشر شد
- We have fixed an issue with debugging using Docker when a web proxy is configured.
- In debugging using Docker, you will now experience improved error handling for failures related to drive sharing configuration (for example, expired credentials).
با انتخاب ok باز نیز با قالب جدیدی به شکل زیر برای انتخاب پروژه مواجه میشوید.
اینجا همه چیز تکراری است به غیر از گزینه Configure Authentication.
همهی گزینهها تکراری اند به غیر از گزینه Individual User Accounts. البته این همان FormsAuthentication قبلی است. نکته قابل توجه، یکپارچی آن با سرویسهای اجتماعی و شبکههای سرویس دهنده است. البته در نسخهی قبلی نیز این سیستم وجود داشت، ولی این دفعه با ASP.NET Identity یک پارچه است که در ادامه بیشتر آن را خواهید دید.
البته گویا حالت دیگری به نام Organizational Accounts نیز وجود دارد که گویا برای فعال سازی،باید یک بستهی به روز رسانی دریافت میکردم، که من نکردم.(اینترنت حجمی و شبانه دانلود کردن...)
این حالت که در شکل زیر مشخص است، امکان یکپارچگی احراز هویت با Active Directory در windows server و azure را دارد.
با توجه به پوشهی مدل این را متوجه میشویم که مایکروسافت هم به لزوم ViewModel اعتقاد پیدا کرده است.
با اجرا کردن پروژه bootstrap و responsive بودن آن، خودنمایی میکنند.