"WeAreHereToHelp":"We are Here to <span class=\"zero-text\">Help</span>",
"BrowseOrAskQuestion":"You can browse our help topics or search in frequently asked questions, or you can ask us a question by using the <a href=\"{0}\" class=\"text-success\">contact form</a>.",
"SearchQuestionPlaceholder":"Search in frequently asked questions",
"WhatIsTheABPCommercial":"What is the ABP Commercial?",
"WhatAreDifferencesThanAbpFramework":"What are the differences between the open source ABP Framework and the ABP Commercial?",
"ABPCommercialExplanation":"ABP Commercial is a set of premium modules, tools, themes and services built on top of the open source <a target=\"_blank\" href=\"{0}\">ABP framework</a>. ABP Commercial is being developed and supported by the same team behind the ABP framework.",
"WhatAreDifferencesThanABPFrameworkExplanation":"<p> <a target=\"_blank\" href=\"{0}\">ABP framework</a> is a modular, themeable, micro-service compatible application development framework for ASP.NET Core. It provides a complete architecture and a strong infrastructure to make you focusing on your own business code rather than repeating yourself for every new project. It is based on software development best practices and popular tools you already know. </p> <p> ABP framework is completely free, open source and community-driven. It also provides a free theme and some pre-built modules (e.g. identity management and tenant management).</p>",
"VisitTheFrameworkVSCommercialDocument":"Visit the following link, for more information <a href=\"{0}\" target=\"_blank\"> {1} </a>",
"ABPCommercialFollowingBenefits":"ABP Commercial adds the following benefits on top of the ABP framework;",
"WhatIsTheABPCommercial":"What is ABP Commercial?",
"WhatAreDifferencesThanAbpFramework":"What are the differences between the open source ABP Framework and ABP Commercial?",
"ABPCommercialExplanation":"ABP Commercial is a set of premium modules, tools, themes and services that are built on top of the open source <a target=\"_blank\" href=\"{0}\">ABP framework</a>. ABP Commercial is being developed and supported by the same team behind the ABP framework.",
"WhatAreDifferencesThanABPFrameworkExplanation":"<p> <a target=\"_blank\" href=\"{0}\">ABP framework</a> is a modular, themeable, micro-service compatible application development framework for ASP.NET Core. It provides a complete architecture and a strong infrastructure to let you focus on your own business code rather than repeating yourself for every new project. It is based on the best practices of software development and popular tools you already know. </p> <p> ABP framework is completely free, open source and community-driven. It also provides a free theme and some pre-built modules (e.g. identity management and tenant management).</p>",
"VisitTheFrameworkVSCommercialDocument":"Visit the following link for more information <a href=\"{0}\" target=\"_blank\"> {1} </a>",
"ABPCommercialFollowingBenefits":"ABP Commercial adds the following benefits on top of the ABP framework:",
"Professional":"Professional",
"UIThemes":"UI themes",
"EnterpriseModules":"Enterprise ready, feature rich, pre-built <a href=\"{0}\">application modules</a> (e.g. Identity Server management, SaaS management, language management)",
"UIThemes":"UI Themes",
"EnterpriseModules":"Enterprise ready, feature rich, pre-built <a href=\"{0}\">Application Modules</a> (e.g. Identity Server management, SaaS management, language management)",
"ToolingToSupport":"Tooling to support your development productivity (e.g. <a href=\"{0}\">ABP Suite</a>)",
"WhatDoIDownloadABPCommercial":"What do I download when I purchase the ABP Commercial?",
"CreateUnlimitedSolutions":"Once you purchase an ABP Commercial license, you will be able to create unlimited solutions like described in the <a href=\"{0}\">Getting Started</a> document.",
"ABPCommercialSolutionExplanation":"When you create a new application, you get a Visual Studio solution (a startup template) based on your preferences. The downloaded solution has commercial modules and themes already installed and configured for you. You can remove a pre-installed module or add another module if you like. All modules and themes are used a NuGet/NPM packages by default.",
"StartDevelopWithTutorials":"The downloaded solution is well architected and documented. You can start to develop your own business code based on it following <a href=\"{0}\">the tutorials</a>",
"TryTheCommercialDemo":"You can try <a href=\"{0}\">the demo</a> to see a sample application created using the ABP Commercial startup template.",
"ABPCommercialSolutionExplanation":"When you create a new application, you get a Visual Studio solution (a startup template) based on your preferences. The downloaded solution has commercial modules and themes already installed and configured for you. You can remove a pre-installed module or add another module if you like. All modules and themes use NuGet/NPM packages by default.",
"StartDevelopWithTutorials":"The downloaded solution is well architected and documented. You can start developing your own business code based on it following the <a href=\"{0}\">tutorials</a>.",
"TryTheCommercialDemo":"You can try the <a href=\"{0}\">Live Demo</a> to see a sample application created using the ABP Commercial startup template.",
"HowManyProducts":"How many different products/solutions can I build using the ABP Commercial?",
"HowManyProductsExplanation":"There is no limit to create an ABP project. You can create as many project as you want, develop and upload them to different servers.",
"HowManyProductsExplanation":"You can create as many projects as you want during your active license period, there is no limit! After your license expires, you cannot create new projects, but you can continue to develop the projects you have downloaded and deploy them to an unlimited count of servers.",
"HowManyDevelopers":"How many developers can work on the ABP Commercial?",
"HowManyDevelopersExplanation":"ABP Commercial licenses are per developer. Different license types have different developer limits. However, you can add more developers to any license type whenever you need. See <a href=\"{0}\">the prices</a> page for license types, developer limits and additional developer costs.",
"HowManyDevelopersExplanation":"ABP Commercial licenses are per developer. Different license types have different developer limits. However, you can add more developers to any license type whenever you need. Check out the <a href=\"{0}\">Plans & Pricing</a> page for license types, developer limits and additional developer costs.",
"ChangingLicenseType":"Can I upgrade my license type later?",
"ChangingLicenseTypeExplanation":"You can upgrade to a higher license by paying the difference within your active license period. When you upgrade to a higher license plan, you get the benefits of the new plan, but the license upgrade does not change the license expiry date. Besides, you can also add new developer seats to your existing license, see \"How many developers can work on the ABP Commercial?\"",
"ChangingLicenseTypeExplanation":"You can upgrade to a higher license by paying the difference within your active license period. When you upgrade to a higher license plan, you get the benefits of the new plan, but the license upgrade does not change the license expiry date. Besides, you can also add new developer seats to your existing license, check out the \"How many developers can work on the ABP Commercial?\" FAQ.",
"LicenseExtendUpgradeDiff":"What is the difference between license extend and upgrade?",
"LicenseExtendUpgradeDiffExplanation":"<strong>Extending:</strong> By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development. When you extend your license, 1 year is added to your license expiry date. <hr/><strong>Upgrading:</strong> By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. See the <a href=\"/pricing\">license comparison table</a> to check the differences between the license plans.<strong>On the other hand, when you upgrade, your license expiry date will not change!</strong>To extend your license end date, you need to extend your license.",
"LicenseExtendUpgradeDiffExplanation":"<strong>Extending:</strong> By extending/renewing your license, you will continue to get premium support and get major updates for the modules and themes. Besides, you will be able to continue creating new projects. And you will still be able to use ABP Suite which speeds up your development. When you extend your license, 1 year is added to your license expiry date. <hr/><strong>Upgrading:</strong> By upgrading your license, you will promote to a higher license plan which will allow you to get additional benefits. Check out the <a href=\"/pricing\">license comparison table</a> to see the differences between the license plans. <strong>On the other hand, when you upgrade, your license expiry date will not change!</strong>To extend your license end date, you need to extend your license.",
"LicenseRenewalCost":"What is the license renewal cost after 1 year?",
"LicenseRenewalCostExplanation":"The renewal (extend) price of the standard Team License is ${0}, standard Business License is ${1} and standard Enterprise License is ${2}. If you are already a customer, <a href='{3}' target='_blank'>log into your account</a> to review the available renewal pricing.",
"LicenseRenewalCostExplanation":"The renewal (extend) price of the standard Team License is ${0}, standard Business License is ${1} and standard Enterprise License is ${2}. If you are already a customer, <a href='{3}' target='_blank'>log into your account</a> to review the current renewal pricing.",
"HowDoIRenewMyLicense":"How do I renew my license?",
"HowDoIRenewMyLicenseExplanation":"You can renew your license by navigating to the <a href='{0}' target='_blank'>organization management page</a>. In order to take advantage of our discounted Early Renewal rates, make sure you renew before your license expires. Don't worry about not knowing when your Early Renewal opportunity closes, however. You'll receive 3 reminder e-mails before your subscription expires. We'll send them at 30 days, 7 days and 1 day before expiration.",
"HowDoIRenewMyLicenseExplanation":"You can renew your license by navigating to the <a href='{0}' target='_blank'>organization management page</a>. In order to take advantage of our discounted Early Renewal rates, make sure you renew before your license expires. Don't worry about not knowing when your Early Renewal opportunity closes, you'll however receive 3 reminder e-mails before your subscription expires. We'll send them 30 days, 7 days and 1 day before expiration.",
"IsSourceCodeIncluded":"Does my license include the source code of the commercial modules and themes?",
"IsSourceCodeIncludedExplanation1":"Depends on the license type you've purchased:",
"IsSourceCodeIncludedExplanation2":"<strong>Team</strong>: Your solution uses the modules and the themes as NuGet and NPM packages. It doesn't include their source code. In this way, you can easily upgrade these modules and themes whenever a new version is available. However, you can not get the source code of the modules and the themes.",
"IsSourceCodeIncludedExplanation2":"<strong>Team</strong>: Your solution uses the modules and themes as NuGet and NPM packages. It doesn't include their source code. In this way, you can easily upgrade these modules and themes whenever a new version is available. However, you can not get the source code of these modules and themes.",
"IsSourceCodeIncludedExplanation3":"<strong>Business/Enterprise</strong>: In addition to the Team license, you are able to download the source code of any module or theme you need. You can even remove the NuGet/NPM package references for a particular module and add its source code directly to your solution to fully change it.",
"IsSourceCodeIncludedExplanation4":"<p>Including the source code of a module to your solution gives you the maximum freedom to customize that module. However, then it will not be possible to automatically upgrade the module when a new version is released.</p><p>None of the licenses include the ABP Suite source code, which is an external tool that generates code for you and assist to your development.</p><p>See <a href=\"{0}\">the pricing</a> page for other differences between the license types.</p>",
"IsSourceCodeIncludedExplanation4":"<p>Including the source code of a module to your solution gives you the maximum freedom to customize that module. However, it will then not be possible to automatically upgrade the module when a new version is released.</p><p>None of the licenses include the ABP Suite source code, which is an external tool that generates code for you and assists your development.</p><p>Check out the <a href=\"{0}\">Plans & Pricing</a> page for other differences between the license types.</p>",
"ChangingDevelopers":"Can I change the registered developers of my organization in the future?",
"ChangingDevelopersExplanation":"In addition to add new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.",
"ChangingDevelopersExplanation":"In addition to adding new developers to your license, you can also change the existing developers (you can remove a developer and add a new one to the same seat) without any additional cost.",
"WhatHappensWhenLicenseEnds":"What happens when my license period ends?",
"WhatHappensWhenLicenseEndsExplanation1":"ABP Commercial license type is <a href=\"{0}\" target=\"_blank\">perpetual license</a>. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. To continue to get new features, performance enhancements, bug fixes, support and continue to use ABP Suite, you need to renew your license. When your license expires, you will not get the following benefits;",
"WhatHappensWhenLicenseEndsExplanation2":"You can not create new solutions using the ABP Commercial, but you can continue to develop your existing applications forever.",
"WhatHappensWhenLicenseEndsExplanation3":"You will be able to get updates for the modules and themes within your MAJOR version (except RC or Preview versions). For example; if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x). For example, when your license expired, the latest release was v4.4.3 and the latest preview version was v5.0.0-rc.2, you can access the v4.X.X but you cannot access the v5.X.X.",
"WhatHappensWhenLicenseEndsExplanation1":"The ABP Commercial license is a <a href=\"{0}\" target=\"_blank\">perpetual license</a>. After your license expires, you can continue developing your project. And you are not obliged to renew your license. Your license comes with a one-year update and support plan out of the box. In order to continue to get new features, performance enhancements, bug fixes, support and continue using ABP Suite, you need to renew your license. When your license expires, you will not get the following benefits:",
"WhatHappensWhenLicenseEndsExplanation2":"You can not create new solutions using the ABP Commercial, but you can continue developing your existing applications forever.",
"WhatHappensWhenLicenseEndsExplanation3":"You will be able to get updates for the modules and themes within your MAJOR version (except RC or Preview versions). For example: if you are using v3.2.0 of a module, you can still get updates for v3.x.x (v3.3.0, v3.5.2... etc.) of that module. But you cannot get updates for the next major version (like v4.x, v5.x). For example, when your license expired, the latest release was v4.4.3 and the latest preview version was v5.0.0-rc.2, you would be able to access the v4.X.X but you wouldn't be access the v5.X.X.",
"WhatHappensWhenLicenseEndsExplanation4":"You can not install new modules and themes added to the ABP Commercial platform after your license ends.",
"WhatHappensWhenLicenseEndsExplanation5":"You can not use the ABP Suite.",
"WhatHappensWhenLicenseEndsExplanation6":"You can not get the <a href=\"{0}\">premium support</a> anymore.",
"WhatHappensWhenLicenseEndsExplanation7":"You can extend (renew) your license if you want to continue to get these benefits. If you extend your license within <strong>1 month</strong> after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount.",
"WhatHappensWhenLicenseEndsExplanation7":"You can extend (renew) your license if you want to continue getting these benefits. If you extend your license within <strong>1 month</strong> after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount.",
"WhenShouldIRenewMyLicense":"When should I renew my license?",
"WhenShouldIRenewMyLicenseExplanation":"If you renew your license within <strong>1 month</strong> after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount. If you renew your license <strong>1 month</strong> after the expiry date of your license, the renewal price will be the same as the license purchase price and there will be no discount on your renewal.",
"WhenShouldIRenewMyLicenseExplanation":"If you renew your license within <strong>1 month</strong> after your license expires, the following discounts will be applied: Team License {0}% discount, Business License {1}% discount, Enterprise License {2}% discount. However, if you renew your license after <strong>1 month</strong> since the expiry date of your license, the renewal price will be the same as the license purchase price and there will be no discount on your renewal.",
"TrialPlan":"Do you have a trial plan?",
"TrialPlanExplanation":"For now, ABP Commercial doesn't have a trial plan. For the Team licenses we provide 30 days money back guarantee. You can just request a refund in the first 30 days. For the Business and Enterprise licenses, we provide 60% refund in 30 days. This is because Business and Enterprise licenses include the full source code of all the modules and the themes.",
"DoYouAcceptBankWireTransfer":"Do you accept bank wire transfer?",
"DoYouAcceptBankWireTransferExplanation":"Yes, we accept bank wire transfer.<br />After sending the license fee via bank transfer, email us at accounting@abp.io your receipt and the type of license requested. Our international bank account information:",
"DoYouAcceptBankWireTransfer":"Do you accept bank wire transfers?",
"DoYouAcceptBankWireTransferExplanation":"Yes, we accept bank wire transfers.<br />After sending the license fee via bank transfer, email us your receipt and the type of license requested at accounting@abp.io, here's our international bank account information:",
"HowToUpgrade":"How to upgrade existing applications when a new version is available?",
"HowToUpgradeExplanation1":"When you create a new application using ABP Commercial, all the modules and the theme are used as NuGet and NPM packages. So, you can easily upgrade the packages when a new version is available.",
"HowToUpgradeExplanation1":"When you create a new application using ABP Commercial, all the modules and theme are used as NuGet and NPM packages. So, you can easily upgrade the packages when a new version is available.",
"HowToUpgradeExplanation2":"In addition to the standard NuGet/NPM upgrades, <a href=\"{0}\">ABP CLI</a> provides an update command that automatically finds and upgrades all ABP related packages in your solution.",
"DatabaseSupport":"Which database systems are supported?",
"DatabaseSupportExplanation":"ABP Framework itself is database agnostic and can work with any database provider by its nature. See <a href=\"{0}\" target=\"_blank\">the data access document</a> for a list of currently implemented providers.",
"DatabaseSupportExplanation":"ABP Framework itself is database agnostic and can work with any database provider by its nature. Check out the <a href=\"{0}\" target=\"_blank\">Data Access document</a> for a list of currently implemented providers.",
"UISupport":"Which UI frameworks are supported?",
"Supported":"Supported",
"UISupportExplanation":"ABP Framework itself is UI framework agnostic and can work with any UI framework. However, startup templates, module UIs and themes were not implemented for all UI frameworks. See <a href=\"{0}\">the getting started document</a> for the up-to-date list of UI options.",
"UISupportExplanation":"ABP Framework itself is UI framework agnostic and can work with any UI framework. However, startup templates, module UIs and themes were not implemented for all UI frameworks. Check out the <a href=\"{0}\">Getting Started document</a> for the up-to-date list of UI options.",
"MicroserviceSupport":"Does it support the micro-service architecture?",
"MicroserviceSupportExplanation1":"One of the major goals of the ABP framework is to provide a convenient infrastructure to create micro-service solutions. See the <a href=\"{0}\">micro-service architecture</a> document to understand how it helps to create micro-service systems.",
"MicroserviceSupportExplanation2":"All the ABP Commercial modules are designed to support micro-service deployment scenarios (with its own API and database) by following the <a href=\"{0}\">module development best practices</a> document.",
"MicroserviceSupportExplanation3":"We provide a sample <a href=\"{0}\">micro-service demo solution</a>that demonstrates a micro-service architecture implementation to help you to create your own solution.",
"MicroserviceSupportExplanation4":"So, the short answer is \"<strong>yes, it supports micro-service architecture</strong>\".",
"MicroserviceSupportExplanation1":"One of the major goals of the ABP framework is to provide a convenient infrastructure to create micro-service solutions. Check out the <a href=\"{0}\">Micro-service Architecture document</a> to understand how it helps with creating micro-service systems.",
"MicroserviceSupportExplanation2":"All the ABP Commercial modules are designed to support micro-service deployment scenarios (with its own API and database) by following the <a href=\"{0}\">Module Development Best Practices document</a>.",
"MicroserviceSupportExplanation3":"We provide a sample <a href=\"{0}\">Micro-service Demo Solution</a> that demonstrates a micro-service architecture implementation to help you create your own solution.",
"MicroserviceSupportExplanation4":"So, the short answer is: \"<strong>Yes, it supports micro-service architecture</strong>\".",
"MicroserviceSupportExplanation5":"However, a micro-service system is a solution and every solution will have different requirements, network topology, communication scenarios, authentication possibilities, database separation/sharing decisions, runtime configurations, 3rd party system integrations and many more.",
"MicroserviceSupportExplanation6":"The ABP Framework and the ABP Commercial provides infrastructure for micro-service scenarios, micro-service compatible modules, samples and documentation to help you to build your own solution. But don't expect to directly download your dream solution pre-built for you. You will need to understand it and bring some parts together based on your requirements.",
"WhereCanIDownloadSourceCode":"Where can I download source-code?",
"WhereCanIDownloadSourceCodeExplanation":"You can download the source code of all ABP modules, Angular packages and themes via ABP Suite or ABP CLI. See <a href=\"{0}\">How to download source-code?</a>",
"MicroserviceSupportExplanation6":"The ABP Framework and ABP Commercial provide infrastructure for micro-service scenarios, micro-service compatible modules, samples and documentation to help you build your own solution. But don't expect to directly download your dream solution pre-built for you. You will need to understand it and bring specific parts together based on your requirements.",
"WhereCanIDownloadSourceCode":"Where can I download the source-code?",
"WhereCanIDownloadSourceCodeExplanation":"You can download the source code of all the ABP modules, Angular packages and themes via ABP Suite or ABP CLI. Check out <a href=\"{0}\">How to download the source-code?</a>",
"ComputerLimitation":"How many computers can a developer login when developing ABP?",
"ComputerLimitationExplanation":"We specifically permit <strong>{0} computers</strong> per individual/licensed developer. Whenever there is a need for a developer to develop ABP Commercial products on a third machine, an e-mail should be sent to license@abp.io explaining the situation and we will then make the appropriate allocation in our system.",
"RefundPolicy":"Do you have a refund policy?",
"RefundPolicyExplanation":"You can request a refund within <strong>30 days</strong> of your license purchase. The Business and Enterprise license types have source-code download option, therefore refunds are not available for the Business and Enterprise (and any licenses that include a right to receive source-code). In addition, no refunds are made for renewals and second license purchases.",
"RefundPolicyExplanation":"You can request a refund within <strong>30 days</strong> of your license purchase. The Business and Enterprise license types have source-code download option, therefore refunds are not available for the Business and Enterprise (along with any licenses that include a right to receive source-code). In addition, no refunds are made for renewals and second license purchases.",
"HowCanIRefundVat":"How can I refund VAT?",
"HowCanIRefundVatExplanation1":"If you made the payment using 2Checkout, you can refund VAT via your 2Checkout account:",
"HowCanIRefundVatExplanation2":"Log in into your <a href=\"https://secure.2checkout.com/cpanel/login.php\" target=\"_blank\">2Checkout</a> account",
"HowCanIRefundVatExplanation2":"Log in to your <a href=\"https://secure.2checkout.com/cpanel/login.php\" target=\"_blank\">2Checkout</a> account",
"HowCanIRefundVatExplanation3":"Find the appropriate order and press \"Refund Belated VAT\" (enter your VAT ID)",
"HowCanIGetMyInvoice":"How can I get my invoice?",
"HowCanIGetMyInvoiceExplanation":"There are 2 payment gateways for purchasing a license: Iyzico and 2Checkout. If you purchase your license through the 2Checkout gateway, it sends the PDF invoice to your email address, see <a href=\"https://knowledgecenter.2checkout.com/Documentation/03Billing-and-payments/Payment-operations/How-do-invoices-work\">2Checkout invoicing.</a> If you purchase through the Iyzico gateway, with custom purchase link or via bank wire transfer, we will prepare and send your invoice. You can request or download your invoice from the <a href=\"{0}\">organization management page</a>. Before contacting us for the invoice, check your organization management page!",
"HowCanIGetMyInvoiceExplanation":"There are 2 payment gateways for purchasing a license: Iyzico and 2Checkout. If you purchase your license through the 2Checkout gateway, it sends the PDF invoice to your email address, check out <a href=\"https://knowledgecenter.2checkout.com/Documentation/03Billing-and-payments/Payment-operations/How-do-invoices-work\">2Checkout invoicing.</a> If you purchase through the Iyzico gateway, with a custom purchase link or via a bank wire transfer, we will prepare and send your invoice. You can request or download your invoice from the <a href=\"{0}\">organization management page</a>. Before contacting us for the invoice, check your organization management page!",
"Forum":"Forum",
"SupportExplanation":"ABP Commercial licenses provides a premium forum support by a team consists of the ABP Framework experts.",
"PayViaAmexCard":"How can I pay via my AMEX card?",
"PayViaAmexCardDescription":"The default payment gateway 'Iyzico' may decline some AMEX credit cards due to the security measures. In this case, you can pay through the alternative payment gateway '2Checkout'.",
"PayViaAmexCardDescription":"The default payment gateway 'Iyzico' may decline some AMEX credit cards due to security measures. In this case, you can pay through the alternative payment gateway '2Checkout'.",
"ThankYou":"Thank you",
"InvalidReCaptchaErrorMessage":"There was an error verifying reCAPTCHA. Please try again.",
"CompanyName":"Company name",
@ -391,7 +391,7 @@
"TrialLicenseExpiredInfo":"Your trial license period has expired!",
"CommercialNewsletterConfirmationMessage":"I agree to the <a href=\"https://commercial.abp.io/TermsConditions\">Terms & Conditions</a> and <a href=\"https://commercial.abp.io/Privacy\">Privacy Policy</a>.",
"DowngradeLicensePlan":"Can I downgrade to a lower license plan in the future?",
"DowngradeLicensePlanExplanation":"You cannot downgrade your existing license plan. But you can purchase a new lower license plan and continue to your development on the new license. After you purchase a lower license, you just need to login to your new license plan via ABP CLI command: ` abp login <username> -o <organization> `.",
"DowngradeLicensePlanExplanation":"You cannot downgrade your existing license plan. But you can purchase a new lower license plan and continue your development on the new license. After you purchase a lower license, you just need to login to your new license plan via ABP CLI command: ` abp login <username> -o <organization> `.",
"LicenseTransfer":"Can a license be transferred from one developer to another?",
"LicenseTransferExplanation":"Yes! When you purchase a license, you become the license holder, hence you will have access to the organization management page. An organization has owner and developer roles. Owners can manage the developer seats and assign developers. Each assigned developer will login via ABP CLI command into the system and will have development and support permissions.",
"UserOwnerDescription":"The 'Owner' of the organization is the admin of this account. He/she manages the organization by purchasing licenses, allocating developers. An 'Owner' cannot write code in the ABP Commercial projects, cannot download the ABP sample projects, or cannot ask questions on the support website. If you want to do all these, you have to add yourself as a developer too.",
@ -428,7 +428,7 @@ This is already done for the startup template integration tests.
Claims are important elements of authentication and authorization. ABP uses the `IAbpClaimsPrincipalFactory` service to create claims on authentication. This service was designed as extensible. If you need to add your custom claims to the authentication ticket, you can implement the `IAbpClaimsPrincipalContributor` in your application.
**Example: Add a `SocialSecurityNumber` claim:**
**Example: Add a `SocialSecurityNumber` claim and get it:**
```csharp
public class SocialSecurityNumberClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
@ -448,6 +448,15 @@ public class SocialSecurityNumberClaimsPrincipalContributor : IAbpClaimsPrincipa
}
}
}
public static class CurrentUserExtensions
{
public static string GetSocialSecurityNumber(this ICurrentUser currentUser)
@ -66,6 +66,33 @@ public class MyLogWorker : HangfireBackgroundWorkerBase
> You can directly implement the `IHangfireBackgroundWorker`, but `HangfireBackgroundWorkerBase` provides some useful properties like Logger.
### UnitOfWork
For use with `UnitOfWorkAttribute`, you need to define an interface for worker:
```csharp
public interface IMyLogWorker : IHangfireBackgroundWorker
{
}
[ExposeServices(typeof(IMyLogWorker))]
public class MyLogWorker : HangfireBackgroundWorkerBase, IMyLogWorker
{
public MyLogWorker()
{
RecurringJobId = nameof(MyLogWorker);
CronExpression = Cron.Daily();
}
[UnitOfWork]
public override Task DoWorkAsync()
{
Logger.LogInformation("Executed MyLogWorker..!");
return Task.CompletedTask;
}
}
```
## Register BackgroundWorkerManager
After creating a background worker class, you should add it to the `IBackgroundWorkerManager`. The most common place is the `OnApplicationInitialization` method of your module class:
@ -78,6 +105,9 @@ public class MyModule : AbpModule
ABP Framework is designed to be [modular](Module-Development-Basics.md), [microservice compatible](Microservice-Architecture.md) and [multi-tenancy](Multi-Tenancy.md) aware. Connection string management is also designed to support these scenarios;
> Connection string system is especially needed when you want to create or use a modular system. If you have a monolithic application with a single database, you can go with the [ABP startup solution template](Startup-Templates/Application.md), which is properly configured for you.
* Allows to set separate connection strings for every module, so every module can have its own physical database. Modules even might be configured to use different DBMSs.
ABP Framework is designed to be [modular](Module-Development-Basics.md) and [multi-tenancy](Multi-Tenancy.md) aware. Connection string management is also designed to support these scenarios;
* Allows to set separate connection strings for every module, so every module can have its own physical database. Modules even might be configured to use different database providers.
* Allows to set separate connection string and use a separate database per tenant (in a SaaS application).
It also supports hybrid scenarios;
* Allows to group modules into databases (all modules into a single shared database, 2 modules to database A, 3 modules to database B, 1 module to database C and rest of the modules to database D... etc.)
* Allows to group modules into databases (e.g., all modules into a single shared database or two modules to database A, three modules to database B, one module to database C and rest of the modules to database D)
* Allows to group tenants into databases, just like the modules.
* Allows to separate databases per tenant per module (which might be harder to maintain for you because of too many databases, but the ABP framework supports it).
* Allows to separate databases per tenant per module (which might be hard to maintain for you because of too many databases, but the ABP framework supports it).
All the [pre-built application modules](Modules/Index.md) are designed to be compatible these scenarios.
@ -29,15 +31,19 @@ See the following configuration:
This configuration defines three different connection strings:
* `MyMainDb` (the `Default` connection string) is the main connection string of the application. If you don't specify a connection string for a module, it fallbacks to the `Default` connection string. The [application startup template](Startup-Templates/Application.md) is configured to use a single connection string, so all the modules uses a single shared database.
* `MyIdsDb` is used by the [IdentityServer](Modules/IdentityServer.md) module.
* `MyPermissionDb` is used by the [Permission Management](Modules/Permission-Management.md) module.
* `MyMainDb` (the `Default` connection string) is the main connection string of the application. If you don't specify a connection string for a module, it fallbacks to the `Default` connection string. The [application startup template](Startup-Templates/Application.md) is configured to use a single connection string, so all the modules uses a single, shared database.
* `MyIdsDb`(the `AbpIdentityServer` connection string) is used by the [IdentityServer](Modules/IdentityServer.md) module.
* `MyPermissionDb`(the `AbpPermissionManagement` connection string) is used by the [Permission Management](Modules/Permission-Management.md) module.
[Pre-built application modules](Modules/Index.md) define constants for the connection string names. For example, the IdentityServer module defines a ` ConnectionStringName ` constant in the ` AbpIdentityServerDbProperties ` class (located in the ` Volo.Abp.IdentityServer ` namespace). Other modules similarly define constants, so you can investigate the connection string name.
[Pre-built application modules](Modules/Index.md) define constants for the connection string names. For example, the [IdentityServer module](Modules/IdentityServer.md) defines a ` ConnectionStringName ` constant in the ` AbpIdentityServerDbProperties ` class (located in the ` Volo.Abp.IdentityServer ` namespace). Other modules similarly define constants, so you can investigate the connection string name.
### AbpDbConnectionOptions
ABP actually uses the `AbpDbConnectionOptions` to get the connection strings. If you set the connection strings as explained above, `AbpDbConnectionOptions` is automatically filled. However, you can set or override the connection strings using [the options pattern](Options.md). You can configure the `AbpDbConnectionOptions` in the `ConfigureServices` method of your [module](Module-Development-Basics.md) as shown below:
`AbpDbConnectionOptions` is the options class that is used to set the connection strings and configure database structures.
#### Setting the connection strings
ABP uses the `AbpDbConnectionOptions` to get the connection strings. If you configure the connection strings as explained above, `AbpDbConnectionOptions` is automatically filled. However, you can set or override the connection strings using [the options pattern](Options.md). You can configure the `AbpDbConnectionOptions` in the `ConfigureServices` method of your [module](Module-Development-Basics.md) as shown below:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
@ -50,6 +56,52 @@ public override void ConfigureServices(ServiceConfigurationContext context)
}
````
#### Configuring the database structures
`Databases` property of the `AbpDbConnectionOptions` class is used to group multiple connection strings (of multiple modules) to a single connection string.
In this example, we've defined four connection strings, but the last three of them are the same; `AbpIdentity`, `AbpIdentityServer` and `AbpPermissionManagement` uses the same database, named `MySecondaryDb`. The main application and the other modules use the `Default` connection string, hence the `MyMainDb` database.
What we want to do here is to group three modules (`AbpIdentity`, `AbpIdentityServer` and `AbpPermissionManagement`) in a single database, but we needed to specify each one manually. Because the fallback connection string is the `Default` one, if we don't specify it for a module.
To eliminate the repetitive connection string definition, we can configure the `AbpDbConnectionOptions.Databases` property to group these connection strings, as shown in the following code (we place that in the `ConfigureServices` method of our [module class](Module-Development-Basics.md)):
````csharp
Configure<AbpDbConnectionOptions>(options =>
{
options.Databases.Configure("MySecondaryDb", db =>
`MySecondaryDb` becomes the new connection string for the mapped connections.
> ABP first looks for the module-specific connection string, then looks if a database mapping is available, finally fallbacks to the `Default` connection string.
## Set the Connection String Name
A module typically has a unique connection string name associated to its `DbContext` class using the `ConnectionStringName` attribute. Example:
@ -62,15 +114,13 @@ public class IdentityServerDbContext
}
````
For [Entity Framework Core](Entity-Framework-Core.md) and [MongoDB](MongoDB.md), write this to your `DbContext` class (and the interface if it has).
> If you are developing a reusable, database provider independent module see also [the best practices guide](Best-Practices/Index.md).
For [Entity Framework Core](Entity-Framework-Core.md) and [MongoDB](MongoDB.md), write this to your `DbContext` class (and the interface if it has). In this way, ABP uses the specified connection string for the related `DbContext` instances.
## Database Migrations for the Entity Framework Core
Relational databases require to create the database and the database schema (tables, views... etc.) before using it.
The startup template (with EF Core ORM) comes with a single database and a `.EntityFrameworkCore` project that contains related classes and the migration files for that database. This project mainly defines a *YourProjectName*DbContext that calls the `Configure...()` methods of the used modules, like `builder.ConfigurePermissionManagement()`.
The startup template (with EF Core ORM) comes with a single database and a `.EntityFrameworkCore` project that contains related classes and the migration files for that database. This project mainly defines a `YourProjectNameDbContext` class that calls the `Configure...()` methods of the used modules, like `builder.ConfigurePermissionManagement()`.
Once you want to separate a module's database, you typically will need to create a second migration path. See the [EF Core Migrations](Entity-Framework-Core-Migrations.md) document to learn how to create and use a different database for a desired module.
@ -18,9 +18,14 @@ Click to a feature to understand and learn how to use it.
All features are individually usable. If you disable a feature, it completely disappears from your application, even from the database tables, by the help of the [Global Features](../../Global-Features.md) system.
## How to Install
## Pre Requirements
- This module depends on [BlobStoring](../../Blob-Storing.md) module for keeping media content.
> Make sure `BlobStoring` module is installed and at leats one provider is configured properly. For more information, check the [documentation](../../Blob-Storing.md).
- CMS Kit uses [distributed cache](../../Caching.md) for responding faster.
> Using a distributed cache, such as [Redis](../../Redis-Cache.md), is highly recommended for data consistency in distributed/clustered deployments.
> This module is depends on [BlobStoring](../../Blob-Storing.md) module, please install `BlobStoring` module first and add a provider. For more information, check the [documentation](../../Blob-Storing.md).
## How to Install
[ABP CLI](../../CLI.md) allows installing a module to a solution using the `add-module` command. You can install the CMS Kit module in a command-line terminal with the following command:
@ -71,4 +76,4 @@ All tables/collections use the `Cms` prefix by default. Set static properties on
This module uses `CmsKit` for the connection string name. If you don't define a connection string with this name, it fallbacks to the `Default` connection string.
See the [connection strings](https://docs.abp.io/en/abp/latest/Connection-Strings) documentation for details.
See the [connection strings](https://docs.abp.io/en/abp/latest/Connection-Strings) documentation for details.
@ -13,7 +13,7 @@ In this tutorial series, you will build an ABP based web application named `Acme
* **{{DB_Value}}** as the database provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
This tutorial is organized as the following parts:
- **Part 1: Creating the server side (this part)**
- [Part 2: The book list page](Part-2.md)
@ -34,7 +34,7 @@ This tutorial has multiple versions based on your **UI** and **Database** prefer
* [Blazor UI with EF Core](https://github.com/abpframework/abp-samples/tree/master/BookStore-Blazor-EfCore)
* [Angular UI with MongoDB](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
> If you encounter the "filename too long" or "unzip error" on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you encounter the "filename too long" or "unzip" error on Windows, it's probably related to the Windows maximum file path limitation. Windows has a maximum file path limitation of 250 characters. To solve this, [enable the long path option in Windows 10](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later).
> If you face long path errors related to Git, try the following command to enable long paths in Windows. See https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
> `git config --system core.longpaths true`
@ -49,14 +49,14 @@ This part is also recorded as a video tutorial and **<a href="https://www.youtub
## Creating the Solution
Before starting to the development, create a new solution named `Acme.BookStore` and run it by following the [getting started tutorial](../Getting-Started.md).
Before starting the development, create a new solution named `Acme.BookStore` and run it by following the [getting started tutorial](../Getting-Started.md).
## Create the Book Entity
**Domain layer** in the startup template is separated into two projects:
- `Acme.BookStore.Domain` contains your [entities](../Entities.md), [domain services](../Domain-Services.md) and other core domain objects.
- `Acme.BookStore.Domain.Shared` contains `constants`, `enums` or other domain related objects those can be shared with clients.
- `Acme.BookStore.Domain.Shared` contains `constants`, `enums` or other domain related objects that can be shared with clients.
So, define your entities in the domain layer (`Acme.BookStore.Domain` project) of the solution.
@ -82,10 +82,10 @@ namespace Acme.BookStore.Books
````
* ABP Framework has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Root** is a [Domain Driven Design](../Domain-Driven-Design.md) concept which can be thought as a root entity that is directly queried and worked on (see the [entities document](../Entities.md) for more).
* `Book` entity inherits from the `AuditedAggregateRoot` which adds some base [auditing](../Audit-Logging.md) properties (like `CreationTime`, `CreatorId`, `LastModificationTime`...) on top of the `AggregateRoot` class. ABP automatically manages these properties for you.
* The `Book` entity inherits from the `AuditedAggregateRoot` which adds some base [auditing](../Audit-Logging.md) properties (like `CreationTime`, `CreatorId`, `LastModificationTime`...) on top of the `AggregateRoot` class. ABP automatically manages these properties for you.
* `Guid` is the **primary key type** of the `Book` entity.
> This tutorials leaves the entity properties with **public get/set** for the sake of simplicity. See the [entities document](../Entities.md) if you learn more about DDD best practices.
> This tutorial leaves the entity properties with **public get/set** for the sake of simplicity. See the [entities document](../Entities.md) if you want to learn more about DDD best practices.
### BookType Enum
@ -113,11 +113,11 @@ The final folder/file structure should be as shown below:
EF Core requires to relate entities with your `DbContext`. The easiest way to do this is to add a `DbSet` property to the `BookStoreDbContext` class in the `Acme.BookStore.EntityFrameworkCore` project, as shown below:
EF Core requires that you relate the entities with your `DbContext`. The easiest way to do so is adding a `DbSet` property to the `BookStoreDbContext` class in the `Acme.BookStore.EntityFrameworkCore` project, as shown below:
````csharp
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
@ -147,7 +147,7 @@ public class BookStoreMongoDbContext : AbpMongoDbContext
### Map the Book Entity to a Database Table
Locate to`OnModelCreating` method in the `BookStoreDbContext` class and add the mapping code for the `Book` entity:
Navigate to the`OnModelCreating` method in the `BookStoreDbContext` class and add the mapping code for the `Book` entity:
* `BookStoreConsts` has constant values for schema and table prefixes for your tables. You don't have to use it, but suggested to control the table prefixes in a single point.
* `ConfigureByConvention()` method gracefully configures/maps the inherited properties. Always use it for all your entities.
* `BookStoreConsts` has constant values for the schema and table prefixes for your tables. You don't have to use it, but it's suggested to control the table prefixes in a single point.
* The `ConfigureByConvention()` method gracefully configures/maps the inherited properties. Always use it for all your entities.
### Add Database Migration
@ -202,13 +202,13 @@ This will add a new migration class to the project:
> If you are using Visual Studio, you may want to use `Add-Migration Created_Book_Entity -c BookStoreMigrationsDbContext` and `Update-Database -c BookStoreMigrationsDbContext` commands in the *Package Manager Console (PMC)*. In this case, ensure that {{if UI=="MVC"}}`Acme.BookStore.Web`{{else if UI=="BlazorServer"}}`Acme.BookStore.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`Acme.BookStore.HttpApi.Host`{{end}} is the startup project and `Acme.BookStore.EntityFrameworkCore` is the *Default Project* in PMC.
> If you are using Visual Studio, you may want to use the `Add-Migration Created_Book_Entity -c BookStoreMigrationsDbContext` and `Update-Database -c BookStoreMigrationsDbContext` commands in the *Package Manager Console (PMC)*. In this case, ensure that {{if UI=="MVC"}}`Acme.BookStore.Web`{{else if UI=="BlazorServer"}}`Acme.BookStore.Blazor`{{else if UI=="Blazor" || UI=="NG"}}`Acme.BookStore.HttpApi.Host`{{end}} is the startup project and `Acme.BookStore.EntityFrameworkCore` is the *Default Project* in PMC.
{{end}}
### Add Sample Seed Data
> It's good to have some initial data in the database before running the application. This section introduces the [Data Seeding](../Data-Seeding.md) system of the ABP framework. You can skip this section if you don't want to create seed data, but it is suggested to follow it to learn this useful ABP Framework feature.
> It's good to have some initial data in the database before running the application. This section introduces the [Data Seeding](../Data-Seeding.md) system of the ABP framework. You can skip this section if you don't want to create the data seeding, but it is suggested to follow along and learn this useful ABP Framework feature.
Create a class deriving from the `IDataSeedContributor` in the `*.Domain` project by copying the following code:
@ -263,7 +263,7 @@ namespace Acme.BookStore
}
```
* This code simply uses the `IRepository<Book, Guid>` (the default [repository](../Repositories.md)) to insert two books to the database, if there is no book currently in the database.
* This code simply uses the `IRepository<Book, Guid>` (the default [repository](../Repositories.md)) to insert two books to the database in case there weren't any books in it.
### Update the Database
@ -271,7 +271,6 @@ Run the `Acme.BookStore.DbMigrator` application to update the database:
`.DbMigrator` is a console application that can be run to **migrate the database schema** and **seed the data** on **development** and **production** environments.
* **DTO** classes are used to **transfer data** between the *presentation layer* and the *application layer*. See the [Data Transfer Objects document](https://docs.abp.io/en/abp/latest/Data-Transfer-Objects) for more details.
* `BookDto` is used to transfer book data to the presentation layer in order to show the book information on the UI.
* `BookDto` is derived from the `AuditedEntityDto<Guid>` which has audit properties just like the `Book` entity defined above.
* The `BookDto` is used to transfer the book data to the presentation layer in order to show the book information on the UI.
* The `BookDto` is derived from the `AuditedEntityDto<Guid>` which has audit properties just like the `Book` entity defined above.
It will be needed to map `Book` entities to `BookDto` objects while returning books to the presentation layer. [AutoMapper](https://automapper.org) library can automate this conversion when you define the proper mapping. The startup template comes with AutoMapper pre-configured. So, you can just define the mapping in the `BookStoreApplicationAutoMapperProfile` class in the `Acme.BookStore.Application` project:
It will be needed to map the `Book` entities to the`BookDto` objects while returning books to the presentation layer. [AutoMapper](https://automapper.org) library can automate this conversion when you define the proper mapping. The startup template comes with AutoMapper pre-configured. So, you can just define the mapping in the `BookStoreApplicationAutoMapperProfile` class in the `Acme.BookStore.Application` project:
* This `DTO` class is used to get book information from the user interface while creating or updating a book.
* This `DTO` class is used to get a book information from the user interface while creating or updating the book.
* It defines data annotation attributes (like `[Required]`) to define validations for the properties. `DTO`s are [automatically validated](https://docs.abp.io/en/abp/latest/Validation) by the ABP framework.
Just like done for the `BookDto` above, we should define the mapping from the `CreateUpdateBookDto` object to the `Book` entity. The final class will be like shown below:
As done to the `BookDto` above, we should define the mapping from the `CreateUpdateBookDto` object to the `Book` entity. The final class will be as shown below:
* `BookAppService` is derived from `CrudAppService<...>` which implements all the CRUD (create, read, update, delete) methods defined by the `ICrudAppService`.
* `BookAppService` injects `IRepository<Book, Guid>` which is the default repository for the `Book` entity. ABP automatically creates default repositories for each aggregate root (or entity). See the [repository document](https://docs.abp.io/en/abp/latest/Repositories).
* `BookAppService` uses `IObjectMapper` service ([see](../Object-To-Object-Mapping.md)) to map `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. We have defined the mappings before, so it will work as expected.
* `BookAppService` uses `IObjectMapper` service ([see](../Object-To-Object-Mapping.md)) to map the `Book` objects to the`BookDto` objects and `CreateUpdateBookDto` objects to the`Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. We have defined the mappings before, so it will work as expected.
## Auto API Controllers
In a typical ASP.NET Core application, you create **API Controllers** to expose application services as **HTTP API** endpoints. This allows browsers or 3rd-party clients to call them over HTTP.
In a typical ASP.NET Core application, you create **API Controllers** to expose the application services as **HTTP API** endpoints. This allows browsers or 3rd-party clients to call them over HTTP.
ABP can [**automagically**](../API/Auto-API-Controllers.md) configures your application services as MVC API Controllers by convention.
ABP can [**automagically**](../API/Auto-API-Controllers.md) configure your application services as MVC API Controllers by convention.
@ -13,7 +13,7 @@ In this tutorial series, you will build an ABP based web application named `Acme
* **{{DB_Value}}** as the ORM provider.
* **{{UI_Value}}** as the UI Framework.
This tutorial is organized as the following parts;
This tutorial is organized as the following parts:
- [Part 1: Creating the server side](Part-1.md)
- **Part 2: The book list page (this part)**
@ -53,7 +53,7 @@ This part is also recorded as a video tutorial and **<a href="https://www.youtub
It's common to call the HTTP API endpoints via AJAX from the **JavaScript** side. You can use `$.ajax` or another tool to call the endpoints. However, ABP offers a better way.
ABP **dynamically** creates **[JavaScript Proxies](../UI/AspNetCore/Dynamic-JavaScript-Proxies.md)** for all API endpoints. So, you can use any **endpoint** just like calling a **JavaScript function**.
ABP **dynamically** creates **[JavaScript Proxies](../UI/AspNetCore/Dynamic-JavaScript-Proxies.md)** for all the API endpoints. So, you can use any **endpoint** just like calling a **JavaScript function**.
* `acme.bookStore.books` is the namespace of the `BookAppService` converted to [camelCase](https://en.wikipedia.org/wiki/Camel_case).
* `book` is the conventional name for the `BookAppService` (removed `AppService` postfix and converted to camelCase).
* `getList` is the conventional name for the `GetListAsync` method defined in the `CrudAppService` base class (removed `Async` postfix and converted to camelCase).
* `{}` argument is used to send an empty object to the `GetListAsync` method which normally expects an object of type `PagedAndSortedResultRequestDto` that is used to send paging and sorting options to the server (all properties are optional with default values, so you can send an empty object).
* `getList` function returns a `promise`. You can pass a callback to the `then` (or `done`) function to get the result returned from the server.
* The `{}` argument is used to send an empty object to the `GetListAsync` method which normally expects an object of type `PagedAndSortedResultRequestDto` that is used to send paging and sorting options to the server (all properties are optional with default values, so you can send an empty object).
* The `getList` function returns a `promise`. You can pass a callback to the `then` (or `done`) function to get the result returned from the server.
> If you downloaded the source code of the tutorial and following the steps from the sample, you should also pass the `authorId` parameter to the create method for **creating a new book**.
> If you downloaded the source code of the tutorial and are following the steps from the sample, you should also pass the `authorId` parameter to the create method for **creating a new book**.
You should see a message in the console something like that:
You should see a message in the console that looks something like this:
````text
successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246
@ -100,19 +100,19 @@ successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246
Check the `Books` table in the database to see the new book row. You can try `get`, `update` and `delete` functions yourself.
We will use these dynamic proxy functions in the next sections to communicate to the server.
We will use these dynamic proxy functions in the next sections to communicate with the server.
{{end}}
## Localization
Before starting to the UI development, we first want to prepare the localization texts (you normally do this when needed while developing your application).
Before starting the UI development, we first want to prepare the localization texts (you normally do this when needed while developing your application).
Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project:
Open the `en.json` (*the English translations*) file and change the content as below:
Open the `en.json` (*the English translations*) file and change the content as shown below:
````json
{
@ -152,17 +152,17 @@ Open the `en.json` (*the English translations*) file and change the content as b
* Add `Menu:` prefix for menu items.
* Use `Enum:<enum-type>:<enum-value>` naming convention to localize the enum members. When you do it like that, ABP can automatically localize the enums in some proper cases.
If a text is not defined in the localization file, it **fallbacks** to the localization key (as ASP.NET Core's standard behavior).
If a text is not defined in the localization file, it **falls back** to the localization key (as ASP.NET Core's standard behavior).
> ABP's localization system is built on [ASP.NET Core's standard localization](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) system and extends it in many ways. See the [localization document](../Localization.md) for details.
> ABP's localization system is built on the [ASP.NET Core's standard localization](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) system and extends it in many ways. Check the [localization document](../Localization.md) for details.
{{if UI == "MVC"}}
## Create a Books Page
It's time to create something visible and usable! Instead of classic MVC, we will use the [Razor Pages UI](https://docs.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/razor-pages-start) approach which is recommended by Microsoft.
It's time to create something visible and usable! Instead of the classic MVC, we will use the [Razor Pages UI](https://docs.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/razor-pages-start) approach which is recommended by Microsoft.
Create `Books` folder under the `Pages` folder of the `Acme.BookStore.Web` project. Add a new Razor Page by right clicking the Books folder then selecting **Add > Razor Page** menu item. Name it as `Index`:
Create a `Books` folder under the `Pages` folder of the `Acme.BookStore.Web` project. Add a new Razor Page by right clicking the Books folder then selecting **Add > Razor Page** menu item. Name it as `Index`:
Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see the new menu item has been added to the main menu:
Run the project, login to the application with the username `admin` and the password `1q2w3E*` and you can see that the new menu item has been added to the main menu:
When you click to the Books menu item under the Book Store parent, you are being redirected to the new empty Books Page.
When you click on the Books menu item under the Book Store parent, you will be redirected to the new empty Books Page.
### Book List
We will use the [Datatables.net](https://datatables.net/) jQuery library to show the book list. Datatables library completely work via AJAX, it is fast, popular and provides a good user experience.
We will use the [Datatables.net](https://datatables.net/) jQuery library to show the book list. Datatables library completely works via AJAX, it is fast, popular and provides a good user experience.
> Datatables library is configured in the startup template, so you can directly use it in any page without including any style or script file to your page.
> Datatables library is configured in the startup template, so you can directly use it in any page without including any style or script file for your page.
#### Index.cshtml
Change the `Pages/Books/Index.cshtml` as following:
Change the `Pages/Books/Index.cshtml` as the following:
````html
@page
@ -250,8 +250,8 @@ Change the `Pages/Books/Index.cshtml` as following:
</abp-card>
````
* `abp-script` [tag helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro) is used to add external **scripts** to the page. It has many additional features compared to standard `script` tag. It handles **minification** and **versioning**. See the [bundling & minification document](../UI/AspNetCore/Bundling-Minification.md) for details.
* `abp-card` is a tag helper for Twitter Bootstrap's [card component](https://getbootstrap.com/docs/4.5/components/card/). There are other useful tag helpers provided by the ABP Framework to easily use most of the [bootstrap](https://getbootstrap.com/) components. You could use the regular HTML tags instead of these tag helpers, but using tag helpers reduces HTML code and prevents errors by help the of IntelliSense and compile time type checking. Further information, see the [tag helpers](../UI/AspNetCore/Tag-Helpers/Index.md) document.
* `abp-script` [tag helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro) is used to add external **scripts** to the page. It has many additional features compared to the standard `script` tag. It handles **minification** and **versioning**. Check the [bundling & minification document](../UI/AspNetCore/Bundling-Minification.md) for details.
* `abp-card` is a tag helper for Twitter Bootstrap's [card component](https://getbootstrap.com/docs/4.5/components/card/). There are other useful tag helpers provided by the ABP Framework to easily use most of [bootstrap](https://getbootstrap.com/)'s components. You could use the regular HTML tags instead of these tag helpers, but using tag helpers reduces HTML code and prevents errors by the help of the IntelliSense and compiles time type checking. For further information, check the [tag helpers](../UI/AspNetCore/Tag-Helpers/Index.md) document.
#### Index.js
@ -316,9 +316,9 @@ $(function () {
});
````
* `abp.localization.getResource` gets a function that is used to localize text using the same JSON file defined in the server side. In this way, you can share the localization values with the client side.
* `abp.localization.getResource` gets a function that is used to localize text using the same JSON file defined on the server side. In this way, you can share the localization values with the client side.
* `abp.libs.datatables.normalizeConfiguration` is a helper function defined by the ABP Framework. There's no requirement to use it, but it simplifies the [Datatables](https://datatables.net/) configuration by providing conventional default values for missing options.
* `abp.libs.datatables.createAjax` is another helper function to adapt ABP's dynamic JavaScript API proxies to [Datatable](https://datatables.net/)'s expected parameter format
* `abp.libs.datatables.createAjax` is another helper function to adapt the ABP's dynamic JavaScript API proxies to the [Datatable](https://datatables.net/)'s expected parameter format
* `acme.bookStore.books.book.getList` is the dynamic JavaScript proxy function introduced before.
* [luxon](https://moment.github.io/luxon/) library is also a standard library that is pre-configured in the solution, so you can use to perform date/time operations easily.
@ -336,9 +336,9 @@ This is a fully working, server side paged, sorted and localized table of books.
## Install NPM packages
> Notice: This tutorial is based on the ABP Framework v3.1.0+ If your project version is older, then please upgrade your solution. See the [migration guide](../UI/Angular/Migration-Guide-v3.md) if you are upgrading an existing project with v2.x.
> Notice: This tutorial is based on the ABP Framework v3.1.0+ If your project version is older, then please upgrade your solution. Check the [migration guide](../UI/Angular/Migration-Guide-v3.md) if you are upgrading an existing project with v2.x.
If you haven't done it before, open a new command line interface (terminal window) and go to your `angular` folder and then run `yarn` command to install the NPM packages:
If you haven't done it before, open a new command line interface (terminal window) and go to your `angular` folder and then run the `yarn` command to install the NPM packages:
```bash
yarn
@ -400,7 +400,7 @@ export class BookModule { }
### Routing
Generated code places the new route definition to the `src/app/app-routing.module.ts` file as shown below:
The generated code places the new route definition to the `src/app/app-routing.module.ts` file as shown below:
````js
const routes: Routes = [
@ -409,7 +409,7 @@ const routes: Routes = [
];
````
Now, open the `src/app/route.provider.ts` file replace the `configureRoutes` function declaration as shown below:
Now, open the `src/app/route.provider.ts` file and replace the `configureRoutes` function declaration as shown below:
```js
function configureRoutes(routes: RoutesService) {
@ -443,18 +443,18 @@ function configureRoutes(routes: RoutesService) {
`RoutesService` is a service provided by the ABP Framework to configure the main menu and the routes.
* `path` is the URL of the route.
* `name` is the localized menu item name (see the [localization document](../UI/Angular/Localization.md) for details).
* `name` is the localized menu item name (check the [localization document](../UI/Angular/Localization.md) for details).
* `iconClass` is the icon of the menu item (you can use [Font Awesome](https://fontawesome.com/) icons by default).
* `order` is the order of the menu item.
* `layout` is the layout of the BooksModule's routes (there are three types of pre-defined layouts: `eLayoutType.application`, `eLayoutType.account` or `eLayoutType.empty`).
For more information, see the [RoutesService document](../UI/Angular/Modifying-the-Menu.md#via-routesservice).
For more information, check the [RoutesService document](../UI/Angular/Modifying-the-Menu.md#via-routesservice).
### Service Proxy Generation
[ABP CLI](../CLI.md) provides `generate-proxy` command that generates client proxies for your HTTP APIs to make easy to consume your HTTP APIs from the client side. Before running`generate-proxy` command, your host must be up and running.
[ABP CLI](../CLI.md) provides a `generate-proxy` command that generates client proxies for your HTTP APIs to make your HTTP APIs easy to consume by the client side. Before running the`generate-proxy` command, your host must be up and running.
> **Warning**: There is a problem with IIS Express; it doesn't allow to connect to the application from another process. If you are using Visual Studio, select the `Acme.BookStore.HttpApi.Host` instead of IIS Express in the run button drop-down list, as shown in the figure below:
> **Warning**: There is a problem with IIS Express; it doesn't allow connecting to the application from another process. If you are using Visual Studio, select the `Acme.BookStore.HttpApi.Host` instead of IIS Express in the run button drop-down list, as shown in the figure below:
@ -499,9 +499,9 @@ export class BookComponent implements OnInit {
```
* We imported and injected the generated `BookService`.
* We are using the [ListService](../UI/Angular/List-Service.md), a utility service of the ABP Framework which provides easy pagination, sorting and searching.
* We are using the [ListService](../UI/Angular/List-Service.md), a utility service from the ABP Framework which provides easy pagination, sorting and searching.
Open the `/src/app/book/book.component.html` and replace the content as below:
Open the `/src/app/book/book.component.html` and replace the content as shown below:
```html
<divclass="card">
@ -546,7 +546,7 @@ Now you can see the final result on your browser:
## Create a Books Page
It's time to create something visible and usable! Right click to the `Pages` folder under the `Acme.BookStore.Blazor` project and add a new **razor component**, named `Books.razor`:
It's time to create something visible and usable! Right click on the `Pages` folder under the `Acme.BookStore.Blazor` project and add a new **razor component**, named `Books.razor`:
@ -562,7 +562,7 @@ Replace the contents of this component as shown below:
}
````
### Add Books Page to the Main Menu
### Add the Books Page to the Main Menu
Open the `BookStoreMenuContributor` class in the `Blazor` project add the following code to the end of the `ConfigureMainMenuAsync` method:
@ -582,17 +582,17 @@ context.Menu.AddItem(
);
````
Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see the new menu item has been added to the main menu:
Run the project, login to the application with the username `admin` and the password `1q2w3E*` and see that the new menu item has been added to the main menu:
When you click to the Books menu item under the Book Store parent, you are being redirected to the new empty Books Page.
When you click on the Books menu item under the Book Store parent, you will be redirected to the new empty Books Page.
### Book List
We will use the [Blazorise library](https://blazorise.com/) as the UI component kit. It is a very powerful library that supports major HTML/CSS frameworks, including the Bootstrap.
We will use the [Blazorise library](https://blazorise.com/) as the UI component kit. It is a very powerful library that supports major HTML/CSS frameworks, including Bootstrap.
ABP Framework provides a generic base class,`AbpCrudPageBase<...>`, to create CRUD style pages. This base class is compatible to the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `AbpCrudPageBase` to automate the code behind for the standard CRUD stuff.
ABP Framework provides a generic base class -`AbpCrudPageBase<...>`, to create CRUD style pages. This base class is compatible with the `ICrudAppService` that was used to build the `IBookAppService`. So, we can inherit from the `AbpCrudPageBase` to automate the code behind for the standard CRUD stuff.
Open the `Books.razor` and replace the content as the following:
@ -651,17 +651,17 @@ Open the `Books.razor` and replace the content as the following:
</Card>
````
> If you see some syntax errors, you can ignore them if your application properly built and run. Visual Studio still has some bugs with Blazor.
> If you see some syntax errors, you can ignore them if your application is properly built and running. Visual Studio still has some bugs with Blazor.
* Inherited from the`AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>` which implements all the CRUD details for us.
* Inherited from `AbpCrudPageBase<IBookAppService, BookDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>` which implements all the CRUD details for us.
* `Entities`, `TotalCount`, `PageSize`, `OnDataGridReadAsync` are defined in the base class.
* Injected `IStringLocalizer<BookStoreResource>` (as `L` object) and used for localization.
While the code above pretty easy to understand, you can check the Blazorise [Card](https://blazorise.com/docs/components/card/) and [DataGrid](https://blazorise.com/docs/extensions/datagrid/) documents to understand them better.
While the code above is pretty easy to understand, you can check the Blazorise [Card](https://blazorise.com/docs/components/card/) and [DataGrid](https://blazorise.com/docs/extensions/datagrid/) documents to understand them better.
#### About the AbpCrudPageBase
We will continue to benefit from the`AbpCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call server side HTTP APIs in your Blazor applications.
We will continue benefitting from `AbpCrudPageBase` for the books page. You could just inject the `IBookAppService` and perform all the server side calls yourself (thanks to the [Dynamic C# HTTP API Client Proxy](../API/Dynamic-CSharp-API-Clients.md) system of the ABP Framework). We will do it manually for the authors page to demonstrate how to call the server side HTTP APIs in your Blazor applications.
## Run the Final Application
@ -675,4 +675,4 @@ This is a fully working, server side paged, sorted and localized table of books.
## The Next Part
See the [next part](Part-3.md) of this tutorial.
Check the [next part](Part-3.md) of this tutorial.
* This class is derived from the `BookStorePageModel` instead of standard `PageModel`. `BookStorePageModel` indirectly inherits the `PageModel` and adds some common properties & methods that can be shared in your page model classes.
* This class is derived from the `BookStorePageModel` instead of the standard `PageModel`. `BookStorePageModel` indirectly inherits the `PageModel` and adds some common properties & methods that can be shared in your page model classes.
* `[BindProperty]` attribute on the `Book` property binds post request data to this property.
* This class simply injects the `IBookAppService` in the constructor and calls the `CreateAsync` method in the `OnPostAsync` handler.
* It creates a new `CreateUpdateBookDto` object in the `OnGet` method. ASP.NET Core can work without creating a new instance like that. However, it doesn't create an instance for you and if your class has some default value assignments or code execution in the class constructor, they won't work. For this case, we set default values for some of the `CreateUpdateBookDto` properties.
@ -129,7 +129,7 @@ Open the `CreateModal.cshtml` file and paste the code below:
</abp-dynamic-form>
````
* This modal uses `abp-dynamic-form` [tag helper](../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md) to automatically create the form from the `CreateBookViewModel` model class.
* This modal uses `abp-dynamic-form` [tag helper](../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md) to automatically create the form from the `CreateBookViewModel` model class.
* `abp-model` attribute indicates the model object where it's the `Book` property in this case.
* `abp-form-content` tag helper is a placeholder to render the form controls (it is optional and needed only if you have added some other content in the `abp-dynamic-form` tag, just like in this page).
@ -155,7 +155,7 @@ Open the `Pages/Books/Index.cshtml` and set the content of `abp-card-header` tag
</abp-card-header>
````
The final content of the `Index.cshtml` is shown below:
The final content of `Index.cshtml` is shown below:
````html
@page
@ -193,7 +193,7 @@ This adds a new button called **New book** to the **top-right** of the table:
* `abp.ModalManager` is a helper class to manage modals in the client side. It internally uses Twitter Bootstrap's standard modal, but abstracts many details by providing a simple API.
* `abp.ModalManager` is a helper class to manage modals on the client side. It internally uses Twitter Bootstrap's standard modal, but abstracts many details by providing a simple API.
* `createModal.onResult(...)` used to refresh the data table after creating a new book.
* `createModal.open();` is used to open the model to create a new book.
The final content of the `Index.js`should be like that:
The final content of the `Index.js`file should be like this:
````js
$(function () {
@ -290,7 +290,7 @@ Create a new razor page, named `EditModal.cshtml` under the `Pages/Books` folder
### EditModal.cshtml.cs
Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace with the following code:
Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace it with the following code:
* `[HiddenInput]` and `[BindProperty]` are standard ASP.NET Core MVC attributes. `SupportsGet` is used to be able to get `Id` value from query string parameter of the request.
* In the `OnGetAsync` method, we get `BookDto` from the `BookAppService` and this is being mapped to the DTO object `CreateUpdateBookDto`.
* `[HiddenInput]` and `[BindProperty]` are standard ASP.NET Core MVC attributes. `SupportsGet` is used to be able to get the `Id` value from the query string parameter of the request.
* In the `OnGetAsync` method, we get the `BookDto` from the `BookAppService` and this is being mapped to the DTO object `CreateUpdateBookDto`.
* The `OnPostAsync` uses `BookAppService.UpdateAsync(...)` to update the entity.
### Mapping from BookDto to CreateUpdateBookDto
To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mapping. To do this, open the `BookStoreWebAutoMapperProfile.cs` in the `Acme.BookStore.Web` project and change it as shown below:
To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mapping. To do this, open the `BookStoreWebAutoMapperProfile.cs`file in the `Acme.BookStore.Web` project and change it as shown below:
````csharp
using AutoMapper;
@ -385,16 +385,16 @@ Replace `EditModal.cshtml` content with the following content:
</abp-dynamic-form>
````
This page is very similar to the `CreateModal.cshtml`, except:
This page is very similar to `CreateModal.cshtml`, except:
* It includes an `abp-input` for the `Id` property to store `Id` of the editing book (which is a hidden input).
* It includes an `abp-input` for the `Id` property to store the `Id` of the editing book (which is a hidden input).
* It uses `Books/EditModal` as the post URL.
### Add "Actions" Dropdown to the Table
We will add a dropdown button to the table named *Actions*.
Open the `Pages/Books/Index.js` and replace the content as below:
Open the `Pages/Books/Index.js`file and replace the content as below:
````js
$(function () {
@ -482,8 +482,8 @@ $(function () {
* Added a new `ModalManager` named `editModal` to open the edit modal dialog.
* Added a new column at the beginning of the `columnDefs` section. This column is used for the "*Actions*" dropdown button.
* "*Edit*" action simply calls `editModal.open()` to open the edit dialog.
* `editModal.onResult(...)` callback refreshes the data table when you close the edit modal.
* The "*Edit*" action simply calls `editModal.open()` to open the edit dialog.
* The `editModal.onResult(...)` callback refreshes the data table when you close the edit modal.
You can run the application and edit any book by selecting the edit action on a book.
@ -493,7 +493,7 @@ The final UI looks as below:
## Deleting a Book
Open the `Pages/Books/Index.js` and add a new item to the `rowAction``items`:
Open the `Pages/Books/Index.js`file and add a new item to the `rowAction``items`:
````js
{
@ -512,8 +512,8 @@ Open the `Pages/Books/Index.js` and add a new item to the `rowAction` `items`:
}
````
* `confirmMessage` option is used to ask a confirmation question before executing the `action`.
* `acme.bookStore.books.book.delete(...)` method makes an AJAX request to the server to delete a book.
* The `confirmMessage` option is used to ask a confirmation question before executing the `action`.
* The `acme.bookStore.books.book.delete(...)` method makes an AJAX request to the server to delete a book.
* `abp.notify.info()` shows a notification after the delete operation.
Since we've used two new localization texts (`BookDeletionConfirmationMessage` and `SuccessfullyDeleted`) you need to add these to the localization file (`en.json` under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project):
@ -721,10 +721,10 @@ Open `/src/app/book/book.component.html` and make the following changes:
</abp-modal>
```
* Added `New book` button to the card header..
* Added a `New book` button to the card header..
* Added the `abp-modal` which renders a modal to allow user to create a new book. `abp-modal` is a pre-built component to show modals. While you could use another approach to show a modal, `abp-modal` provides additional benefits.
You can open your browser and click **New book** button to see the new modal.
You can open your browser and click the **New book** button to see the new modal.

@ -801,11 +801,11 @@ export class BookComponent implements OnInit {
```
* Imported `FormGroup`, `FormBuilder` and `Validators` from `@angular/forms`.
* Added `form: FormGroup` property.
* Added `bookTypes` property as a list of `BookType` enum members. That will be used in form options.
* Added a `form: FormGroup` property.
* Added a `bookTypes` property as a list of `BookType` enum members. That will be used in form options.
* Injected `FormBuilder` into the constructor. [FormBuilder](https://angular.io/api/forms/FormBuilder) provides convenient methods for generating form controls. It reduces the amount of boilerplate needed to build complex forms.
* Added `buildForm` method to the end of the file and executed the `buildForm()` in the `createBook` method.
* Added `save` method.
* Added a `buildForm` method to the end of the file and executed the `buildForm()` in the `createBook` method.
* Added a `save` method.
Open `/src/app/book/book.component.html` and replace `<ng-template #abpBody> </ng-template>` with the following code part:
@ -863,7 +863,7 @@ Also replace `<ng-template #abpFooter> </ng-template>` with the following code p
### Datepicker
We've used [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) in this component. So, need to arrange dependencies related to this component.
We've used [NgBootstrap datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) in this component. So, we need to arrange the dependencies related to this component.
Open `/src/app/book/book.module.ts` and replace the content as below:
@ -959,7 +959,7 @@ export class BookComponent implements OnInit {
```
* Imported ` NgbDateNativeAdapter` and `NgbDateAdapter`.
* We added a new provider `NgbDateAdapter` that converts Datepicker value to `Date` type. See the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details.
* We added a new provider `NgbDateAdapter` that converts the Datepicker value to `Date` type. Check out the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details.
Now, you can open your browser to see the changes:
@ -1054,14 +1054,14 @@ export class BookComponent implements OnInit {
```
* We declared a variable named `selectedBook` as `BookDto`.
* We added `editBook` method. This method fetches the book with the given `id` and sets it to `selectedBook` object.
* We added an `editBook` method. This method fetches the book with the given `id` and sets it to `selectedBook` object.
* We replaced the `buildForm` method so that it creates the form with the `selectedBook` data.
* We replaced the `createBook` method so it sets `selectedBook` to an empty object.
* We changed the `save` method to handle both of create and update operations.
### Add "Actions" Dropdown to the Table
Open the `/src/app/book/book.component.html` and add the following `ngx-datatable-column` definition as the first column in the `ngx-datatable`:
Open `/src/app/book/book.component.html` and add the following `ngx-datatable-column` definition as the first column in the `ngx-datatable`:
```html
<ngx-datatable-column
@ -1101,11 +1101,11 @@ Also, change the `ng-template #abpHeader` section as shown below:
</ng-template>
```
This template will show **Edit** text for edit record operation, **New Book** for new record operation in the title.
This template will show the **Edit** text for edit record operation, **New Book** for new record operation in the title.
## Deleting a Book
Open the `/src/app/book/book.component.ts` and inject the `ConfirmationService`.
Open the `/src/app/book/book.component.ts`file and inject the `ConfirmationService`.
Replace the constructor as below:
@ -1137,7 +1137,7 @@ delete(id: string) {
* We injected `ConfirmationService` to the constructor.
* Added a `delete` method.
> See the [Confirmation Popup documentation](../UI/Angular/Confirmation-Service) for more about this service.
> Check out the [Confirmation Popup documentation](../UI/Angular/Confirmation-Service) for more about this service.
### Add a Delete Button
@ -1169,7 +1169,7 @@ Clicking the "Delete" action calls the `delete` method which then shows a confir
In this section, you will learn how to create a new modal dialog form to create a new book. Since we've inherited from the `AbpCrudPageBase`, we only need to develop the view part.
### Add "New Button" Button
### Add a "New Button" Button
Open the `Books.razor` and replace the `<CardHeader>` section with the following code:
@ -1191,7 +1191,7 @@ This will change the card header by adding a "New book" button to the right side
Now, we can add a modal that will be opened when we click to the button.
Now, we can add a modal that will be opened when we click the button.
### Book Creation Modal
@ -1259,7 +1259,7 @@ This code requires a service; Inject the `AbpBlazorMessageLocalizerHelper<T>` at
````
* The form implements validation and the `AbpBlazorMessageLocalizerHelper` is used to simply localize the validation messages.
* `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` method are defined by the base class. See the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components.
* The `CreateModal` object, `CloseCreateModalAsync` and `CreateEntityAsync` methods are defined by the base class. Check out the [Blazorise documentation](https://blazorise.com/docs/) if you want to understand the `Modal` and the other components.
That's all. Run the application and try to add a new book:
@ -1267,7 +1267,7 @@ That's all. Run the application and try to add a new book:
## Updating a Book
Editing a books is similar to the creating a new book.
Editing a book is similar to creating a new book.
### Actions Dropdown
@ -1287,7 +1287,7 @@ Open the `Books.razor` and add the following `DataGridEntityActionsColumn` secti
* `OpenEditModalAsync` is defined in the base class which takes the entity (book) to edit.
`DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. `DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it:
The `DataGridEntityActionsColumn` component is used to show an "Actions" dropdown for each row in the `DataGrid`. The`DataGridEntityActionsColumn` shows a **single button** instead of a dropdown if there is only one available action inside it:
[`ConfigStateService`](Config-State-Service.md) service can be used to obtain the `application-configuration` API response (e.g. getting current user or tenant).
In the example above, added a new user menu item labeled "Reports", updated the "My account" item HTML content, and removed the "Logout" item.
`PageAlertService` service is used to get the current page alerts to render on the layout. See the [Page Alerts](Page-Alerts.md) document to learn more.
`PageAlertService` service is used to get the current page alerts to render on the layout. See the [Page Alerts](Page-Alerts.md) document to learn more.
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() <= 0)
{
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
// ADDED SEED DATA FOR AUTHORS
if (await _authorRepository.GetCountAsync() <= 0)
{
await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"George Orwell",
new DateTime(1903, 06, 25),
"Orwell produced literary criticism and poetry, fiction and polemical journalism; and is best known for the allegorical novella Animal Farm (1945) and the dystopian novel Nineteen Eighty-Four (1949)."
)
);
await _authorRepository.InsertAsync(
await _authorManager.CreateAsync(
"Douglas Adams",
new DateTime(1952, 03, 11),
"Douglas Adams was an English author, screenwriter, essayist, humorist, satirist and dramatist. Adams was an advocate for environmentalism and conservation, a lover of fast cars, technological innovation and the Apple Macintosh, and a self-proclaimed 'radical atheist'."
returnValidationAttributeHelper.SetErrorMessagePlaceholders("The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.");