ایجاد AutoComplete با استفاده از AngularJS با Web API در MVC
جمعه 7 خرداد 1395در این مقاله خواهیم آموخت چگونه می توان یک textbox با خاصیت AutoComplete به همراه داده هایی از SQL server ایجاد کرد. از معماری MVC به همراه Web API و AngularJS برای واکشی داده ها استفاده میکنیم.
در این مقاله خواهیم آموخت چگونه می توان یک textbox با خاصیت AutoComplete به همراه داده هایی از SQL server ایجاد کرد. از معماری MVC به همراه Web API و AngularJS برای واکشی داده ها استفاده میکنیم.
یک پروژه MVC با انتخاب گزینه های MVC و Web API به صورت زیر ایجاد میکنیم.
ساختار برنامه به صورت زیر خواهد بود:
قبل از رفتن به سراغ بخش کد نویسی، از نصب منابع و افزونه های مورد نیاز اطمینان حاصل کنید. AngularJS و jQuery را باید نصب کرد.
می توانید آنها را با استفاده از NuGet و انتخاب گزینه Manage NuGet Pakages نصب کنید.
همانطور که گفته شد از AngularJS برای عملیات های سمت سرویس گیرنده استفاده می شود ، بنا براین بهتر است اول فایل اسکریپت AngularJS را ایجاد کنیم. برای این کار یک فایل جاوا اسکریپت با نام Home.js که اسکریپتهای خود را در آن خواهیم نوشت ایجاد میکنیم. اکنون یک کنترلر Web API ایجاد کرده و داده ها را با فرمت JSON از دیتابیس دریافت میکنیم.
ابتدا دیتابیس خود را آماده میکنیم که بتوانیم Entity Model برای اپلیکیشن خود را ایجاد کنیم.
از کد زیر می توانید برای ایجاد یک دیتابیس در SQL استفاده کنید.
USE [master] GO /****** Object: Database [TrialsDB] CREATE DATABASE [TrialsDB] CONTAINMENT = NONE ON PRIMARY ( NAME = N'TrialsDB', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\TrialsDB.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ) LOG ON ( NAME = N'TrialsDB_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\TrialsDB_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%) GO ALTER DATABASE [TrialsDB] SET COMPATIBILITY_LEVEL = 110 GO IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled')) begin EXEC [TrialsDB].[dbo].[sp_fulltext_database] @action = 'enable' end GO ALTER DATABASE [TrialsDB] SET ANSI_NULL_DEFAULT OFF GO ALTER DATABASE [TrialsDB] SET ANSI_NULLS OFF GO ALTER DATABASE [TrialsDB] SET ANSI_PADDING OFF GO ALTER DATABASE [TrialsDB] SET ANSI_WARNINGS OFF GO ALTER DATABASE [TrialsDB] SET ARITHABORT OFF GO ALTER DATABASE [TrialsDB] SET AUTO_CLOSE OFF GO ALTER DATABASE [TrialsDB] SET AUTO_CREATE_STATISTICS ON GO ALTER DATABASE [TrialsDB] SET AUTO_SHRINK OFF GO ALTER DATABASE [TrialsDB] SET AUTO_UPDATE_STATISTICS ON GO ALTER DATABASE [TrialsDB] SET CURSOR_CLOSE_ON_COMMIT OFF GO ALTER DATABASE [TrialsDB] SET CURSOR_DEFAULT GLOBAL GO ALTER DATABASE [TrialsDB] SET CONCAT_NULL_YIELDS_NULL OFF GO ALTER DATABASE [TrialsDB] SET NUMERIC_ROUNDABORT OFF GO ALTER DATABASE [TrialsDB] SET QUOTED_IDENTIFIER OFF GO ALTER DATABASE [TrialsDB] SET RECURSIVE_TRIGGERS OFF GO ALTER DATABASE [TrialsDB] SET DISABLE_BROKER GO ALTER DATABASE [TrialsDB] SET AUTO_UPDATE_STATISTICS_ASYNC OFF GO ALTER DATABASE [TrialsDB] SET DATE_CORRELATION_OPTIMIZATION OFF GO ALTER DATABASE [TrialsDB] SET TRUSTWORTHY OFF GO ALTER DATABASE [TrialsDB] SET ALLOW_SNAPSHOT_ISOLATION OFF GO ALTER DATABASE [TrialsDB] SET PARAMETERIZATION SIMPLE GO ALTER DATABASE [TrialsDB] SET READ_COMMITTED_SNAPSHOT OFF GO ALTER DATABASE [TrialsDB] SET HONOR_BROKER_PRIORITY OFF GO ALTER DATABASE [TrialsDB] SET RECOVERY FULL GO ALTER DATABASE [TrialsDB] SET MULTI_USER GO ALTER DATABASE [TrialsDB] SET PAGE_VERIFY CHECKSUM GO ALTER DATABASE [TrialsDB] SET DB_CHAINING OFF GO ALTER DATABASE [TrialsDB] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF ) GO ALTER DATABASE [TrialsDB] SET TARGET_RECOVERY_TIME = 0 SECONDS GO ALTER DATABASE [TrialsDB] SET READ_WRITE GO
جدولی با نام Products را در آن با استفاده از کدهای زیر ایجاد میکنیم.
USE [TrialsDB] GO /****** Object: Table [dbo].[Product] SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Product]( [ProductID] [int] NOT NULL, [Name] [nvarchar](max) NOT NULL, [ProductNumber] [nvarchar](25) NOT NULL, [MakeFlag] [bit] NOT NULL, [FinishedGoodsFlag] [bit] NOT NULL, [Color] [nvarchar](15) NULL, [SafetyStockLevel] [smallint] NOT NULL, [ReorderPoint] [smallint] NOT NULL, [StandardCost] [money] NOT NULL, [ListPrice] [money] NOT NULL, [Size] [nvarchar](5) NULL, [SizeUnitMeasureCode] [nchar](3) NULL, [WeightUnitMeasureCode] [nchar](3) NULL, [Weight] [decimal](8, 2) NULL, [DaysToManufacture] [int] NOT NULL, [ProductLine] [nchar](2) NULL, [Class] [nchar](2) NULL, [Style] [nchar](2) NULL, [ProductSubcategoryID] [int] NULL, [ProductModelID] [int] NULL, [SellStartDate] [datetime] NOT NULL, [SellEndDate] [datetime] NULL, [DiscontinuedDate] [datetime] NULL, [rowguid] [uniqueidentifier] ROWGUIDCOL NOT NULL, [ModifiedDate] [datetime] NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO
و برای وارد کردن داده به آن از کدهای زیر استفاده میکنیم.
USE [TrialsDB] GO INSERT INTO [dbo].[Product] ([ProductID] ,[Name] ,[ProductNumber] ,[MakeFlag] ,[FinishedGoodsFlag] ,[Color] ,[SafetyStockLevel] ,[ReorderPoint] ,[StandardCost] ,[ListPrice] ,[Size] ,[SizeUnitMeasureCode] ,[WeightUnitMeasureCode] ,[Weight] ,[DaysToManufacture] ,[ProductLine] ,[Class] ,[Style] ,[ProductSubcategoryID] ,[ProductModelID] ,[SellStartDate] ,[SellEndDate] ,[DiscontinuedDate] ,[rowguid] ,[ModifiedDate]) VALUES (<ProductID, int,> ,<Name, nvarchar(max),> ,<ProductNumber, nvarchar(25),> ,<MakeFlag, bit,> ,<FinishedGoodsFlag, bit,> ,<Color, nvarchar(15),> ,<SafetyStockLevel, smallint,> ,<ReorderPoint, smallint,> ,<StandardCost, money,> ,<ListPrice, money,> ,<Size, nvarchar(5),> ,<SizeUnitMeasureCode, nchar(3),> ,<WeightUnitMeasureCode, nchar(3),> ,<Weight, decimal(8,2),> ,<DaysToManufacture, int,> ,<ProductLine, nchar(2),> ,<Class, nchar(2),> ,<Style, nchar(2),> ,<ProductSubcategoryID, int,> ,<ProductModelID, int,> ,<SellStartDate, datetime,> ,<SellEndDate, datetime,> ,<DiscontinuedDate, datetime,> ,<rowguid, uniqueidentifier,> ,<ModifiedDate, datetime,>) GO
مرحله بعدی ایجاد یک ADO.NET Entity Data Model است. پس از طی این روند یک فایل edmx را می توانید در پوشه model مشاهده کنید. و هنگامی که این فایل را باز کنید جدول Product را به صورت زیر مشاهده میکنید.
برای ایجاد کنترلر Web API بر روی پوشه کنترلر کلیک راست کرده و با انتخاب گزینه Add در پنجره باز شده Web API 2 controller with actions,using Entity Framework را انتخاب میکنیم.
برای کلاس مدل Product(AngularJSAutocompleteInMVCWithWebAPI.Models) را انتخاب میکنیم.
و برای کلاس data Context گزینه TrialsDBEntities(AngularJSAutocompleteInMVCWithWebAPI.Models) را انتخاب میکینم.
همانطور که میبینید نام کنترلر نیز Products قرار میگیرد. کدهای زیر در این کنترلر قرار میگیرند.
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Http.Description; using WebApplication19.Models; namespace WebApplication19.Controllers { public class ProductsController : ApiController { private TrialsDBEntities db = new TrialsDBEntities(); // GET: api/Products public IQueryable<Product> GetProducts() { return db.Product; } // GET: api/Products/5 [ResponseType(typeof(Product))] public IHttpActionResult GetProduct(int id) { Product product = db.Product.Find(id); if (product == null) { return NotFound(); } return Ok(product); } // PUT: api/Products/5 [ResponseType(typeof(void))] public IHttpActionResult PutProduct(int id, Product product) { if (!ModelState.IsValid) { return BadRequest(ModelState); } if (id != product.ProductID) { return BadRequest(); } db.Entry(product).State = EntityState.Modified; try { db.SaveChanges(); } catch (DbUpdateConcurrencyException) { if (!ProductExists(id)) { return NotFound(); } else { throw; } } return StatusCode(HttpStatusCode.NoContent); } // POST: api/Products [ResponseType(typeof(Product))] public IHttpActionResult PostProduct(Product product) { if (!ModelState.IsValid) { return BadRequest(ModelState); } db.Product.Add(product); try { db.SaveChanges(); } catch (DbUpdateException) { if (ProductExists(product.ProductID)) { return Conflict(); } else { throw; } } return CreatedAtRoute("DefaultApi", new { id = product.ProductID }, product); } // DELETE: api/Products/5 [ResponseType(typeof(Product))] public IHttpActionResult DeleteProduct(int id) { Product product = db.Product.Find(id); if (product == null) { return NotFound(); } db.Product.Remove(product); db.SaveChanges(); return Ok(product); } protected override void Dispose(bool disposing) { if (disposing) { db.Dispose(); } base.Dispose(disposing); } private bool ProductExists(int id) { return db.Product.Count(e => e.ProductID == id) > 0; } } }
از آنجایی که فقط به عملیات خواندن نیاز است می توانید سایر قابلیت ها را حذف کنید و فقط متدهای Get را نگه دارید.
// GET: api/Products public IQueryable<Product> GetProducts() { return db.Products; }
اکنون بخش واکشی داده ها از دیتابیس آماده است. برای تست آن به آدرس http://localhost:8053/api/products می رویم . نتیجه به صورت زیر خواهد بود.
به فایل AngularJS برگشته و Web API را در آن استفاده خواهیم کرد. باید کدها در Home.js به صورت زیر تغییر کنند.
(function () { 'use strict'; angular .module('MyApp', ['ngMaterial', 'ngMessages', 'material.svgAssetsCache']) .controller('AutoCompleteCtrl', AutoCompleteCtrl); function AutoCompleteCtrl($http, $timeout, $q, $log) { var self = this; self.simulateQuery = true; self.products = loadAllProducts($http); self.querySearch = querySearch; function querySearch(query) { var results = query ? self.products.filter(createFilterFor(query)) : self.products, deferred; if (self.simulateQuery) { deferred = $q.defer(); $timeout(function () { deferred.resolve(results); }, Math.random() * 1000, false); return deferred.promise; } else { return results; } } function loadAllProducts($http) { var allProducts = []; var url = ''; var result = []; url = 'api/products'; $http({ method: 'GET', url: url, }).then(function successCallback(response) { allProducts = response.data; angular.forEach(allProducts, function (product, key) { result.push( { value: product.Name.toLowerCase(), display: product.Name }); }); }, function errorCallback(response) { console.log('Oops! Something went wrong while fetching the data. Status Code: ' + response.status + ' Status statusText: ' + response.statusText); }); return result; } function createFilterFor(query) { var lowercaseQuery = angular.lowercase(query); return function filterFn(product) { return (product.value.indexOf(lowercaseQuery) === 0); }; } } })();
در اینجا MyApp نام ماژول و AutoCompleteCtrl نام کنترلر ماست . تابع loadAllProducts برای بارگذاری همه محصولات از دیتابیس با استفاده از http in Angular JS$ است. هنگامی که سرویس ما فراخوانی می شود، داده ها را دریافت خواهیم کرد. داده ها در متغیری ذخیره خواهند شد. حلقه ای را با استفاده از angular.forEach و فرمت مورد نیاز ایجاد میکنیم.
angular.forEach(allProducts, function(product, key) { result.push( { value: product.Name.toLowerCase(), display: product.Name }); });
تابع querySearch هر زمان که کاربری به جستجوی محصول خاصی بپردازد فراخوانی خواهد شد. که از طریق View به صورت زیر فراخوانی خواهد شد.
md-items="item in ctrl.querySearch(ctrl.searchText)"
به یک View برای نمایش داده ها نیاز داریم . برای این کار ابتدا یک کنترلر با نام Home ایجاد میکنیم و کدهای زیر را در آن قرار می دهیم .
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace WebApplication19.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } } }
برای ایجاد View بر روی نام کنترلر کلیک راست کرده و آن را اضافه میکنیم.
در View منابع مورد نیاز را اضافه میکنیم.
<script src="~/scripts/angular.min.js"></script> <script src="~/scripts/angular-route.min.js"></script> <script src="~/scripts/angular-aria.min.js"></script> <script src="~/scripts/angular-animate.min.js"></script> <script src="~/scripts/angular-messages.min.js"></script> <script src="~/scripts/angular-material.js"></script> <script src="~/scripts/svg-assets-cache.js"></script> <script src="~/scripts/Home.js"></script>
بعد از اضافه کردن منابع می توان کنترلر AngularJS را فراخوانی کرد و کدهای View را به صورت زیر تغییر داد.
<div ng-controller="AutoCompleteCtrl" layout="column" ng-cloak="" class="autocompletedemoBasicUsage" ng-app="MyApp" style="width: 34%;"> <md-content class="md-padding"> <form ng-submit="$event.preventDefault()"> <md-autocomplete md-no-cache="false" md-selected-item="ctrl.selectedItem" md-search-text="ctrl.searchText" md-items="item in ctrl.querySearch(ctrl.searchText)" md-item-text="item.display" md-min-length="0" placeholder="برای جستجوی محصول اینجا کلیک کنید."> <md-item-template> <span md-highlight-text="ctrl.searchText" md-highlight-flags="^i">{{item.display}}</span> </md-item-template> <md-not-found> مشابه "{{ctrl.searchText}}" یافت نشد. </md-not-found> </md-autocomplete> </form> </md-content> </div>
در اینجا md-autocomplete نتیجه دریافت شده از دیتابیس را برای جلوگیری از بازدیدهای ناخواسته به دیتابیس، cache خواهد کرد. که آن را می توان با کمک md-no-cache فعال یا غیر فعال کرد. اگر برنامه را اجرا کنید مشاهده میکنید که فراخوانی Web API به درستی کار میکند و داده ها دریافت می شود. یک Style sheet را نیز برای نمایش بهتر View اضافه میکنیم.
<link href="~/Content/angular-material.css" rel="stylesheet" />
خروجی برنامه به صورت زیر خواهد بود.
آموزش angular
- AngularJs
- 2k بازدید
- 2 تشکر