ग्लोबल इवेंट्स इन एंगुलर


224

क्या इसके बराबर $scope.emit()या $scope.broadcast()कोणीय में नहीं है?

मैं EventEmitterकार्यक्षमता जानता हूं , लेकिन जहां तक ​​मैं समझता हूं कि यह केवल मूल HTML तत्व के लिए एक घटना का उत्सर्जन करेगा।

क्या होगा अगर मुझे एफएक्स के बीच संवाद करने की आवश्यकता है। भाई बहन या डीओएम की जड़ में एक घटक और एक तत्व के बीच कई स्तरों पर घोंसला होता है?


2
मेरे पास संवाद घटक बनाने से संबंधित एक समान प्रश्न था जो डोम में किसी भी बिंदु से एक्सेस किया जा सकता था: stackoverflow.com/questions/34572539/… मूल रूप से, एक समाधान एक सेवा में एक इवेंट एमिटर डालने के लिए है
ब्रैंडो

1
यहां आरएक्सजेएस का उपयोग करके ऐसी सेवा का मेरा कार्यान्वयन है जो सदस्यता पर एनटीटी अंतिम मूल्यों को प्राप्त करने की अनुमति देता है। stackoverflow.com/questions/46027693/…
कोडवर्ड 20

जवाबों:


385

AngularJS से $scope.emit()या इसके बराबर कोई नहीं है $scope.broadcast()। एक घटक के अंदर EventEmitter करीब आता है, लेकिन जैसा कि आपने उल्लेख किया है, यह केवल तत्काल मूल घटक के लिए एक घटना का उत्सर्जन करेगा।

कोणीय में, अन्य विकल्प हैं जो मैं नीचे समझाने की कोशिश करूंगा।

@Input () बाइंडिंग एप्लिकेशन मॉडल को एक निर्देशित ऑब्जेक्ट ग्राफ (पत्तियों को रूट) में जोड़ने की अनुमति देता है। किसी घटक की परिवर्तन डिटेक्टर रणनीति का डिफ़ॉल्ट व्यवहार किसी भी जुड़े घटक से सभी बाइंडिंग के लिए एक आवेदन मॉडल में सभी परिवर्तनों का प्रचार करना है।

इसके अलावा: दो प्रकार के मॉडल हैं: मॉडल और एप्लिकेशन मॉडल देखें। एक एप्लिकेशन मॉडल @Input () बाइंडिंग के माध्यम से जुड़ा हुआ है। एक दृश्य मॉडल सिर्फ एक घटक संपत्ति है (@Input () के साथ सजाया नहीं गया है) जो घटक के टेम्पलेट में बंधा हुआ है।

अपने सवालों के जवाब देने के लिए:

क्या होगा अगर मुझे सिबलिंग घटकों के बीच संवाद करने की आवश्यकता है?

  1. साझा किया गया एप्लिकेशन मॉडल : भाई-बहन एक साझा एप्लिकेशन मॉडल (कोणीय 1 की तरह) के माध्यम से संवाद कर सकते हैं। उदाहरण के लिए, जब एक भाई किसी मॉडल में बदलाव करता है, तो दूसरे भाई जिस मॉडल से बाइंडिंग करते हैं, वह स्वचालित रूप से अपडेट हो जाता है।

  2. घटक ईवेंट : चाइल्ड घटक @Output () बाइंडिंग का उपयोग करके किसी ईवेंट को मूल घटक में उत्सर्जित कर सकते हैं। मूल घटक ईवेंट को संभाल सकता है, और एप्लिकेशन मॉडल में हेरफेर कर सकता है या यह स्वयं का दृश्य मॉडल है। एप्लिकेशन मॉडल में परिवर्तन स्वचालित रूप से सभी घटकों के लिए प्रचारित किया जाता है जो प्रत्यक्ष या अप्रत्यक्ष रूप से एक ही मॉडल से जुड़ते हैं।

  3. सेवा ईवेंट : घटक सेवा ईवेंट की सदस्यता ले सकते हैं। उदाहरण के लिए, दो भाई घटक एक ही सेवा घटना के लिए सदस्यता ले सकते हैं और अपने संबंधित मॉडल को संशोधित करके जवाब दे सकते हैं। इस पर अधिक नीचे।

मैं एक रूट घटक और एक घटक के बीच संवाद कैसे कर सकता हूं जिसमें कई स्तर गहरे हैं?

  1. साझा किए गए एप्लिकेशन मॉडल : एप्लिकेशन मॉडल को रूट घटकों से नीचे @ नेपुट () बाइंडिंग के माध्यम से गहराई से नेस्टेड घटकों को पास किया जा सकता है। किसी भी घटक से एक मॉडल में परिवर्तन स्वचालित रूप से सभी घटकों को प्रचारित करेगा जो समान मॉडल साझा करते हैं।
  2. सेवा कार्यक्रम : आप EventEmitter को एक साझा सेवा में भी स्थानांतरित कर सकते हैं, जो किसी भी घटक को सेवा को स्कैन करने और घटना की सदस्यता लेने की अनुमति देता है। इस तरह, एक रूट घटक एक सेवा पद्धति (आमतौर पर मॉडल को बदलने) को कॉल कर सकता है, जो बदले में एक घटना का उत्सर्जन करता है। कई परतें नीचे, एक भव्य-बाल घटक जिसने सेवा को भी इंजेक्ट किया है और उसी घटना के लिए सदस्यता ली है, इसे संभाल सकता है। कोई भी ईवेंट हैंडलर जो एक साझा एप्लिकेशन मॉडल को बदलता है, स्वचालित रूप से उस पर निर्भर सभी घटकों को प्रचारित करेगा। यह संभवतः $scope.broadcast()कोणीय 1. से निकटतम समतुल्य है । अगला भाग इस विचार का अधिक विस्तार से वर्णन करता है।

एक अवलोकन योग्य सेवा का उदाहरण जो परिवर्तन को प्रसारित करने के लिए सेवा घटनाओं का उपयोग करता है

यहां एक अवलोकन योग्य सेवा का एक उदाहरण है जो परिवर्तनों को प्रसारित करने के लिए सेवा घटनाओं का उपयोग करता है। जब एक TodoItem जोड़ा जाता है, तो सेवा अपने घटक ग्राहकों को सूचित करने वाली घटना का उत्सर्जन करती है।

export class TodoItem {
    constructor(public name: string, public done: boolean) {
    }
}
export class TodoService {
    public itemAdded$: EventEmitter<TodoItem>;
    private todoList: TodoItem[] = [];

    constructor() {
        this.itemAdded$ = new EventEmitter();
    }

    public list(): TodoItem[] {
        return this.todoList;
    }

    public add(item: TodoItem): void {
        this.todoList.push(item);
        this.itemAdded$.emit(item);
    }
}

यहां बताया गया है कि रूट घटक किस तरह से इस कार्यक्रम की सदस्यता लेगा:

export class RootComponent {
    private addedItem: TodoItem;
    constructor(todoService: TodoService) {
        todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
    }

    private onItemAdded(item: TodoItem): void {
        // do something with added item
        this.addedItem = item;
    }
}

एक बाल घटक ने कई स्तरों पर घोंसला बनाया, उसी तरह से इस घटना की सदस्यता लेंगे:

export class GrandChildComponent {
    private addedItem: TodoItem;
    constructor(todoService: TodoService) {
        todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
    }

    private onItemAdded(item: TodoItem): void {
        // do something with added item
        this.addedItem = item;
    }
}

यहां वह घटक है जो सेवा को ईवेंट को ट्रिगर करने के लिए कहता है (यह घटक ट्री में कहीं भी रह सकता है):

@Component({
    selector: 'todo-list',
    template: `
         <ul>
            <li *ngFor="#item of model"> {{ item.name }}
            </li>
         </ul>
        <br />
        Add Item <input type="text" #txt /> <button (click)="add(txt.value); txt.value='';">Add</button>
    `
})
export class TriggeringComponent{
    private model: TodoItem[];

    constructor(private todoService: TodoService) {
        this.model = todoService.list();
    }

    add(value: string) {
        this.todoService.add(new TodoItem(value, false));
    }
}

संदर्भ: एंगुलर में परिवर्तन का पता लगाएं


27
मैंने कुछ पोस्टों में अब एक अनुशीलन या EventEmitter के लिए अनुगामी $ को देखा है - उदाहरण के लिए itemAdded$,। यह एक आरएक्सजेएस सम्मेलन है या कुछ और? यह कहां से आता है?
मार्क राजकोक

1
अच्छा उत्तर। आपने कहा, "ऐप मॉडल में परिवर्तन स्वचालित रूप से सभी घटकों के लिए प्रचारित किया जाता है जो प्रत्यक्ष या अप्रत्यक्ष रूप से एक ही मॉडल से जुड़ते हैं।" मेरे पास एक कूबड़ है कि यह इस तरह से काम नहीं करता है (लेकिन मुझे यकीन नहीं है)। सैक्विन की अन्य ब्लॉग पोस्टstreet ऐप मॉडल की संपत्ति को बदलने वाले एक घटक का एक उदाहरण देती है , लेकिन चूंकि कोणीय 2 पहचान / संदर्भ द्वारा पहचान में परिवर्तन करता है, इसलिए कोई भी परिवर्तन प्रचारित onChangesनहीं किया जाता है ( इसे नहीं कहा जाता है), क्योंकि ऐप मॉडल का संदर्भ नहीं बदला है ( cont ...)
मार्क राजकॉक

10
आप सेवा में एक EventEmitter के बजाय एक वेधशाला का उपयोग करने के लिए अपने उत्तर को अपडेट करना चाह सकते हैं। देखें stackoverflow.com/a/35568924/215945 और stackoverflow.com/questions/36076700
मार्क राजकोक

2
हां, प्रत्यय $ Cx.js द्वारा लोकप्रिय एक RxJS सम्मेलन है। cycle.js.org/…
jody tate

4
आपको मैन्युअल रूप से एक इवेंटमेम्बर की सदस्यता नहीं लेनी चाहिए। यह अंतिम रिलीज में एक अवलोकन नहीं हो सकता है! इसे देखें: bennadel.com/blog/…
NetProvoke

49

घटनाओं को संभालने के लिए एक साझा सेवा का उपयोग करके कोणीय 2 में $ गुंजाइश. emit () या $ गुंजाइश. broadcast () के प्रतिस्थापन के उदाहरण के रूप में निम्न कोड ।

import {Injectable} from 'angular2/core';
import * as Rx from 'rxjs/Rx';

@Injectable()
export class EventsService {
    constructor() {
        this.listeners = {};
        this.eventsSubject = new Rx.Subject();

        this.events = Rx.Observable.from(this.eventsSubject);

        this.events.subscribe(
            ({name, args}) => {
                if (this.listeners[name]) {
                    for (let listener of this.listeners[name]) {
                        listener(...args);
                    }
                }
            });
    }

    on(name, listener) {
        if (!this.listeners[name]) {
            this.listeners[name] = [];
        }

        this.listeners[name].push(listener);
    }

    off(name, listener) {
        this.listeners[name] = this.listeners[name].filter(x => x != listener);
    }

    broadcast(name, ...args) {
        this.eventsSubject.next({
            name,
            args
        });
    }
}

उदाहरण का उपयोग:

प्रसारण:

function handleHttpError(error) {
    this.eventsService.broadcast('http-error', error);
    return ( Rx.Observable.throw(error) );
}

श्रोता:

import {Inject, Injectable} from "angular2/core";
import {EventsService}      from './events.service';

@Injectable()
export class HttpErrorHandler {
    constructor(eventsService) {
        this.eventsService = eventsService;
    }

    static get parameters() {
        return [new Inject(EventsService)];
    }

    init() {
        this.eventsService.on('http-error', function(error) {
            console.group("HttpErrorHandler");
            console.log(error.status, "status code detected.");
            console.dir(error);
            console.groupEnd();
        });
    }
}

यह कई तर्कों का समर्थन कर सकता है:

this.eventsService.broadcast('something', "Am I a?", "Should be b", "C?");

this.eventsService.on('something', function (a, b, c) {
   console.log(a, b, c);
});

यह क्या करता है? स्टैटिक गेट पैरामीटर () {रिटर्न [नया इंजेक्शन (घटनाक्रम सेवा)]; }
बीनवाह

इस उदाहरण में मैं Ionic 2 फ्रेमवर्क का उपयोग कर रहा हूं। स्थिर पैरामीटर विधि को कहा जाता है जब निर्माण विधि को लागू किया जाता है और इसका उपयोग निर्माणकर्ता को निर्भरता की आपूर्ति करने के लिए किया जाता है। स्पष्टीकरण यहाँ stackoverflow.com/questions/35919593/…
jim.taylor.1974

1
अच्छी तरह से किया। सरल और पूरे ऐप के लिए एक आसानी से अनुकूलनीय अधिसूचना प्रणाली प्रदान करता है, न कि केवल एक-बंद।
माइक एम

मैंने वाइल्डकार्ड समर्थन के साथ ऐसी ही सेवा बनाई है। आशा करता हूँ की ये काम करेगा। github.com/govorov/ng-radio
Stanislav E. Govorov

2
बहुत बढ़िया, इसका इस्तेमाल किया, लेकिन अगर कोई दिलचस्पी है तो एक ऑफ फंक्शन जोड़ा: off(name, listener) { this.listeners[name] = this.listeners[name].filter(x => x != listener); }
LVDM

16

मैं एक संदेश सेवा का उपयोग कर रहा हूं जो rxjs Subject(टाइपस्क्रिप्ट) लपेटता है

प्लंकर उदाहरण: संदेश सेवा

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/filter'
import 'rxjs/add/operator/map'

interface Message {
  type: string;
  payload: any;
}

type MessageCallback = (payload: any) => void;

@Injectable()
export class MessageService {
  private handler = new Subject<Message>();

  broadcast(type: string, payload: any) {
    this.handler.next({ type, payload });
  }

  subscribe(type: string, callback: MessageCallback): Subscription {
    return this.handler
      .filter(message => message.type === type)
      .map(message => message.payload)
      .subscribe(callback);
  }
}

घटक सदस्यता ले सकते हैं और घटनाओं (प्रेषक) को प्रसारित कर सकते हैं:

import { Component, OnDestroy } from '@angular/core'
import { MessageService } from './message.service'
import { Subscription } from 'rxjs/Subscription'

@Component({
  selector: 'sender',
  template: ...
})
export class SenderComponent implements OnDestroy {
  private subscription: Subscription;
  private messages = [];
  private messageNum = 0;
  private name = 'sender'

  constructor(private messageService: MessageService) {
    this.subscription = messageService.subscribe(this.name, (payload) => {
      this.messages.push(payload);
    });
  }

  send() {
    let payload = {
      text: `Message ${++this.messageNum}`,
      respondEvent: this.name
    }
    this.messageService.broadcast('receiver', payload);
  }

  clear() {
    this.messages = [];
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

(रिसीवर)

import { Component, OnDestroy } from '@angular/core'
import { MessageService } from './message.service'
import { Subscription } from 'rxjs/Subscription'

@Component({
  selector: 'receiver',
  template: ...
})
export class ReceiverComponent implements OnDestroy {
  private subscription: Subscription;
  private messages = [];

  constructor(private messageService: MessageService) {
    this.subscription = messageService.subscribe('receiver', (payload) => {
      this.messages.push(payload);
    });
  }

  send(message: {text: string, respondEvent: string}) {
    this.messageService.broadcast(message.respondEvent, message.text);
  }

  clear() {
    this.messages = [];
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

रिटर्न की subscribeविधि MessageServiceएक rxjs Subscriptionवस्तु है, जो इस तरह से सदस्यता समाप्त की जा सकती है:

import { Subscription } from 'rxjs/Subscription';
...
export class SomeListener {
  subscription: Subscription;

  constructor(private messageService: MessageService) {
    this.subscription = messageService.subscribe('someMessage', (payload) => {
      console.log(payload);
      this.subscription.unsubscribe();
    });
  }
}

यह उत्तर भी देखें: https://stackoverflow.com/a/36782616/1861779

प्लंकर उदाहरण: संदेश सेवा


2
बहुत मूल्यवान। जवाब के लिए धन्यवाद। मुझे अभी पता चला है कि आप इस तरह से दो अलग-अलग मॉड्यूल में दो घटकों के साथ संवाद नहीं कर सकते हैं । लक्ष्य को प्राप्त करने के लिए मुझे वहां प्रदाताओं को जोड़कर मैसेज सर्विस को ऐपमॉडल स्तर में पंजीकृत करना था। किसी भी तरह से यह वास्तव में अच्छा तरीका है।
रुक्शां दंगल

यह सब बहुत पुराना है। विशेष रूप से प्लंकर जो किसी भी संसाधन को सफलतापूर्वक लोड नहीं करता है। वे सभी 500 त्रुटि कोड हैं।
tatsu

मुझे मिलता हैProperty 'filter' does not exist on type 'Subject<EventMessage>'.
ड्रू

@, RxJS के नए संस्करणों का उपयोग करें this.handler.pipe(filter(...))लेटेबल ऑपरेटर देखें ।
t.888

1
@ t.888 धन्यवाद, मुझे यह समझ में आया। अद्यतित सदस्यता फ़ंक्शन ऐसा दिखता हैreturn this.handler.pipe( filter(message => message.type === type), map(message => message.payload) ).subscribe(callback);
ड्रू

12

अपनी सेवा संचार के लिए EventEmitter का उपयोग न करें

आपको अवलोकन प्रकारों में से एक का उपयोग करना चाहिए। मुझे व्यक्तिगत रूप से BehaviorSubject पसंद है।

सरल उदाहरण:

आप प्रारंभिक अवस्था को पार कर सकते हैं, यहाँ मैं अशक्त गुजर रहा हूँ

let subject = new BehaviorSubject (null);

जब आप विषय को अपडेट करना चाहते हैं

subject.next (myObject)

किसी भी सेवा या घटक से निरीक्षण करें और नए अपडेट प्राप्त होने पर कार्य करें।

subject.subscribe (this.YOURMETHOD);

यहाँ अधिक जानकारी है।


1
क्या आप इस डिजाइन निर्णय के कारणों पर विस्तार से बता सकते हैं?
mtraut

@mtraut उस लिंक की भी व्यापक व्याख्या है।
दानियाल कलबसी


वास्तव में मुझे जो चाहिए था। अच्छा और सरल :)
लो

8

आप एक इवेंटबस सेवा बनाने के लिए EventEmitter या वेधशाला का उपयोग कर सकते हैं जिसे आप DI के साथ पंजीकृत करते हैं। प्रत्येक घटक जो केवल भाग लेना चाहता है, वह सेवा को निर्माता पैरामीटर के रूप में अनुरोध करता है और घटनाओं के लिए / और सब्सक्राइब करता है।

यह सभी देखें


2

मेरा पसंदीदा तरीका यह है कि मैं अपने सभी उपसंपादकों को नियंत्रित करने के लिए व्यवहार सेवा या ईवेंट एमिटर (लगभग समान) का उपयोग करूं।

कोणीय क्ली का उपयोग करते हुए, एक नई सेवा बनाने के लिए एनजी जीएस चलाएं फिर एक व्यवहार-विषय या EventEemter का उपयोग करें

export Class myService {
#all the stuff that must exist

myString: string[] = [];
contactChange : BehaviorSubject<string[]> = new BehaviorSubject(this.myString);

   getContacts(newContacts) {
     // get your data from a webservices & when you done simply next the value 
    this.contactChange.next(newContacts);
   }
}

जब आप ऐसा करते हैं कि प्रदाता के रूप में आपकी सेवा का उपयोग करने वाला प्रत्येक घटक परिवर्तन से अवगत होगा। जैसे आप EventEmitter के साथ करते हैं वैसे ही सदस्यता लें;)

export Class myComp {
#all the stuff that exists like @Component + constructor using (private myService: myService)

this.myService.contactChange.subscribe((contacts) => {
     this.contactList += contacts; //run everytime next is called
  }
}

1

मैंने यहां एक पब-उप नमूना बनाया है:

http://www.syntaxsuccess.com/viewarticle/pub-sub-in-angular-2.0

विचार यह है कि आरएक्सजे सब्जेक्ट्स को ऑब्जर्वर और ऑब्जर्वबल्स को वायरिंग के लिए इस्तेमाल किया जाए और कस्टम इवेंट्स को छोड़ने और सब्सक्राइब करने के लिए एक जेनेरिक सॉल्यूशन के रूप में इस्तेमाल किया जाए। अपने नमूने में मैं डेमो उद्देश्यों के लिए एक ग्राहक वस्तु का उपयोग करता हूं

this.pubSubService.Stream.emit(customer);

this.pubSubService.Stream.subscribe(customer => this.processCustomer(customer));

यहाँ एक लाइव डेमो है: http://www.syntaxsuccess.com/angular-2-samples/#/demo/pub-sub


1

यह मेरा संस्करण है:

export interface IEventListenr extends OnDestroy{
    ngOnDestroy(): void
}

@Injectable()
export class EventManagerService {


    private listeners = {};
    private subject = new EventEmitter();
    private eventObserver = this.subject.asObservable();


    constructor() {

        this.eventObserver.subscribe(({name,args})=>{



             if(this.listeners[name])
             {
                 for(let listener of this.listeners[name])
                 {
                     listener.callback(args);
                 }
             }
        })

    }

    public registerEvent(eventName:string,eventListener:IEventListenr,callback:any)
    {

        if(!this.listeners[eventName])
             this.listeners[eventName] = [];

         let eventExist = false;
         for(let listener of this.listeners[eventName])
         {

             if(listener.eventListener.constructor.name==eventListener.constructor.name)
             {
                 eventExist = true;
                 break;
             }
         }

        if(!eventExist)
        {
             this.listeners[eventName].push({eventListener,callback});
        }
    }

    public unregisterEvent(eventName:string,eventListener:IEventListenr)
    {

        if(this.listeners[eventName])
        {
            for(let i = 0; i<this.listeners[eventName].length;i++)
            {

                if(this.listeners[eventName][i].eventListener.constructor.name==eventListener.constructor.name)
                {
                    this.listeners[eventName].splice(i, 1);
                    break;
                }
            }
        }


    }


    emit(name:string,...args:any[])
    {
        this.subject.next({name,args});
    }
}

उपयोग:

export class <YOURCOMPONENT> implements IEventListener{

  constructor(private eventManager: EventManagerService) {


    this.eventManager.registerEvent('EVENT_NAME',this,(args:any)=>{
       ....
    })


  }

  ngOnDestroy(): void {
    this.eventManager.unregisterEvent('closeModal',this)
  }

}

फेंकना:

 this.eventManager.emit("EVENT_NAME");

0

हमने एक ngModelChange अवलोकनीय निर्देश को लागू किया है जो एक इवेंट एमिटर के माध्यम से सभी मॉडल परिवर्तनों को भेजता है जिसे आप अपने स्वयं के घटक में तत्काल करते हैं। आपको बस अपने ईवेंट एमिटर को निर्देशन से बांधना होगा।

देखें: https://github.com/atomicbits/angular2-modelchangeobservable

Html में, अपने ईवेंट एमिटर को बाँधें (इस उदाहरण में देशबद्ध):

<input [(ngModel)]="country.name"
       [modelChangeObservable]="countryChanged" 
       placeholder="Country"
       name="country" id="country"></input>

अपने टाइपस्क्रिप्ट घटक में, EventEmitter पर कुछ async संचालन करें:

import ...
import {ModelChangeObservable} from './model-change-observable.directive'


@Component({
    selector: 'my-component',
    directives: [ModelChangeObservable],
    providers: [],
    templateUrl: 'my-component.html'
})

export class MyComponent {

    @Input()
    country: Country

    selectedCountries:Country[]
    countries:Country[] = <Country[]>[]
    countryChanged:EventEmitter<string> = new EventEmitter<string>()


    constructor() {

        this.countryChanged
            .filter((text:string) => text.length > 2)
            .debounceTime(300)
            .subscribe((countryName:string) => {
                let query = new RegExp(countryName, 'ig')
                this.selectedCountries = this.countries.filter((country:Country) => {
                    return query.test(country.name)
                })
            })
    }
}

0

सेवा ईवेंट: घटक सेवा ईवेंट की सदस्यता ले सकते हैं। उदाहरण के लिए, दो भाई घटक एक ही सेवा घटना के लिए सदस्यता ले सकते हैं और अपने संबंधित मॉडल को संशोधित करके जवाब दे सकते हैं। इस पर अधिक नीचे।

लेकिन मूल घटक को नष्ट करने के लिए उस पर सदस्यता समाप्त करना सुनिश्चित करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.