SELECT * FROM [List$]
ASP.NET MVC #7
public class Products : List<Product>
public interface IShoppingCartService { ShoppingCart GetContents(); ShoppingCart AddItemToCart(int itemId, int quantity); } public class ShoppingCartService : IShoppingCartService { public ShoppingCart GetContents() { throw new NotImplementedException("Get cart from Persistence Layer"); } public ShoppingCart AddItemToCart(int itemId, int quantity) { throw new NotImplementedException("Add Item to cart then return updated cart"); } } public class ShoppingCart { public List<product> Items { get; set; } } public class Product { public int ItemId { get; set; } public string ItemName { get; set; } } public class ShoppingCartController : Controller { //Concrete object below points to actual service //private ShoppingCartService _shoppingCartService; //loosely coupled code below uses the interface rather than the //concrete object private IShoppingCartService _shoppingCartService; public ShoppingCartController() { _shoppingCartService = new ShoppingCartService(); } public ActionResult GetCart() { //now using the shared instance of the shoppingCartService dependency ShoppingCart cart = _shoppingCartService.GetContents(); return View("Cart", cart); } public ActionResult AddItemToCart(int itemId, int quantity) { //now using the shared instance of the shoppingCartService dependency ShoppingCart cart = _shoppingCartService.AddItemToCart(itemId, quantity); return View("Cart", cart); } }
//loosely coupled code below uses the interface rather //than the concrete object private IShoppingCartService _shoppingCartService; //MVC uses this constructor public ShoppingCartController() { _shoppingCartService = new ShoppingCartService(); } //You can use this constructor when testing to inject the //ShoppingCartService dependency public ShoppingCartController(IShoppingCartService shoppingCartService) { _shoppingCartService = shoppingCartService; }
[TestClass] public class ShoppingCartControllerTests { [TestMethod] public void GetCartSmokeTest() { //arrange ShoppingCartController controller = new ShoppingCartController(new ShoppingCartServiceStub()); // Act ActionResult result = controller.GetCart(); // Assert Assert.IsInstanceOfType(result, typeof(ViewResult)); } } /// <summary> /// This is is a stub of the ShoppingCartService /// </summary> public class ShoppingCartServiceStub : IShoppingCartService { public ShoppingCart GetContents() { return new ShoppingCart { Items = new List<product> { new Product {ItemId = 1, ItemName = "Widget"} } }; } public ShoppingCart AddItemToCart(int itemId, int quantity) { throw new NotImplementedException(); } }
روش جاری شباهت زیادی به استفاده از 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 در مواقعی نیاز است تا محتوایی را به صورت داینامیک به کدهای موجود در یک app Angular تزریق کنیم و یا محتوای موجود را تغییر دهیم. اگر این محتوای لود شده دارای دایرکتیوهای Angularباشد بعد از لود محتوا متوجه میشویم که کدهای Angular ما کار نمیکنند. چرا؟ اینجاست که نقش compile$ مشخص میشود. از طریق compile$ محتوای لود شده مجددا در محدوده تحت مدیریت Angular قرار گرفته و مشکل حل میشود. مثال زیر را بررسی میکنیم :
app.directive("otcDynamic", function(){ return { link: function(scope, element){ element.html("<button ng-click='doSomething()'>{{label}}</button>"); } }; });
در کد فوق ما یک دکمه را که در کلیک آن قرار است متدی توسط Angular فراخوانی شود، به صورت داینامیک ایجاد کرده و به المنت مورد نظرمان اضافه کردهایم. دقت داشته باشید که این محتوا ممکن است مثلا توسط درخواستهای Ajax از جایی فراخوانی شود. با کلیک بر روی دکمهی ایجاد شده، متد doSomething اجرا نخواهد شد؛ چرا که Angular قادر به تشخیص HTML تزریق شده نمیباشد و در واقع اصلا از وجود کد ما خبری ندارد. برای حل این مشکل از compile $ استفاده میکنیم:
app.directive("otcDynamic", function($compile){ return{ link: function(scope, element){ var template = "<button ng-click='doSomething()'>{{label}}</button>"; var linkFn = $compile(template); var content = linkFn(scope); element.append(content); } } });
با استفاده از compile $ در واقع ما
کدهای خود را مجددا تحت مدیریت Angular قرار میدهیم.
مثال فوق یک مثال ساده از این مبحث بود. در واقع کدهای تزریق شده میتوانند شامل کل
HTML تحت
مدیریت Angular
باشد که مثلا از طریق درخواستهای Ajax بهروز رسانی میشوند.
Smarter way to organize “import” statements using “index.ts” file(s) in Angular
لیستی از Icônes
چک لیست تهیه یک برنامه ASP.NET MVC
emit$:
دو کنترلر به نامهای FirstCtrl و SecondCtrl داریم. FirstCtrl به عنوان والد کنترلر Second است(در این مورد در این پست توضیح داده شده است).
پس فایل html نیز به صورت زیر خواهد بود:
<body ng-app> <div ng-controller="FirstCtrl"> <p>{{title}}</p> <div ng-controller="SecondCtrl"> <button ng-click="onUpdate()">Update First Ctrl Title</button> </div> </div> </body>
function ChildCtrl($scope){ $scope.onUpdate = function(){ this.$emit("Update_Title", "Good Bye"); }; }
function FirstCtrl($scope){ $scope.title= "Hello"; $scope.$on("Update_Title", function(event, message){ $scope.title= message; }); }
»پارامتر دوم سرویس on$ برابر با مقدار جدید ارسال شده توسط سرویس emit$ است.
»نام رویدادی که به عنوان پارامتر به on$ پاس داده میشود باید برابر با نام رویداد پاس داده شده به emit$ باشد.
»میتوان چندین پارامتر را با استفاده از emit$ ارسال کرد و در سرویس on$ با تعریف متغیر به تعداد پارامترها مقادیر آنها را دریافت نمود.
broadcast$
همان طور که مشاهده کردید SecondCtrl در محدوده FirstCtrl تعریف شده است. در نتیجه به راحتی با استفاده از سرویس emit$ توانستیم یک رویداد را منتشر نماییم. اما نکته مهم این است که اگر قصد داشته باشیم یک رویداد را از کنترلر والد (در این جا FirstCtrl است) منتشر نماییم به طوری که در کنترلرهای فرزند قابل دریافت باشد(حرکت رویداد بالا به پایین است)، باید از broadcast$ استفاده کنیم.
»broadcast$ فقط از نظر کاربرد با emit$ متفاوت است و در پیاده سازی کاملا مشابه هستند.
یک مثال:
function ParentCtrl($scope){ $scope.foo = "Hello"; $scope.$on("UPDATE_PARENT", function(event, message){ $scope.title= message; $scope.$broadcast("DO_BIDDING", { buttonTitle : "Taken over", onButtonClick : function(){ $scope.title= "HAHA this button no longer works!"; } }); }); } function ChildCtrl($scope){ $scope.buttonTitle = "Update Parent"; $scope.onButtonClick = function(){ this.$emit("UPDATE_PARENT", "Updated"); }; $scope.$on("DO_BIDDING", function(event, data){ for(var i in data){ $scope[i] = data[i]; } }); }
اگر حالت فرزند و والد بین کنترلرها نباشد چه؟
در این حالت باید rootScope$ را به کنترلر مورد نظر تزریق نمایید و سپس با استفاده از سرویس broadcast$ یا emit$ رویدادتان را منتشر کنید. مثال:
'use strict'; angular.module('myAppControllers', []) .controller('FirstCtrl', function ($rootScope) { $rootScope.$broadcast('UPDATE_ALL'); Or $rootScope.$emit('UPDATE_ALL'); });
از آن جا که حرکت بالا به پایین event bubbling بسیار هزینه برتر است نسبت به حرکت پایین به بالا در نتیجه سعی کنید تا جای ممکن از rootScope$.$broadcast استفاده نکنید. در این جا توضیح کاملی درباره دلایل عدم استفاده از rootScope$.$broadcast داده شده است.
هم چنین میتوانید یک مثال Live را نیز برای مقایسه بین emit$ و broadcast$ در این جا مشاهده کنید.