Pesquisar

Pergunta
· Jul. 11

Stamp Duty and Property Registration Charges in Mumbai 2025

In 2025, many homebuyers, individuals, and investors dream of buying a house in Mumbai and this decision is beyond just finances and is more of a milestone and emotionally rooted move, While it is important to choose the right home that fits your budget and needs, it is equally important to understand the legal costs and the mandatory processes involved while booking a flat. Stamp duty and property registration fees are particularly important and are two charges that you definitely should be aware of. They not just keep tabs on transactions but ensure homeownership is legal, valid, secure, and protected by the law.

With this blog, let us understand what stamp duty is, what are property registration prices in Mumbai in 2025, why they matter and how they are calculated.

What Is Stamp Duty?

Stamp duty is a type of tax imposed by the government on transfer of property ownership and is suitable for documents related to purchase, sale, or transfer of home ownership and real estate. Stamp duty acts as a proof for ownership and ensures transactions are transparent and have a trail by lodging them into public records. The Maharashtra Stamp Act, 1958 governs the stamp duty act and ensures its smooth functioning. Tax rates can vary based on factors such as location; urban or rural, property type; residential vs commercial, buyer gender; male vs female, and total ticket value.

Why Does Stamp Duty Matter?

  • Without paying the stamp duty the sale is considered to be incomplete and has no standing in court as it is not legally registered thus not protected.
  • Stamp duty protects your ownership of the property, by giving you rights to protect yourself and to evade legal disputes, title deeds, and possession related troubles, if they were to arise.
  • Paying stamp duty not only protects your legal rights, but funds the development of public infrastructure projects such as roads, utilities, and other civic amenities.
  • Finally, stamp duty should be paid before the registration of the property document, as delays can attract penalties.

How To Calculate Stamp Duty?

Stamp duty in 2025 is calculated by the total transaction value i.e., the price that you pay for the property, or based on the ready reckoner rate, i.e., the government-assessed value depending on the location and property type, whichever is higher out of the two.

For example, if the ready reckoner rate for your flat is ₹80 lakh, but you purchased it for ₹85 lakh, the stamp duty will be calculated on ₹85 lakh because it’s higher.

In Mumbai, the stamp duty rates to be paid by female buyers is 5% and 6% for male buyers. Furthermore the applicable percentage of stamp duty is based on the location of the property, the type of property, and gender of the buyer.

For example, If the applicable stamp duty rate is 6%, and your property value is ₹85 lakh, the stamp duty would be ₹5.1 lakh (85,00,000 × 6%).

JSB Group Pro Tip: Before finalizing a property purchase, use official stamp duty calculators available on the Maharashtra government or IGR website to estimate your costs accurately and avoid surprises.

Stamp Duty Rates in Mumbai (2025)

Buyer Type/ Gender Stamp Duty % Metro Cess % Total Stamp Duty
Male buyer 5% 1% 6%
Female buyer 4% 1% 5%
Joint(male+female) 5.5% 1% 6-6.5%

Metro Cess is a small percentage, usually 1%, imposed by the government in metropolitan areas, to invest and fund public infrastructure projects and civic amenities.

Concessions & Exemptions in 2025:

To promote and support homebuying amongst women and to encourage more and more affordable homes in Mumbai, the Maharashtra Government offers some concessions and exemptions and they are as follows:

  • Exclusive 1% concession for female buyers on stamp duty as compared to male buyers.
  • Concessions on government schemes like Pradhan Mantri Awas Yojana (PMAY).
  • Exemption on family transfers; In cases of gift deeds within family i.e., parent to child, the stamp duty is a mere ₹200.
  • Lower stamp duty for properties in gram panchayat areas and rural areas.

Documents Required for Stamp Duty & Registration:

To complete the process, you will need:

  • ✅ Sale deed/agreement
  • ✅ Previous property title documents
  • ✅ No Objection Certificate (NOC) from society (if applicable)
  • ✅ PAN card (buyer and seller)
  • ✅ Aadhar card
  • ✅ Passport-size photographs
  • ✅ Latest property tax receipts

How to Pay Stamp Duty in Mumbai (2025)

Paying the stamp duty is one of the most crucial steps in securing homeownership that is recognized and legally protected. The Government of Maharashtra to ensure a smooth and hassle free payment experience has streamlined the payment process by making various arrangements, such as:

Best for: Homebuyers who want value speed and prefer paperless transactions.

  • - GRAS Portal (Government Receipt Accounting System): The GRAS website is one the most common, widely used, and recommended paying methods. Regarded as a safe payment option the GRAS Portal government authorised and is known to generate receipts instantly.

Best for: Individuals and business requiring guidance completing the payment process

  • - eSBTR (Electronic Secure Bank & Treasure Receipt): A revolutionary move in making paying stamp duty even more comfortable and fast, eSBTR unlike traditional payment methods lets you pay stamp duty digitally at select authorised banks generating e-receipts for your payments.

Best for: In 2025, it is recommended to opt for GRAS or eSBTR.

  • Stamp Papers: Old school! These were the old ways of paying stamp duty by getting actual stamp papers from vendors. However with the introduction of GRAS and eSBTR, stamp papers are growing out of fashion as they are considered to be less secure, harder to verify, and not effective, leaving room for scams and frauds.

What Is Property Registration?

Property registration refers to the process of officially registering your property transaction with the Sub-Registrar Office (SRO), under the Registration Act 1908. After submitting all the relevant documents and completing the process the SRO then legally declares you (the buyer) as the rightful owner of the property and protects you under laws if any mishap happens.

Without registration the transaction is not legally bound or recognised, you cannot claim ownership and rights on the property, and it is impossible to resell or rent out the property.

Property Registration Process Step-by-Step in Mumbai 2025 :- Guide By JSB Group:

1. Pay Stamp Duty: Before registering your property, it is mandatory to pay stamp duty first and the quickest, easiest, and safest way to pay is online through GRAS website and eSBTR solutions for a hassle free transaction.

2. Prepare the Sale Agreement / Sale Deed: After paying the stamp duty and securing the receipt, a sale agreement and a sale deed needs to be drafted and be signed by the buyer, seller, and two witnesses. The document must clearly mention and include all the details of the property and the sale terms should be outlined. It is often suggested to get it done by lawyers and legal personnel to avoid errors and misunderstandings.

3. Book Your Registration Appointment: The next step in the property registration process is logging into the Department of Registration & Stamps Government of Maharashtra website and booking your appointment by choosing the nearest SRO to your location and selecting a date and time for registration. It is important to note that slots in tier-1 cities like Mumbai can fill up fast so it is always better to plan ahead.

4. Visit the Sub-Registrar Office: After booking your slot, on your appointment day both parties, the buyer as well as the seller have to be present in person for the registration. Both the parties must carry all relevant and original documents and valid identification proofs and if one of the either parties is unable to come an exception is made by sending an authorised personnel with a power of attorney.

5. Biometric Verification: At the time of registration at the SRO, government officials will ask for your biometrics to verify the details, keep record, and ensure a transparent, well documented, and authentic transaction. Fingerprints, retina scans, and photographs are collected and stored of all parties involved, i.e., buyer, seller, and both witnesses.

6. Document Registration & Final Receipt: After going through every process and verifying the details and establishing trust the Sub-Registrar will officially register the sale deed. Then, the registration receipt will be handed over as proof of completion and a digital copy of it will be made to enter in public records, and a physical copy will be handed over to you thus completing your registration.

Conclusion:

Stamp duty and registration are not just legal fees but documents and formalities that ensure validity of a property, its legality, traceability, and protection by the law. Paying stamp duty on time can make all the difference as it will shade you from legal challenges and secure ownership and your rights on your home. Paying the property registration fees in Mumbai or in any other city ensures a distinguished title deed and the right to purchase, sell, or mortgage property as per your desire:

Before finalizing your property purchase in Mumbai, always:

  • Use authorised calculators to estimate your stamp duty and registration fee cost accurately.
  • Understand applicable concessions, such as lower rates for female buyers or exemptions on family transfers to avoid being tapped.
  • Opt for secure, digital payment options like GRAS or eSBTR to prevent errors and fraud.
  • It is advised to take help of a lawyer to ensure documentations are in order and the process is completed smoothly.

At JSB Group, our dedicated team of experts are here to guide you every step of the homebuying process, helping you choose not just the right home but also guiding you through the stamp duty costs and registration fees in Mumbai so you can focus on building a secure future for you and your family, all while being legally recognized and valid.

Discussão (0)1
Entre ou crie uma conta para continuar
Artigo
· Jul. 11 7min de leitura

Metabase IRIS Driver

Hi InterSystems Community! I'm Sidd one of the interns at the Singapore office and I recently had the chance to develop to develop a driver to connect IRIS to Metabase to help some of the Sales Engineers here. I was encouraged to share it here so that if any others have a similar issue they can use the driver and as well as get some feedback on potential improvements. The full GitHub repo with the quick start step, brief overview and the driver building process can be found here.

2 novos comentários
Discussão (2)3
Entre ou crie uma conta para continuar
Artigo
· Jul. 10 1min de leitura

文字列を全部大文字/小文字にしたり、指定文字コードに変換したり、HTML内の記号やURLのクエリパラメータをエスケープする方法

Discussão (0)1
Entre ou crie uma conta para continuar
Pergunta
· Jul. 10

Persistent Python DB-API Connection Issues (SSL Error) to IRIS CE Docker despite SSL disabled

Hello,

I'm trying to connect a Python backend application to an InterSystems IRIS Community Edition instance running in a Docker container on an AWS EC2 instance. I'm facing persistent connection issues and an SSL Error despite the Superserver apparently having SSL disabled. I'm hoping for some insight into what might be causing this contradictory behavior.

My Setup:

  • InterSystems IRIS: Community Edition (Docker image intersystems/iris-community:2025.1)
  • Deployment: AWS EC2 (Ubuntu) instance.
  • Port Mapping: Host port 9091 mapped to container port 1972 (Superserver). Host port 9092 mapped to container port 52773 (Management Portal).
  • Persistent Storage: Configured and confirmed working with irisowner user and appropriate permissions.
  • Python Client: Using the intersystems_irispython package (version 5.1.2) as the DB-API driver.

Symptoms and Diagnostics Performed:

  1. Python Connection Error:
    • My Python script attempts to connect using iris.connect().
    • The error received is: RuntimeError: <COMMUNICATION LINK ERROR> Failed to connect to server; Details: <SSL Error>.
  2. telnet Test to Superserver Port:
    • From the Python backend's EC2 instance, I ran telnet YOUR_EC2_PUBLIC_IP 9091.
    • The output shows: Connected to ... followed immediately by Connection closed by foreign host.
    • This indicates the TCP connection is established, but the server immediately drops it.
  3. AWS Security Group Check:
    • Inbound rules for the IRIS EC2 instance explicitly allow TCP traffic on port 9091 from 0.0.0.0/0 (for testing, will restrict later).
    • Outbound rules from the backend EC2 instance allow all traffic.
    • Conclusion: Basic network/firewall is not blocking the connection.
  4. InterSystems IRIS Management Portal (Superserver SSL Configuration):
    • I accessed System Administration > Security > Superservers > Edit Superserver 1972.
    • Under "SSL/TLS support level", the "Disabled" radio button is selected. This confirms, according to the portal, that the Superserver is NOT configured for SSL.

The Contradiction:

The primary source of confusion is that both the Python client and the telnet behavior suggest the server is expecting an SSL connection (or immediately rejecting non-SSL), despite the Management Portal explicitly showing "SSL/TLS support level: Disabled" for Superserver 1972.

Actions Taken (Python Script Variations):

  • Attempted iris.connect with no sslconfig parameter (default).
  • Attempted iris.connect with sslconfig=False.
  • Attempted iris.connect with an ssl.SSLContext object (received sslconfig must be a string or bool error, indicating this parameter expects specific types).

My Question:

Given that the Management Portal indicates SSL is disabled for the Superserver, what could be causing the persistent <SSL Error> from the Python client and the immediate Connection closed by foreign host from telnet? Are there any other hidden configurations or common pitfalls that could lead to this behavior?

Any help or insights would be greatly appreciated!

2 novos comentários
Discussão (2)2
Entre ou crie uma conta para continuar
Artigo
· Jul. 10 16min de leitura

The Zen Angle

Dear community, I have a confession to make. I have not gotten over Zen yet. Alas, all good things must come to an EOF, so I am currently learning about Angular. I am working on proving to myself that with the right back end and Angular components, I can deliver to myself and my team a very Zen-like experience in this environment. Since this is my first attempt, here is a fair warning: I will be providing some rather large code samples before discussing them. Please warm up your mouse and hand for extensive upcoming scrolling! Also, a note on the code snippets: many of them are labeled "Javascript" when they should actually be "Typescript"; Typescript simply is not an option when inserting a code snippet.

The Back End

As with most front ends, I will need a REST API to call. If you are familiar with how the %CSP.REST class extends, you should be very comfortable with the class below.

Class DH.REST Extends %CSP.REST
{
    Parameter HandleCorsRequest = 1;
    XData UrlMap [ XMLNamespace = "http://www.intersystems.com" ]
    {
        <Routes>
            <Route Url="/select/:class/:id" Method="GET" Call="Select" />
            <Route Url="/update/:class/:id" Method="PUT" Call="Update" />
            <Route Url="/delete/:class/:id" Method="DELETE" Call="Delete" />
            <Route Url="/insert/:class" Method="POST" Call="Update" />
        </Routes>
    }
    ClassMethod Select(class, id) As %Status
    {
        try{
            set myobj = $CLASSMETHOD(class,"%OpenId",id,,.sc)
            $$$ThrowOnError(sc)
            do myobj.%JSONExport()
            return $$$OK
        }
        catch ex{
            return ex.AsStatus()
        }
    }
    ClassMethod Delete(class, id) As %Status
    {
        try{
            return $CLASSMETHOD(class,"%DeleteId",id)
        }
        catch ex{
            return ex.AsStatus()
        }
    }
    ClassMethod Update(class, id = 0) As %Status
    {
        try{
            if %request.Method = "POST"{
                set record = $CLASSMETHOD(class,"%New")
            }
            else{
                set record = $CLASSMETHOD(class,"%OpenId",id,,.sc)
                $$$ThrowOnError(sc)
            }
            $$$ThrowOnError(record.%JSONImport(%request.Content))
            $$$ThrowOnError(record.%Save())
            do record.%JSONExport()
            return $$$OK
        }
        catch ex{
            return ex.AsStatus() 
        }
    }
}

Most of this is basic IRIS stuff. I have a route map with routes defined to select, update, and delete records. I have the methods defined to follow that. Those methods are generally usable for any persistent class that extends the %JSON.Adaptor through the use of $CLASSMETHOD. In the update method, I utilize the %JSON.Adaptor’s %JSONImport method.  Also, note that I have written this method in a way that allows it to handle both creating new records and updating existing ones, eliminating redundant code. To accomplish it, I have defined two different routes and checked the %request.Method to determine whether to open an existing record or create a new one.

I have this class set as a dispatch class for a web application called /zenular. Since I am only testing the concept on my local PC and not planning to go live, I am allowing unauthenticated access for simplicity’s sake. Of course, in a production environment, you would prefer to secure the API and limit the tables it can work on. However, it is a discussion for another article.

For my proof-of-concept purposes today, I will be using a very simple persistent class called DH.Person. It will contain just three properties: a first name, a last name, and a calculated MyId field, which will represent the object's ID. We will use it (MyId) when creating new objects. Without it, when we ask the API to save a new object, the JSON we return would not include the newly assigned ID, which we will need for our form. It will extend the %JSON.Adaptor class, as my API utilizes it.

Class DH.Person Extends (%Persistent, %JSON.Adaptor)
{
    Property FirstName As %String;
    Property LastName As %String;
    Property MyId As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ];
}

The Project

In my Angular project directory, I have created a new project called “zenular” using the command below:

ng new zenular

It generates numerous default files and folders for the project. Within the app subfolder, I have made another subfolder, also named "zenular," to house all my Angular bits and pieces. Currently, I will only be modifying one of those files (app.config.ts):

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
  providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes),provideHttpClient()]
};

Note that I have added an import for provideHttpClient as well as provideHttpClient() to the providers here. I did it to make the HttpClient class available and ready to go throughout our other components. Since we are going to call a REST API, we will need to use it.

From the command line, within the /app/zenular project folder, I also ran the following commands to generate the pieces I will require:

ng generate service zservice
ng generate component ztext
ng generate component zformbutton
ng generate component zform

For each generated component, I get a folder with four files: a .component.css file, a .component.html file, a .component.ts file, and a .component.ts.spec file. However, we have just one file (zservice.ts) for our purposes. Therefore, we will perform most of our work in the .ts files.

zservice

import {Injectable} from '@angular/core';
import {BehaviorSubject} from 'rxjs';
@Injectable({providedIn: 'root',})

export class DataControl{
    private updatecount = new BehaviorSubject<number>(0);
    zform: string = "";
    zid: string = "";
    zop: string = "";
    currentcount = this.updatecount.asObservable();

    updateForm(zformid: string, zid: string, zop: string){
        this.zform = zformid;
        this.zid = zid;
        this.zop = zop;
        this.updatecount.next(this.updatecount.value + 1);
    }
}

This is the shortest and simplest part of the project. An Angular service provides a place for various Angular components to share data, meaning that when one of our components updates the zservice, those changes become visible to the other components. To enable components to watch for alterations, you require something observable. In this case, I have created a BehaviorSubject that is simply a number. My plan is to allow various components to utilize the updateForm function to notify forms on the page about the following: which form they are requesting a change on, a record ID if needed, and an operation to perform. After setting that, we increment our BehaviorSubject. The forms will detect it, check whether they are the intended target, and execute the requested operation accordingly.

ztext

Iplan to attempt using text fields for my proof of concept. Naturally, I could go for other inputs if I wanted, but this is a good place to start. I will be working within the .ts file.

import { Component, Attribute } from '@angular/core';
import { DataControl } from '../zservice'
@Component({
  selector: 'ztext',
  imports: [],
  template: '<input type="text" value="{{value}}" (change)="onChange($event)" />',
  styleUrl: './ztext.component.css'
})

export class ZtextComponent {
  value: string = ""
  fieldname: string = ""
  dcform: string = ""
  constructor(@Attribute('fieldname') myfieldname:string, @Attribute('dataControllerForm') mydcform:string, private dc:DataControl){
    this.fieldname = myfieldname
    this.dcform = mydcform
  }

  onChange(e: Event){
    const target = e.target as HTMLInputElement
    this.value = target.value
    if (this.dcform !== null){
      this.dc.updateForm(this.dcform,target.value,'select')
    }
  }
}

You might notice that I am importing the Attribute class from the @angular.core package. It will allow us to read the attributes of the HTML element that we will employ to put this component on a template later on. I have set the selector to ztext, meaning that I will utilize a <ztext /> HTML-style tag to operate this component. I have replaced the default templateUrl with a simple template here. I used (change) to ensure this component utilizes the onChange method, defined in this class, whenever the text box's value changes.

By default, there is an exported class ZtextComponent in the file. However, I have modified it by adding a few string values. One of them is for the value of the component. Another one is called fieldname, and I will use it to indicate which property of a persistent class in IRIS each ztext component corresponds to. I also want to allow a text box to control a form, so I have one more property called dcform that defines which form it will control. I intend to allow users to type a record ID into the text input and utilize the REST API to populate the remaining form with the relevant data.

The constructor is also crucial here. I have used the @Attribute decorator in the constructor definition to specify which attributes of the component’s tag will be used in the constructor to set the component’s properties. In this case, I have configured it to use the fieldname attribute to set the component's fieldname property and the dataControllerForm attribute to set the dcform for the component. I have also added a reference to the DataControl class from the zservice to give the class access to that service.

In the onChange event, we retrieve the value from the HTML text input. If this component is configured to be a data controller for a form, we then pass the form name, the value, and the string "select" to the zservice. When we decide to create the form itself later, we will ensure it watches for those requests and handles them accordingly. 

zformbutton

Being able to load data is important, but it is no fun if we cannot also save or delete it! We need buttons.

import { Component, Attribute } from '@angular/core';
import { DataControl } from '../zservice';

@Component({
  selector: 'zformbutton',
  imports: [],
  template: '<button (click)="onClick()"><ng-content></ng-content></button>',
  styleUrl: './zformbutton.component.css'
})

export class ZformbuttonComponent {
  dcform: string = "";
  dcop: string = "";

  constructor(@Attribute('dataControllerForm') mydcform:string, @Attribute('dataControllerOp') mydcop:string, private dc:DataControl){
    this.dcform = mydcform;
    this.dcop = mydcop;
  }

  onClick(){
    this.dc.updateForm(this.dcform,'',this.dcop);
  }
}

There are various similarities between this and the ztext. We use attributes to set the component’s dcform and dcop, which I will employ to define whether this button is supposed to update, delete, or create a new record. I have once more provided it with the DataControl class from zservice. I have also replaced the templateUrl with a simple template again. You should notice a matched set of ng-content tags in the middle of it. This is how you tell Angular where any content placed between the opening and closing selector tags for a component should go. We similarly have an event that utilizes the zservice’s updateForm method to request a data operation. This time, it is the click event instead of the change, and we pass the form name (not an ID) since the form will operate on its current record and component’s dcop.

The App Component

When I created my project, I got a default app component. I have modified the app.component.ts file as shown below:

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ZtextComponent } from './zenular/ztext/ztext.component';
import { ZformComponent } from './zenular/zform/zform.component';
import { ZformbuttonComponent } from './zenular/zformbutton/zformbutton.component';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, ZtextComponent, ZformComponent, ZformbuttonComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
})

export class AppComponent {
  title = 'zenular';
  apiroot = 'http://localhost:52773'
}

As of this moment, we have not yet discussed the zform. However, I am bringing up this app component now because I have included a property called apiroot within it. I have done this exclusively to make the project more portable. I am not entirely sure whether it will be necessary in the long run. Yet, it is important to keep that in mind as we move into the zform. Also, note the imports in this file, both at the top and in the @Component. It will be essential for using those components in the app.component.html file later on.

Zform

This is the component that will do the heavy lifting and interact with the API.

import { Component, OnDestroy, Attribute, ContentChildren, QueryList } from '@angular/core';
import { DataControl } from '../zservice';
import { Subscription } from 'rxjs';
import { ZtextComponent } from '../ztext/ztext.component';
import {HttpClient} from '@angular/common/http';
import { AppComponent } from '../../app.component';

@Component({
  selector: 'zform',
  imports: [],
  template: '<ng-content></ng-content>',
  styleUrl: './zform.component.css'
})

export class ZformComponent implements OnDestroy {
  subscription:Subscription;
  formname: string = "";
  currentId: string = "";
  table: string = "";
  @ContentChildren(ZtextComponent) contentChildren!: QueryList<any>;

  constructor(@Attribute('name') myname:string, @Attribute('tablename') mytable: string, private dc:DataControl, private http: HttpClient, private appcon: AppComponent){
    this.subscription = this.dc.currentcount.subscribe(count => (this.updateData()));
    this.table = mytable
    this.formname = myname
  }

  updateData(){
    if (this.formname === this.dc.zform){
      if(this.dc.zop === 'select'){
          this.currentId = this.dc.zid
          this.http.get(this.appcon.apiroot+'/zenular/select/'+this.table+'/'+this.dc.zid, {responseType: 'text'}).subscribe((json: string) => {this.reloadForm(json)})
        }
      }
      if(this.dc.zop === 'update'){
        var jsonData : { [ key: string ]: any} = {};
          this.contentChildren.forEach(child => {
            jsonData[child.fieldname] = child.value;
          })
        if(this.currentId == ""){
          var reqid = "0"
          this.http.post(this.appcon.apiroot+'/zenular/insert/'+this.table, jsonData, {responseType: 'text'}).subscribe((json: string) => {this.reloadForm(json)})
        }
        else{
          var reqid = this.currentId
          this.http.put(this.appcon.apiroot+'/zenular/update/'+this.table+'/'+reqid, jsonData, {responseType: 'text'}).subscribe((json: string) => {this.reloadForm(json)})
        }
      }
      if(this.dc.zop === 'delete'){
        this.http.delete(this.appcon.apiroot+'/zenular/delete/'+this.table+'/'+this.currentId, {responseType: 'text'}).subscribe((json: string) => console.log(json))
        this.contentChildren.forEach(child => {
            child.value="";
        })
        this.currentId="";
      }
      if(this.dc.zop === 'new'){
        this.currentId = "";
      }
    }

  reloadForm(json: string){
    var jsonData = JSON.parse(json);
    this.contentChildren.forEach(child => {
      if(child.dcform !== this.formname){
        child.value=jsonData[child.fieldname];
      }
      else{
        child.value=jsonData['MyId'];
      }
    })
    this.currentId=jsonData['MyId'];
  }

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

}

My import list is significantly longer this time, but I still need the component and attribute. From the same package, I also require OnDestroy for cleanup when the component is destroyed, ContentChildren to retrieve a list of components within this component's ng-content, and QueryList to iterate over those components. Ialso still need the DataControl and Subscription to subscribe to the Observable in the zservice to trigger updates. Since I will be programmatically working with the ztext components, I must interact with their class. As this is where I will make the API requests, HttpClient will be required. I am also importing the previously mentioned AppComponent solely to access the API root. I have kept the template very simple, containing only the ng-content tags.

The class includes a property called contentChildren, which will provide a list of all the components within the ng-content tags that are the ztext components. The class also has a subscription, which is where the @Observable in the zservice comes into play. In the constructor, I set up that subscription to subscribe to that Observable. When it gets triggered, the zform calls its private updateData method. The subscription is also the reason for implementing the OnDestroy. I have defined an ngOnDestroy method that unsubscribes from the subscription. This is simply the best practice cleanup step.

The updateData form is where the interaction between the server and the application occurs. As you can see, the form first checks the zservice to determine whether it is the form whose update has been requested. If the operation is "new," we simply clear the form's currentId. Depending on your usage, you might also want to clear the entire form. For my particular implementation, we do not typically empty the form to create a new record. Since we are an ERP system, it is common to make multiple records for such things as item numbers, where the majority of fields are identical, with only three or four different ones. This is why we leave them populated to facilitate the quick production of similar records. However, your use case may demand something else.

If the operation is "delete", we send a request to the delete API to remove the current record. We also clear all form inputs and the form's currentId. 

If the request is "select", we check the zservice for the ID we are looking for, then send that request to the API. Upon receiving the response, we iterate over the inputs and pull values for them out of the JSON according to their fieldname properties.

If it is an "update", I package the values of all text inputs into JSON and apply a "put" request to the API with the help of the currentId. Alternatively, I utilize a "post" request without the ID if the currentId is not set, since this is how my API knows it should make a new record. The API responds with the JSON of the new object, which I then reload into the form. This is where the MyId field comes into play. When we create a new record, we need to pay attention to that and set the form’s currentId and data controller field. It will ensure that further changes to the object will result in an update rather than the creation of a new form.

Back to the App Component

When I previously discussed the app component, I left the templateUrl in the .ts file intact. This means that to add components to it, we should go to app.component.html. Now it is time to see how all that groundwork will pay off! We will replace the entire default contents of that file with the following:

<main class="main">
  <zform tablename="DH.Person" name="PersonForm">
    <ztext dataControllerForm="PersonForm" fieldname="ID" table="DH.Person"/>
    <ztext fieldname="FirstName" />
    <ztext fieldname="LastName" />
    <zformbutton dataControllerForm="PersonForm" dataControllerOp="update">Save</zformbutton>
    <zformbutton dataControllerForm="PersonForm" dataControllerOp="delete">Delete</zformbutton>
    <zformbutton dataControllerForm="PersonForm" dataControllerOp="new">New</zformbutton>
  </zform>
</main>

With just those steps, we have successfully created a basic form featuring fields that synchronize with IRIS via our API, complete with buttons to save and delete records. 

While this is still a bit rough, it has provided strong reassurance that this is a promising direction for us! If you would like to witness how this project continues to develop, let me know and suggest what you would like to see refined next. Also, since I have only been using Angular for a couple of weeks, please let me know what I could have done better. 

7 novos comentários
Discussão (7)6
Entre ou crie uma conta para continuar