@ -1,24 +1,24 @@ |
|||
name: Merge branch rel-4.1 with rel-4.0 |
|||
name: Merge branch dev with rel-4.1 |
|||
on: |
|||
push: |
|||
branches: |
|||
- rel-4.0 |
|||
- rel-4.1 |
|||
jobs: |
|||
merge-rel-4-1-with-rel-4-0: |
|||
merge-dev-with-rel-4-1: |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- uses: actions/checkout@v2 |
|||
with: |
|||
ref: rel-4.1 |
|||
ref: dev |
|||
- name: Reset promotion branch |
|||
run: | |
|||
git fetch origin rel-4.0:rel-4.0 |
|||
git reset --hard rel-4.0 |
|||
git fetch origin $GITHUB_REF:$GITHUB_REF |
|||
git reset --hard $GITHUB_REF |
|||
- name: Create Pull Request |
|||
uses: peter-evans/create-pull-request@v3 |
|||
with: |
|||
branch: auto-merge/rel-4-0/${{github.run_number}} |
|||
title: Merge branch rel-4.1 with ${{github.ref}} |
|||
body: This PR generated automatically to merge rel-4.1 with rel-4.0. Please review the changed files before merging to prevent any errors that may occur. |
|||
branch: auto-merge/rel-4-1/${{github.run_number}} |
|||
title: Merge branch dev with rel-4.1 |
|||
body: This PR generated automatically to merge dev with rel-4.1. Please review the changed files before merging to prevent any errors that may occur. |
|||
reviewers: ${{github.actor}} |
|||
token: ${{ github.token }} |
|||
|
|||
@ -0,0 +1,29 @@ |
|||
<Project> |
|||
<PropertyGroup> |
|||
|
|||
<!-- All Microsoft packages --> |
|||
<MicrosoftPackageVersion>5.0.*</MicrosoftPackageVersion> |
|||
|
|||
<!-- Microsoft.NET.Test.Sdk https://www.nuget.org/packages/Microsoft.NET.Test.Sdk --> |
|||
<MicrosoftNETTestSdkPackageVersion>16.8.3</MicrosoftNETTestSdkPackageVersion> |
|||
|
|||
<!-- NSubstitute https://www.nuget.org/packages/NSubstitute --> |
|||
<NSubstitutePackageVersion>4.2.2</NSubstitutePackageVersion> |
|||
|
|||
<!-- Shouldly https://www.nuget.org/packages/Shouldly --> |
|||
<ShouldlyPackageVersion>4.0.1</ShouldlyPackageVersion> |
|||
|
|||
<!-- xunit https://www.nuget.org/packages/xUnit --> |
|||
<xUnitPackageVersion>2.4.1</xUnitPackageVersion> |
|||
|
|||
<!-- xunit.extensibility.execution https://www.nuget.org/packages/xunit.extensibility.execution --> |
|||
<xUnitExtensibilityExecutionPackageVersion>2.4.1</xUnitExtensibilityExecutionPackageVersion> |
|||
|
|||
<!-- xunit.runner.visualstudio https://www.nuget.org/packages/xunit.runner.visualstudio --> |
|||
<xUnitRunnerVisualstudioPackageVersion>2.4.3</xUnitRunnerVisualstudioPackageVersion> |
|||
|
|||
<!-- Mongo2Go https://www.nuget.org/packages/Mongo2Go --> |
|||
<Mongo2GoPackageVersion>2.2.14</Mongo2GoPackageVersion> |
|||
|
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -1,33 +1,33 @@ |
|||
{ |
|||
"culture": "es", |
|||
"texts": { |
|||
"Volo.AbpIo.Domain:010004": "", |
|||
"Volo.AbpIo.Domain:010005": "", |
|||
"Volo.AbpIo.Domain:010006": "", |
|||
"Volo.AbpIo.Domain:010007": "", |
|||
"Volo.AbpIo.Domain:010008": "", |
|||
"Volo.AbpIo.Domain:010009": "", |
|||
"Volo.AbpIo.Domain:010010": "", |
|||
"Volo.AbpIo.Domain:010011": "", |
|||
"Volo.AbpIo.Domain:010012": "", |
|||
"Volo.AbpIo.Domain:020001": "", |
|||
"Volo.AbpIo.Domain:020002": "", |
|||
"Volo.AbpIo.Domain:020003": "", |
|||
"Volo.AbpIo.Domain:020004": "", |
|||
"WantToLearn?": "", |
|||
"ReadyToGetStarted?": "", |
|||
"JoinOurCommunity": "", |
|||
"GetStartedUpper": "", |
|||
"ForkMeOnGitHub": "", |
|||
"Features": "", |
|||
"GetStarted": "", |
|||
"Documents": "", |
|||
"Community": "", |
|||
"ContributionGuide": "", |
|||
"Blog": "", |
|||
"Commercial": "", |
|||
"MyAccount": "", |
|||
"SeeDocuments": "", |
|||
"Samples": "" |
|||
"Volo.AbpIo.Domain:010004": "Número máximo de miembros alcanzado!", |
|||
"Volo.AbpIo.Domain:010005": "Número máximo de propietarios alcanzado!", |
|||
"Volo.AbpIo.Domain:010006": "Este usuario ya es un propietario de esta organización!", |
|||
"Volo.AbpIo.Domain:010007": "Este usuario ya es un desarrollador en este organización!", |
|||
"Volo.AbpIo.Domain:010008": "Número de desarrolladores permitido no puede ser menor que el número actual de desarrolladores!", |
|||
"Volo.AbpIo.Domain:010009": "El número de desarrolladores no puede ser menor que cero!", |
|||
"Volo.AbpIo.Domain:010010": "Número máximo de dirección mac excedido!", |
|||
"Volo.AbpIo.Domain:010011": "Una licencia personal no puede tener más de un desarrollador!", |
|||
"Volo.AbpIo.Domain:010012": "La licencia no puede ser extendida un mes despues de que expire!", |
|||
"Volo.AbpIo.Domain:020001": "Este paquete NPM no pudo ser borrado porque \"{NugetPackages}\" paquetes Nuget son dependientes de este paquete.", |
|||
"Volo.AbpIo.Domain:020002": "Este paquete NPM no pudo ser borrado porque \"{Modules}\" modulos están usando este paquete.", |
|||
"Volo.AbpIo.Domain:020003": "Este paquete NPM no pudo ser borrado porque \"{Modules}\" modulos están usando este paquete.y \"{NugetPackages}\" paquetes Nuget son dependientes de este paquete.", |
|||
"Volo.AbpIo.Domain:020004": "Este paquete Nuget no pudo ser borrado porque \"{Modules}\" modulos están usando este paquete.", |
|||
"WantToLearn?": "¿Quieres aprender?", |
|||
"ReadyToGetStarted?": "¿Preparado para comenzar?", |
|||
"JoinOurCommunity": "Unete a nuestra comunidad", |
|||
"GetStartedUpper": "COMENZAR", |
|||
"ForkMeOnGitHub": "Fork en GitHub", |
|||
"Features": "Características", |
|||
"GetStarted": "Comenzar", |
|||
"Documents": "Documentos", |
|||
"Community": "Comunidad", |
|||
"ContributionGuide": "Guia de contribución", |
|||
"Blog": "Blog", |
|||
"Commercial": "Comercial", |
|||
"MyAccount": "Mi cuenta", |
|||
"SeeDocuments": "Ver documentos", |
|||
"Samples": "Ejemplos" |
|||
} |
|||
} |
|||
@ -1,35 +1,35 @@ |
|||
{ |
|||
"culture": "es", |
|||
"texts": { |
|||
"OrganizationManagement": "", |
|||
"OrganizationList": "", |
|||
"Volo.AbpIo.Commercial:010003": "", |
|||
"OrganizationNotFoundMessage": "", |
|||
"DeveloperCount": "", |
|||
"QuestionCount": "", |
|||
"Unlimited": "", |
|||
"Owners": "", |
|||
"AddMember": "", |
|||
"AddOwner": "", |
|||
"AddDeveloper": "", |
|||
"UserName": "", |
|||
"Name": "", |
|||
"EmailAddress": "", |
|||
"Developers": "", |
|||
"OrganizationManagement": "Gestión de la organización", |
|||
"OrganizationList": "Lista organización", |
|||
"Volo.AbpIo.Commercial:010003": "Tu no eres el propietario de esta organización!", |
|||
"OrganizationNotFoundMessage": "Organización no encontrada!", |
|||
"DeveloperCount": "Total desarrolladores asignados", |
|||
"QuestionCount": "Total de preguntas restantes", |
|||
"Unlimited": "Ilimitado", |
|||
"Owners": "Propietarios", |
|||
"AddMember": "Añadir miembro", |
|||
"AddOwner": "Añadir propietario", |
|||
"AddDeveloper": "Añadir desarrollador", |
|||
"UserName": "Nombre de usuario", |
|||
"Name": "Nombre", |
|||
"EmailAddress": "Dirección de Email", |
|||
"Developers": "Desarrolladores", |
|||
"LicenseType": "Tipo de licencia", |
|||
"Manage": "", |
|||
"StartDate": "", |
|||
"EndDate": "", |
|||
"Manage": "Gestionar", |
|||
"StartDate": "Fecha de inicio", |
|||
"EndDate": "Fecha de fin", |
|||
"Modules": "Módulos", |
|||
"LicenseExtendMessage": "", |
|||
"LicenseUpgradeMessage": "", |
|||
"LicenseAddDeveloperMessage": "", |
|||
"Volo.AbpIo.Commercial:010004": "", |
|||
"MyOrganizations": "", |
|||
"ApiKey": "", |
|||
"UserNameNotFound": "", |
|||
"SuccessfullyAddedToNewsletter": "", |
|||
"MyProfile": "", |
|||
"EmailNotValid": "" |
|||
"LicenseExtendMessage": "Tu fecha de finalización de tu licencia ha sido extendido a {0}", |
|||
"LicenseUpgradeMessage": "Tu licencia esta actualizada a {0}", |
|||
"LicenseAddDeveloperMessage": "{0} desarrolladores añadidos a tu licencia", |
|||
"Volo.AbpIo.Commercial:010004": "No se pudo encontrar el usuario especificado. El usuario debe estar ya registrado.", |
|||
"MyOrganizations": "Mis organizaciones", |
|||
"ApiKey": "API Key", |
|||
"UserNameNotFound": "No hay un usuario con el nombre de usuario {0}", |
|||
"SuccessfullyAddedToNewsletter": "Gracias por suscribirte a nuestro boletín de noticias!", |
|||
"MyProfile": "Mi perfil", |
|||
"EmailNotValid": "Por favor, introduce una dirección de email válida." |
|||
} |
|||
} |
|||
@ -1,90 +1,90 @@ |
|||
{ |
|||
"culture": "es", |
|||
"texts": { |
|||
"Permission:CommunityArticle": "", |
|||
"Permission:Edit": "", |
|||
"Waiting": "", |
|||
"Approved": "", |
|||
"Rejected": "", |
|||
"Wait": "", |
|||
"Approve": "", |
|||
"Reject": "", |
|||
"ReadArticle": "", |
|||
"Status": "", |
|||
"ContentSource": "", |
|||
"Details": "", |
|||
"Url": "", |
|||
"Title": "", |
|||
"CreationTime": "", |
|||
"Permission:CommunityArticle": "Artículo de comunidad", |
|||
"Permission:Edit": "Editar", |
|||
"Waiting": "Esperando", |
|||
"Approved": "Aprobado", |
|||
"Rejected": "Rechazado", |
|||
"Wait": "Esperar", |
|||
"Approve": "Aprobar", |
|||
"Reject": "Rechazar", |
|||
"ReadArticle": "Leer artículo", |
|||
"Status": "Estado", |
|||
"ContentSource": "Fuente de contenido", |
|||
"Details": "Detalles", |
|||
"Url": "Url", |
|||
"Title": "Título", |
|||
"CreationTime": "Fecha de creación", |
|||
"Save": "Guardar", |
|||
"SameUrlAlreadyExist": "", |
|||
"UrlIsNotValid": "", |
|||
"UrlNotFound": "", |
|||
"UrlContentNotFound": "", |
|||
"Summary": "", |
|||
"MostRead": "", |
|||
"Latest": "", |
|||
"ContributeAbpCommunity": "", |
|||
"SubmitYourArticle": "", |
|||
"ContributionGuide": "", |
|||
"BugReport": "", |
|||
"SeeAllArticles": "", |
|||
"WelcomeToABPCommunity!": "", |
|||
"MyProfile": "", |
|||
"MyOrganizations": "", |
|||
"EmailNotValid": "", |
|||
"FeatureRequest": "", |
|||
"CreateArticleTitleInfo": "", |
|||
"CreateArticleUrlInfo": "", |
|||
"CreateArticleSummaryInfo": "", |
|||
"CreateArticleCoverInfo": "", |
|||
"ThisExtensionIsNotAllowed": "", |
|||
"TheFileIsTooLarge": "", |
|||
"GoToTheArticle": "", |
|||
"Contribute": "", |
|||
"OverallProgress": "", |
|||
"Done": "", |
|||
"Open": "", |
|||
"Closed": "", |
|||
"LatestQuestionOnThe": "", |
|||
"Stackoverflow": "", |
|||
"Votes": "", |
|||
"Answer": "", |
|||
"Views": "", |
|||
"Answered": "", |
|||
"WaitingForYourAnswer": "", |
|||
"Asked": "", |
|||
"AllQuestions": "", |
|||
"NextVersion": "", |
|||
"MilestoneErrorMessage": "", |
|||
"QuestionItemErrorMessage": "", |
|||
"Oops": "", |
|||
"CreateArticleSuccessMessage": "", |
|||
"ChooseCoverImage": "", |
|||
"CoverImage": "", |
|||
"ShareYourExperiencesWithTheABPFramework": "", |
|||
"Optional": "", |
|||
"UpdateUserWebSiteInfo": "", |
|||
"UpdateUserTwitterInfo": "", |
|||
"UpdateUserGithubInfo": "", |
|||
"UpdateUserLinkedinInfo": "", |
|||
"UpdateUserCompanyInfo": "", |
|||
"UpdateUserJobTitleInfo": "", |
|||
"UserName": "", |
|||
"Company": "", |
|||
"PersonalWebsite": "", |
|||
"RegistrationDate": "", |
|||
"Social": "", |
|||
"Biography": "", |
|||
"HasNoPublishedArticlesYet": "", |
|||
"Author": "", |
|||
"LatestGithubAnnouncements": "", |
|||
"SeeAllAnnouncements": "", |
|||
"SameUrlAlreadyExist": "La url ya existe si tu quieres añadir este artículo, tu debes cambiar la url!", |
|||
"UrlIsNotValid": "Url no es valida", |
|||
"UrlNotFound": "Url no encontrada", |
|||
"UrlContentNotFound": "Contenido de la Url no encontrado", |
|||
"Summary": "Resumen", |
|||
"MostRead": "Más leído", |
|||
"Latest": "Últimos", |
|||
"ContributeAbpCommunity": "Contribuye a la comunidad ABP", |
|||
"SubmitYourArticle": "Envía tu artículo", |
|||
"ContributionGuide": "Guía de contribución", |
|||
"BugReport": "Informe de errores", |
|||
"SeeAllArticles": "Ver todos los artículos", |
|||
"WelcomeToABPCommunity!": "Bienvenido a la comunidad ABP", |
|||
"MyProfile": "Mi perfil", |
|||
"MyOrganizations": "Mis organizaciones", |
|||
"EmailNotValid": "Por favor entra una dirección de email válida.", |
|||
"FeatureRequest": "Solucitud de característica", |
|||
"CreateArticleTitleInfo": "Título del artículo para ser mostrado en la lista de artículos.", |
|||
"CreateArticleUrlInfo": "Url original del artículo GitHub/ Externo", |
|||
"CreateArticleSummaryInfo": "Un pequeño resumen del artículo para ser mostrado en la lista de artículos.", |
|||
"CreateArticleCoverInfo": "Para crear un artículo eficaz, agregue una foto de portada. Cargue imágenes con una relación de aspecto de 16: 9 para obtener la mejor vista.", |
|||
"ThisExtensionIsNotAllowed": "Esta extensión no está permitida.", |
|||
"TheFileIsTooLarge": "El fichero es demasiado grande.", |
|||
"GoToTheArticle": "Ir a el artículo", |
|||
"Contribute": "Contribuir", |
|||
"OverallProgress": "Progreso general", |
|||
"Done": "Hecho", |
|||
"Open": "Abrir", |
|||
"Closed": "Cerrado", |
|||
"LatestQuestionOnThe": "Última pregunta en la", |
|||
"Stackoverflow": "Stackoverflow", |
|||
"Votes": "Votos", |
|||
"Answer": "Respuesta", |
|||
"Views": "Vistas", |
|||
"Answered": "Respondido", |
|||
"WaitingForYourAnswer": "Esperando tu respuesta", |
|||
"Asked": "Preguntado", |
|||
"AllQuestions": "Todas las preguntas", |
|||
"NextVersion": "Siguiente versión", |
|||
"MilestoneErrorMessage": "No se pudieron obtener los detalles del hito actual en Github.", |
|||
"QuestionItemErrorMessage": "no se pudieron obtener los detalles de pregunta actual en Stackoverflow.", |
|||
"Oops": "Oops!", |
|||
"CreateArticleSuccessMessage": "El artículo se ha enviado correctamente. Se publicará después de una revisión del administrador del sitio.", |
|||
"ChooseCoverImage": "Elige una imagen de portada...", |
|||
"CoverImage": "Imagen de portada", |
|||
"ShareYourExperiencesWithTheABPFramework": "Comparte tus experiencias con el ABP Framework!", |
|||
"Optional": "Opcional", |
|||
"UpdateUserWebSiteInfo": "Ejemplo: https://johndoe.com", |
|||
"UpdateUserTwitterInfo": "Ejemplo: johndoe", |
|||
"UpdateUserGithubInfo": "Ejemplo: johndoe", |
|||
"UpdateUserLinkedinInfo": "Ejemplo: https://www.linkedin.com/...", |
|||
"UpdateUserCompanyInfo": "Ejemplo: Volosoft", |
|||
"UpdateUserJobTitleInfo": "Ejemplo: desarrollador de software", |
|||
"UserName": "Nombre de usuario", |
|||
"Company": "Empresa", |
|||
"PersonalWebsite": "Sitio web personal", |
|||
"RegistrationDate": "Fecha de registro", |
|||
"Social": "Social", |
|||
"Biography": "Biografía", |
|||
"HasNoPublishedArticlesYet": "No has publicado articules todavía", |
|||
"Author": "Autor", |
|||
"LatestGithubAnnouncements": "Últimas notificaciones de Github", |
|||
"SeeAllAnnouncements": "Ver todos las notificaciones", |
|||
"LatestBlogPost": "", |
|||
"Edit": "", |
|||
"ProfileImageChange": "", |
|||
"BlogItemErrorMessage": "", |
|||
"PlannedReleaseDate": "", |
|||
"CommunityArticleRequestErrorMessage": "" |
|||
"Edit": "Editar", |
|||
"ProfileImageChange": "Cambiar la imagen de perfil", |
|||
"BlogItemErrorMessage": "No se pudo obtener los detalles del último blog desde ABP.", |
|||
"PlannedReleaseDate": "Fecha de entrega planificada", |
|||
"CommunityArticleRequestErrorMessage": "No se pudo obtener la última petición de artículo desde Github" |
|||
} |
|||
} |
|||
@ -1,189 +1,189 @@ |
|||
{ |
|||
"culture": "es", |
|||
"texts": { |
|||
"GetStarted": "", |
|||
"Create": "", |
|||
"NewProject": "", |
|||
"DirectDownload": "", |
|||
"ProjectName": "", |
|||
"ProjectType": "", |
|||
"DatabaseProvider": "", |
|||
"NTier": "", |
|||
"IncludeUserInterface": "", |
|||
"CreateNow": "", |
|||
"TheStartupProject": "", |
|||
"Tutorial": "", |
|||
"UsingCLI": "", |
|||
"SeeDetails": "", |
|||
"AbpShortDescription": "", |
|||
"SourceCodeUpper": "", |
|||
"LatestReleaseLogs": "", |
|||
"Infrastructure": "", |
|||
"Architecture": "", |
|||
"Modular": "", |
|||
"DontRepeatYourself": "", |
|||
"DeveloperFocused": "", |
|||
"FullStackApplicationInfrastructure": "", |
|||
"GetStarted": "Comencemos - Plantillas de inicio", |
|||
"Create": "Crear", |
|||
"NewProject": "Nuevo proyecto", |
|||
"DirectDownload": "Descarga directa", |
|||
"ProjectName": "Nombre de proyecto", |
|||
"ProjectType": "Tipo de proyecto", |
|||
"DatabaseProvider": "Proveedor de base de datos", |
|||
"NTier": "N-Capas", |
|||
"IncludeUserInterface": "Incluir interface de usuario", |
|||
"CreateNow": "Crear ahora", |
|||
"TheStartupProject": "El proyecto de inicio", |
|||
"Tutorial": "Tutorial", |
|||
"UsingCLI": "Usando CLI", |
|||
"SeeDetails": "Ver detalles", |
|||
"AbpShortDescription": "ABP Framework es una completa infraestructure para crear modernas aplicaciones web que sigue las mejores prácticas y convenciones de desarrollo de software.", |
|||
"SourceCodeUpper": "CÓDIGO FUENTE", |
|||
"LatestReleaseLogs": "Últimos registros de lanzamiento", |
|||
"Infrastructure": "infraestructura", |
|||
"Architecture": "Arquitectura", |
|||
"Modular": "Modular", |
|||
"DontRepeatYourself": "No te repitas tu mismo", |
|||
"DeveloperFocused": "Pensado para el desarrollador", |
|||
"FullStackApplicationInfrastructure": "infraestructura de aplicación Full stack", |
|||
"DomainDrivenDesign": "", |
|||
"DomainDrivenDesignExplanation": "", |
|||
"Authorization": "", |
|||
"AuthorizationExplanation": "", |
|||
"MultiTenancy": "", |
|||
"MultiTenancyExplanationShort": "", |
|||
"CrossCuttingConcerns": "", |
|||
"CrossCuttingConcernsExplanationShort": "", |
|||
"BuiltInBundlingMinification": "", |
|||
"BuiltInBundlingMinificationExplanation": "", |
|||
"VirtualFileSystem": "", |
|||
"VirtualFileSystemExplanation": "", |
|||
"Theming": "", |
|||
"ThemingExplanationShort": "", |
|||
"BootstrapTagHelpersDynamicForms": "", |
|||
"DomainDrivenDesignExplanation": "Diseñado y desarrollado basandose en los patrones y principios de DDD. Promorciona una capa de modelo para tus aplicaciones.", |
|||
"Authorization": "Autorización", |
|||
"AuthorizationExplanation": "Sistema de autorización avanzado con usuarios, roles y permisos de granularidad-fina. Construido sobre la librería Microsoft Identity.", |
|||
"MultiTenancy": "Multi-inquilino", |
|||
"MultiTenancyExplanationShort": "Aplicaciones SaaS sencillas!, integradas con multi-inquilino desde la base de datos hasta la UI.", |
|||
"CrossCuttingConcerns": "Requerimientos comunes", |
|||
"CrossCuttingConcernsExplanationShort": "Completa infraestructura de autorización, validación, manejo de excepciones, caching, auditoría, gestión de transacciones y más.", |
|||
"BuiltInBundlingMinification": "Empaquetado y minificado integrado", |
|||
"BuiltInBundlingMinificationExplanation": "No se necesitan usar herramientas externas para el empaquetado y minificado. ABP ofrece un camino simple, dinámico, potente, modular e integrado.", |
|||
"VirtualFileSystem": "Sistema de ficheros virtual", |
|||
"VirtualFileSystemExplanation": "Incrusta vistas, scripts, estilos, imágenes en paquetes/ librerias y reusalos en diferentes aplicaciones.", |
|||
"Theming": "Temas", |
|||
"ThemingExplanationShort": "Usa y personaliza la UI estandar basada en bootstrap o crea tu propio tema.", |
|||
"BootstrapTagHelpersDynamicForms": "Boostrap Tag Helpers y formularios dinámicos", |
|||
"BootstrapTagHelpersDynamicFormsExplanation": "", |
|||
"HTTPAPIsDynamicProxies": "", |
|||
"HTTPAPIsDynamicProxiesExplanation": "", |
|||
"CompleteArchitectureInfo": "", |
|||
"DomainDrivenDesignBasedLayeringModelExplanation": "", |
|||
"DomainDrivenDesignBasedLayeringModelExplanationCont": "", |
|||
"MicroserviceCompatibleModelExplanation": "", |
|||
"MicroserviceCompatibleModelExplanationCont": "", |
|||
"ModularInfo": "", |
|||
"PreBuiltModulesThemes": "", |
|||
"PreBuiltModulesThemesExplanation": "", |
|||
"NuGetNPMPackages": "", |
|||
"NuGetNPMPackagesExplanation": "", |
|||
"ExtensibleReplaceable": "", |
|||
"ExtensibleReplaceableExplanation": "", |
|||
"CrossCuttingConcernsExplanation2": "", |
|||
"CrossCuttingConcernsExplanation3": "", |
|||
"AuthenticationAuthorization": "", |
|||
"ExceptionHandling": "", |
|||
"Validation": "", |
|||
"DatabaseConnection": "", |
|||
"TransactionManagement": "", |
|||
"AuditLogging": "", |
|||
"Caching": "", |
|||
"Multitenancy": "", |
|||
"DataFiltering": "", |
|||
"ConventionOverConfiguration": "", |
|||
"ConventionOverConfigurationExplanation": "", |
|||
"ConventionOverConfigurationExplanationList1": "", |
|||
"ConventionOverConfigurationExplanationList2": "", |
|||
"ConventionOverConfigurationExplanationList3": "", |
|||
"ConventionOverConfigurationExplanationList4": "", |
|||
"ConventionOverConfigurationExplanationList5": "", |
|||
"ConventionOverConfigurationExplanationList6": "", |
|||
"BaseClasses": "", |
|||
"BaseClassesExplanation": "", |
|||
"DeveloperFocusedExplanation": "", |
|||
"DeveloperFocusedExplanationCont": "", |
|||
"SeeAllFeatures": "", |
|||
"CLI_CommandLineInterface": "", |
|||
"CLI_CommandLineInterfaceExplanation": "", |
|||
"StartupTemplates": "", |
|||
"StartupTemplatesExplanation": "", |
|||
"BasedOnFamiliarTools": "", |
|||
"BasedOnFamiliarToolsExplanation": "", |
|||
"ORMIndependent": "", |
|||
"ORMIndependentExplanation": "", |
|||
"Features": "", |
|||
"ABPCLI": "", |
|||
"Modularity": "", |
|||
"BootstrapTagHelpers": "", |
|||
"DynamicForms": "", |
|||
"BundlingMinification": "", |
|||
"HTTPAPIsDynamicProxies": "HTTP APIs y proxies dinámicos", |
|||
"HTTPAPIsDynamicProxiesExplanation": "Expon servicios de aplicación como REST HTPP APIs y consumelos dinámicamente desde proxies JavaScript y C#.", |
|||
"CompleteArchitectureInfo": "Arquitectura moderna para crear soluciones software sostenibles.", |
|||
"DomainDrivenDesignBasedLayeringModelExplanation": "Ayuda a implementar ", |
|||
"DomainDrivenDesignBasedLayeringModelExplanationCont": "Promorciona plantillas de inicio, clases base, servicios, documentación y guías de ayuda para desarrollar tu aplicación basada en patrones y principios DDD.", |
|||
"MicroserviceCompatibleModelExplanation": "El núcleo del framework y los modules pre-construidos están diseñados con una arquitectura microservicios en mente.", |
|||
"MicroserviceCompatibleModelExplanationCont": "Proporciona la infraestructura, integraciones, ejemplos y documentación para implementar soluciones microservicios más facilmente, mientras no trae complejidad adicional si quieres una aplicación monolítica.", |
|||
"ModularInfo": "ABP proporciona un sistema de modulos que te permite desarrollar modulos de aplicacion resusables. Conectado en el ciclo de eventos de aplicación y mínimas dependencias entre las partes principales de tu sistema.", |
|||
"PreBuiltModulesThemes": "Modulos y temas integrados", |
|||
"PreBuiltModulesThemesExplanation": "Módulos y temas de código abierto y comerciales preparados para usar en tu aplicación de negocio.", |
|||
"NuGetNPMPackages": "Paquetes Nuget y NPM", |
|||
"NuGetNPMPackagesExplanation": "Paquetes distribuidos a través de Nuget y NPM para instalar y actualizar.", |
|||
"ExtensibleReplaceable": "Extensible/ Reemplazable", |
|||
"ExtensibleReplaceableExplanation": "Todos los serivios y módulos están diseñados para ser extensibles. Tu puedes reemplazar servicios, páginas, estilos y componentes.", |
|||
"CrossCuttingConcernsExplanation2": "Mantiene tu código base más pequeño y te permite enforcate en el código que es especifico en tu negocio.", |
|||
"CrossCuttingConcernsExplanation3": "No pierdas tiempo implementando requerimientos comunes de aplicación en múltiples proyectos.", |
|||
"AuthenticationAuthorization": "Autenticación y autorización", |
|||
"ExceptionHandling": "Manejo de excepciones.", |
|||
"Validation": "Validación", |
|||
"DatabaseConnection": "Conexión a la base de datos", |
|||
"TransactionManagement": "Gestión de transacciones", |
|||
"AuditLogging": "Registro de auditoría", |
|||
"Caching": "Caché", |
|||
"Multitenancy": "Multi-inquilino", |
|||
"DataFiltering": "Filtrado de datos", |
|||
"ConventionOverConfiguration": "Convención sobre la configuración", |
|||
"ConventionOverConfigurationExplanation": "ABP implementa comunes convenciones de aplicación por defecto con una mínima o nula configuración.", |
|||
"ConventionOverConfigurationExplanationList1": "Los registradores conocen los servicios para la inyección de dependencias.", |
|||
"ConventionOverConfigurationExplanationList2": "Expone servicios de aplicación como APIs HTTP mediante convenciones de nomenclatura.", |
|||
"ConventionOverConfigurationExplanationList3": "Crea clientes proxies HTTP dinámicos para C# y JavaScript.", |
|||
"ConventionOverConfigurationExplanationList4": "Proporciona repositorios por defecto para tus entidades.", |
|||
"ConventionOverConfigurationExplanationList5": "Maneja Unit of Work por cada petición web o metodo de servicio de aplicación.", |
|||
"ConventionOverConfigurationExplanationList6": "Publica, crea, actualiza y borra eventos para tus entidades.", |
|||
"BaseClasses": "Clase base", |
|||
"BaseClassesExplanation": "Clases base integradas para patrones comunes de aplicación.", |
|||
"DeveloperFocusedExplanation": "ABP es para desarrolladores.", |
|||
"DeveloperFocusedExplanationCont": "Su objetivo es simplificar su desarrollo diario de software sin restringirle la escritura de código de bajo nivel.", |
|||
"SeeAllFeatures": "Ver todas las características", |
|||
"CLI_CommandLineInterface": "CLI (Command Line Interface)", |
|||
"CLI_CommandLineInterfaceExplanation": "Incluye un CLI que te ayuda a automatizar la creación de nuevos proyectos y añadir nuevos módulos.", |
|||
"StartupTemplates": "Plantillas de inicio", |
|||
"StartupTemplatesExplanation": "Varias plantillas de inicio proporcionan una solución completamente configurada lista para comenzar el desarrollo.", |
|||
"BasedOnFamiliarTools": "Basado en herramientas familiares", |
|||
"BasedOnFamiliarToolsExplanation": "Construido y diseñado con herramientas populares que tu ya conoces. Baja curva de aprendizaje, facil adaptación y desarrollo confortable.", |
|||
"ORMIndependent": "Independiente del ORM", |
|||
"ORMIndependentExplanation": "El núcleo del core es independiente del ORM/ base de datos y puedes trabajar con cualquier fuente de datos. Entity Framework Core y MongoDB están actualmente disponibles.", |
|||
"Features": "Explora las características de ABP Framework", |
|||
"ABPCLI": "ABP CLI", |
|||
"Modularity": "Modularidad", |
|||
"BootstrapTagHelpers": "Bootstrap Tag Helpers", |
|||
"DynamicForms": "Formularios dinámicos", |
|||
"BundlingMinification": "Empaquetado y Minimificación", |
|||
"BackgroundJobs": "", |
|||
"BackgroundJobsExplanation": "", |
|||
"DDDInfrastructure": "", |
|||
"DomainDrivenDesignInfrastructure": "", |
|||
"AutoRESTAPIs": "", |
|||
"DynamicClientProxies": "", |
|||
"DistributedEventBus": "", |
|||
"DistributedEventBusWithRabbitMQIntegration": "", |
|||
"TestInfrastructure": "", |
|||
"AuditLoggingEntityHistories": "", |
|||
"ObjectToObjectMapping": "", |
|||
"ObjectToObjectMappingExplanation": "", |
|||
"EmailSMSAbstractions": "", |
|||
"EmailSMSAbstractionsWithTemplatingSupport": "", |
|||
"Localization": "", |
|||
"SettingManagement": "", |
|||
"ExtensionMethods": "", |
|||
"ExtensionMethodsHelpers": "", |
|||
"AspectOrientedProgramming": "", |
|||
"DependencyInjection": "", |
|||
"DependencyInjectionByConventions": "", |
|||
"ABPCLIExplanation": "", |
|||
"ModularityExplanation": "", |
|||
"MultiTenancyExplanation": "", |
|||
"MultiTenancyExplanation2": "", |
|||
"MultiTenancyExplanation3": "", |
|||
"MultiTenancyExplanation4": "", |
|||
"BootstrapTagHelpersExplanation": "", |
|||
"DynamicFormsExplanation": "", |
|||
"AuthenticationAuthorizationExplanation": "", |
|||
"CrossCuttingConcernsExplanation": "", |
|||
"DatabaseConnectionTransactionManagement": "", |
|||
"CorrelationIdTracking": "", |
|||
"BundlingMinificationExplanation": "", |
|||
"VirtualFileSystemnExplanation": "", |
|||
"ThemingExplanation": "", |
|||
"DomainDrivenDesignInfrastructureExplanation": "", |
|||
"Specification": "", |
|||
"Repository": "", |
|||
"DomainService": "", |
|||
"ValueObject": "", |
|||
"ApplicationService": "", |
|||
"DataTransferObject": "", |
|||
"AggregateRootEntity": "", |
|||
"AutoRESTAPIsExplanation": "", |
|||
"DynamicClientProxiesExplanation": "", |
|||
"DistributedEventBusWithRabbitMQIntegrationExplanation": "", |
|||
"TestInfrastructureExplanation": "", |
|||
"AuditLoggingEntityHistoriesExplanation": "", |
|||
"EmailSMSAbstractionsWithTemplatingSupportExplanation": "", |
|||
"LocalizationExplanation": "", |
|||
"SettingManagementExplanation": "", |
|||
"ExtensionMethodsHelpersExplanation": "", |
|||
"AspectOrientedProgrammingExplanation": "", |
|||
"DependencyInjectionByConventionsExplanation": "", |
|||
"DataFilteringExplanation": "", |
|||
"PublishEvents": "", |
|||
"HandleEvents": "", |
|||
"AndMore": "", |
|||
"Code": "", |
|||
"Result": "", |
|||
"SeeTheDocumentForMoreInformation": "", |
|||
"IndexPageHeroSection": "", |
|||
"UiFramework": "", |
|||
"EmailAddress": "", |
|||
"Mobile": "", |
|||
"ReactNative": "", |
|||
"Strong": "", |
|||
"Complete": "", |
|||
"BasedLayeringModel": "", |
|||
"Microservice": "", |
|||
"Compatible": "", |
|||
"MeeTTheABPCommunityInfo": "", |
|||
"JoinTheABPCommunityInfo": "", |
|||
"AllArticles": "", |
|||
"SubmitYourArticle": "", |
|||
"DynamicClientProxyDocument": "", |
|||
"EmailSMSAbstractionsDocument": "", |
|||
"CreateProjectWizard": "", |
|||
"TieredOption": "", |
|||
"SeparateIdentityServerOption": "", |
|||
"UseslatestPreVersion": "", |
|||
"ReadTheDocumentation": "", |
|||
"Documentation": "", |
|||
"GettingStartedTutorial": "", |
|||
"ApplicationDevelopmentTutorial": "", |
|||
"TheStartupTemplate": "", |
|||
"InstallABPCLIInfo": "", |
|||
"DifferentLevelOfNamespaces": "", |
|||
"ABPCLIExamplesInfo": "", |
|||
"SeeCliDocumentForMoreInformation": "", |
|||
"Optional": "", |
|||
"LocalFrameworkRef": "" |
|||
"BackgroundJobsExplanation": "Defina clases simples para ejecutar trabajos en segundo plano como en cola. Utilice el administrador de trabajos integrado o integre el suyo propio. <a href=\"{0}\">Hangfire</a> & <a href=\"{1}\">RabbitMQ</a> integraciones están actualmente disponibles.", |
|||
"DDDInfrastructure": "infraestructura DSS", |
|||
"DomainDrivenDesignInfrastructure": "infraestructura Domain Driven Design", |
|||
"AutoRESTAPIs": "Auto REST APIs", |
|||
"DynamicClientProxies": "Clientes proxies dinámicos", |
|||
"DistributedEventBus": "Bus de eventos distribuido", |
|||
"DistributedEventBusWithRabbitMQIntegration": "Bus de eventos distribuido con la integración RabbitMQ ", |
|||
"TestInfrastructure": "infraestructura de Test", |
|||
"AuditLoggingEntityHistories": "Registro de auditoría y historial de entidades", |
|||
"ObjectToObjectMapping": "Mapeado objeto a objeto", |
|||
"ObjectToObjectMappingExplanation": "<a href=\"{0}\">Mapeado objeto a objeto</a> abstracción integrada con AutoMapper.", |
|||
"EmailSMSAbstractions": "Abstracciones para Email y SMS", |
|||
"EmailSMSAbstractionsWithTemplatingSupport": "Abstracciones para Email y SMS con soporte para plantillas", |
|||
"Localization": "Localización", |
|||
"SettingManagement": "Gestión de la configuración", |
|||
"ExtensionMethods": "Extension Methods", |
|||
"ExtensionMethodsHelpers": "Extension Methods & Helpers", |
|||
"AspectOrientedProgramming": "Programación orientada a aspectos", |
|||
"DependencyInjection": "Inyección de dependencias", |
|||
"DependencyInjectionByConventions": "Inyección de dependencias por convenciones", |
|||
"ABPCLIExplanation": "ABP CLI (Command Line Interface) es una herramienta en línea de comandos que proporciona ciertas tareas comunes para soluciones basadas en ABP.", |
|||
"ModularityExplanation": "ABP proporciona una infraestructura completa para construir tu propios módulos de aplicación que pueden contener entidades, servicios, base de datos integradas, APIs, componentes de UI y otros...", |
|||
"MultiTenancyExplanation": "ABP framework no sólo soporta el desarrollo de aplicaciones multi-inquilino, si no también hace que tu código no tenga que preocuparse de multi-inquilino.", |
|||
"MultiTenancyExplanation2": "Puedes determinar automaticamente el inquilino actual, y aislar los datos de diferentes inquilinos entre sí. ", |
|||
"MultiTenancyExplanation3": "Soporta base de datos única, base de datos por inquilino y enfoques híbridos.", |
|||
"MultiTenancyExplanation4": "Enfocate en tu código y deja al framework que maneje multi-inquilino en tu nombre.", |
|||
"BootstrapTagHelpersExplanation": "En vez de escribir repetidamente los detalle de componentes bootstrap, usa ABP's tag helpers para simplificarlo y obtener ventaja de intellisence. Tu puedes definitivamente usar Bootstrp cuando tu lo necesites.", |
|||
"DynamicFormsExplanation": "Los formularios dinámicos y tag helpers de entrada pueden crear el formulario completo a partir de una clase de C# como modelo. ", |
|||
"AuthenticationAuthorizationExplanation": "Autenticación enriquecidad y opciones de autorización integradas con ASP.NET Core Identity & IdentityServer4. Proporciona un extensible y detallado sistema de permisos.", |
|||
"CrossCuttingConcernsExplanation": "No te repitas tu mismo al implementar todas esas cosas comunes una y otra vez. Enfocate en tu negocio y permite a ABP automatizarlos por convención.", |
|||
"DatabaseConnectionTransactionManagement": "Conexión de base de datos y gestión de transacciones", |
|||
"CorrelationIdTracking": "Correlation-Id de seguimiento", |
|||
"BundlingMinificationExplanation": "ABP ofrece un sistema simple, dinámico, potente, modular y empaquetado & minificación ", |
|||
"VirtualFileSystemnExplanation": "El sistema de archivos virtuales permite administrar archivos que no existen físicamente en el sistema de archivos (disco). Se utiliza principalmente para incrustar (js, css, image, cshtml...) en ensamblados y utilizarlos como archivos físicos en tiempo de ejecución.", |
|||
"ThemingExplanation": "El sistema de temas permite desarrollar el tema de su aplicación y módulos independiente mediante la definición de un conjunto de bibliotecas y diseños base comunes, basado en el último marco de Trabajo bootstrap.", |
|||
"DomainDrivenDesignInfrastructureExplanation": "Una infraestructura completa para crear aplicaciones en capas basadas en los patrones y principios DDD", |
|||
"Specification": "Especificación", |
|||
"Repository": "Repositorios", |
|||
"DomainService": "Servicio de dominio", |
|||
"ValueObject": "Objetos de valor", |
|||
"ApplicationService": "Servicio de aplicación", |
|||
"DataTransferObject": "Objetos de transferencia de datos", |
|||
"AggregateRootEntity": "Agregado ráiz, Entidad", |
|||
"AutoRESTAPIsExplanation": "ABP puede configurar automáticamente los servicios de aplicación como controladores de API por convención.", |
|||
"DynamicClientProxiesExplanation": "Consuma fácilmente sus API de clientes de JavaScript y C#", |
|||
"DistributedEventBusWithRabbitMQIntegrationExplanation": "Publique y consuma eventos distribuidos fácilmente mediante el bus de eventos distribuido integrado con la integración RabbitMQ disponible.", |
|||
"TestInfrastructureExplanation": "El framework se ha desarrollado unitariamente y pruebas de integración. Proporciona clases base para que sea más fácil. Las plantillas de inicio vienen preconfiguradas para las pruebas.", |
|||
"AuditLoggingEntityHistoriesExplanation": "Registro de auditoría integrado para aplicaciones críticas para el negocio. Registro de auditorías de solicitud, servicio, nivel de método e historiales de entidades con detalles de nivel de propiedad.", |
|||
"EmailSMSAbstractionsWithTemplatingSupportExplanation": "Las abstracciones IEmailSender e ISmsSender desacoplan la lógica de la aplicación de la infraestructura. El sistema avanzado de plantillas de correo electrónico permite crear y localizar plantillas de correo electrónico y utilizarlas fácilmente cuando sea necesario.", |
|||
"LocalizationExplanation": "El sistema de localización permite crear recursos en archivos JSON sin formato y utilizarlos para localizar la interfaz de usuario. Admite escenarios avanzados como herencia, extensiones e integración de JavaScript, mientras que es totalmente compatible con el sistema de localización de AspNet Core.", |
|||
"SettingManagementExplanation": "Defina la configuración de la aplicación y obtenga valores en tiempo de ejecución en función de la configuración actual, el inquilino y el usuario.", |
|||
"ExtensionMethodsHelpersExplanation": "No te repitas ni siquiera para partes de código triviales. Extensiones y ayudantes para tipos estándar hace que su código sea mucho más limpio y fácil de escribir.", |
|||
"AspectOrientedProgrammingExplanation": "Proporciona una infraestructura cómoda para crear servidores proxy dinámicos e implementar la programación orientada a aspectos. Interceptar cualquier clase y ejecutar el código antes y después de cada ejecución del método.", |
|||
"DependencyInjectionByConventionsExplanation": "No es necesario registrar las clases en la inserción de dependencias manualmente. Registra automáticamente los tipos de servicio comunes por convención. Para otro tipo de servicios, puede utilizar interfaces y atributos para que sea más fácil y in situ.", |
|||
"DataFilteringExplanation": "Definir y usar filtros de datos que se aplican automáticamente al consultar entidades desde la base de datos. Los filtros Soft Delete & MultiTenant se proporcionan de fábrica cuando se implementan interfaces sencillas.", |
|||
"PublishEvents": "Publicar eventos", |
|||
"HandleEvents": "Manejar eventos", |
|||
"AndMore": "y más...", |
|||
"Code": "Código", |
|||
"Result": "Resultado", |
|||
"SeeTheDocumentForMoreInformation": "Ver el <a href=\"{1}\">{0} documento</a> para más información.", |
|||
"IndexPageHeroSection": "<span class=\"first-line shine\"><strong>Código fuente abierto</strong></span><span class=\"second-line text-uppercase\">Web Application<br />Framework </span><span class=\"third-line shine2\"><strong>para asp.net core</strong></span>", |
|||
"UiFramework": "UI Framework", |
|||
"EmailAddress": "Dirección de correo", |
|||
"Mobile": "Móvil", |
|||
"ReactNative": "React Nativo", |
|||
"Strong": "Fuerte", |
|||
"Complete": "Completar", |
|||
"BasedLayeringModel": "Módelo basado en capas", |
|||
"Microservice": "Microservicio", |
|||
"Compatible": "Compatible", |
|||
"MeeTTheABPCommunityInfo": "Nuestra misión es crear un entorno en el que los desarrolladores puedan ayudarse entre sí con artículos, tutoriales, estudios de casos, etc. y conocer personas de ideas afines.", |
|||
"JoinTheABPCommunityInfo": "¡Participe en una comunidad vibrante y conviértase en colaborador del Marco ABP!", |
|||
"AllArticles": "Todos los artículos", |
|||
"SubmitYourArticle": "Envía tu artículo", |
|||
"DynamicClientProxyDocument": "Ver la documentación del cliente proxy dinámico para <a href=\"{0}\">JavaScript</a> & <a href=\"{1}\">C#</a>.", |
|||
"EmailSMSAbstractionsDocument": "Ver los documentos de <a href=\"{0}\">emailing</a> y <a href=\"{1}\">envío SMS </a> para más información.", |
|||
"CreateProjectWizard": "Este asistente crea un nuevo proyecto a partir de la plantilla de inicio que está correctamente configurado para comenzar con su proyecto.", |
|||
"TieredOption": "Crea una solución por niveles en la que las capas de API Web y HTTP están separadas físicamente. Si no se marca, crea una solución en capas que es menos compleja y adecuada para la mayoría de los escenarios.", |
|||
"SeparateIdentityServerOption": "Separa el lado del servidor en dos aplicaciones: la primera es para el servidor de identidad y la segunda es para la API HTTP del lado del servidor.", |
|||
"UseslatestPreVersion": "Usar la última versión pre-release", |
|||
"ReadTheDocumentation": "<span class=\"text-primary\">Leer</span><span class=\"text-success\">La Documentación</span>", |
|||
"Documentation": "Documentación", |
|||
"GettingStartedTutorial": "Tutorial para iniciarse", |
|||
"ApplicationDevelopmentTutorial": "Tutorial de desarrollo de aplicación", |
|||
"TheStartupTemplate": "La Plantilla de Inicio", |
|||
"InstallABPCLIInfo": "ABP CLI es la forma más rápida de iniciar una nueva solución con el marco ABP. Instale la CLI de ABP mediante una ventana de línea de comandos:", |
|||
"DifferentLevelOfNamespaces": "Tu puedes usar diferentes niveles de espacio de nombres; ej. BookStore, Acme.BookStore or Acme.Retail.BookStore.", |
|||
"ABPCLIExamplesInfo": "<strong>nuevo</strong> comando crea una <strong>aplicación MVC por capas</strong> con <strong>Entity Framework Core</strong> como proveedor de base de datos. Sin embargo, tiene distintas opciones. Ejemplos:", |
|||
"SeeCliDocumentForMoreInformation": "Ver el <a href=\"{0}\">documento ABP CLI </a> para más opciones o selecciona la \"Direct Download\" pestaña de arriba.", |
|||
"Optional": "Opcional", |
|||
"LocalFrameworkRef": "Mantén la referencia al proyecto local para los paquetes del framework." |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
# ABP.IO Platform 4.0 with .NET 5.0 in the 4th Year! |
|||
|
|||
Today, we are extremely happy to release ABP Framework 4.0 with **.NET 5.0 support**! |
|||
|
|||
## 4 Years of Work |
|||
|
|||
As a nice coincidence, today is the **4th year** since the first commit made in the [abp repository](https://github.com/abpframework/abp)! So, we can say "*Happy Birthday ABP Framework!*". |
|||
|
|||
 |
|||
|
|||
### Some Statistics |
|||
|
|||
ABP.IO Platform and the ABP Community is growing. Here, a summary of these 4 years. |
|||
|
|||
From GitHub, only from the main [abp repository](https://github.com/abpframework/abp); |
|||
|
|||
* **15,297 commits** done. |
|||
* **3,764 issues** are closed. |
|||
* **2,133 pull requests** are merged. |
|||
* **158 contributors**. |
|||
* **88 releases** published. |
|||
* **5.2K stars** on GitHub. |
|||
|
|||
From NuGet & NPM; |
|||
|
|||
* **220 NuGet** packages & **52 NPM** packages. |
|||
* **1,000,000 downloads** only for the core NuGet package. |
|||
|
|||
From Website; |
|||
|
|||
* **200,000 visitors**. |
|||
* **1,000,000+ sessions**. |
|||
|
|||
## What's New With 4.0? |
|||
|
|||
Since all the new features are already explained in details with the [4.0 RC Announcement Post](https://blog.abp.io/abp/ABP.IO-Platform-v4.0-RC-Has-Been-Released-based-on-.NET-5.0), I will not repeat all the details again. Please read [the RC post](https://blog.abp.io/abp/ABP.IO-Platform-v4.0-RC-Has-Been-Released-based-on-.NET-5.0) for **new feature and changes** you may need to do for your solution while upgrading to the version 4.0. |
|||
|
|||
Here, a brief list of major features and changes; |
|||
|
|||
* Migrated to **.NET 5.0**. |
|||
* Stable **Blazor** UI. |
|||
* Moved to **System.Text.Json**. |
|||
* Upgraded to **IdentityServer** version 4.0. |
|||
* **WPF** startup template. |
|||
|
|||
## Creating New Solutions |
|||
|
|||
You can create a new solution with the ABP Framework version 4.0 by either using the `abp new` command or using the **direct download** tab on the [get started page](https://abp.io/get-started). |
|||
|
|||
> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for details. |
|||
|
|||
## How to Upgrade an Existing Solution |
|||
|
|||
This is a **major version** and requires some **manual work**, especially related to **.NET 5.0** and **IdentityServer** 4.0 upgrades. |
|||
|
|||
* See the [MIGRATION GUIDE](https://docs.abp.io/en/abp/latest/Migration-Guides/Abp-4_0) that covers all the details about the upgrade progress. |
|||
|
|||
* You can also see the [upgrading document](https://docs.abp.io/en/abp/latest/Upgrading). |
|||
|
|||
## New Guides / Documents |
|||
|
|||
We are constantly improving the documentation. Our purpose is not only document the ABP Framework, but also write architectural and practical guides for developers. |
|||
|
|||
### Implementing Domain Driven Design |
|||
|
|||
[Implementing Domain Driven Design](https://docs.abp.io/en/abp/latest/Domain-Driven-Design-Implementation-Guide) is a practical guide for they want to implement the DDD principles in their solutions. While the implementation details rely on the ABP Framework infrastructure, core concepts, principles and patterns are applicable in any kind of solution, even if it is not a .NET solution. |
|||
|
|||
 |
|||
|
|||
### Testing |
|||
|
|||
The new [Testing document](https://docs.abp.io/en/abp/latest/Testing) discusses different kind of automated tests and explains how you can write tests for your ABP based solutions. |
|||
|
|||
### UI Documents |
|||
|
|||
We've created a lot of documents for the [MVC](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Overall), [Blazor](https://docs.abp.io/en/abp/latest/UI/Blazor/Overall) and the [Angular](https://docs.abp.io/en/abp/latest/UI/Angular/Quick-Start) UI. |
|||
|
|||
## About the Next Version |
|||
|
|||
The next versions 4.1 will mostly focus on; |
|||
|
|||
* Improving current features. |
|||
* Complete module features for the Blazor UI. |
|||
* Improve developer experience and productivity. |
|||
* More documentation and examples. |
|||
|
|||
Planned preview date for the version **4.1 is December 17, 2020**. See the [Road Map](https://docs.abp.io/en/abp/latest/Road-Map) document and [GitHub Milestones](https://github.com/abpframework/abp/milestones) to learn what's planned for the next versions. We are trying to be clear about the coming features and the next release dates. |
|||
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 149 KiB |
@ -0,0 +1,202 @@ |
|||
# ABP Framework 4.1 RC Has Been Published |
|||
|
|||
Today, we have released the [ABP Framework](https://abp.io/) (and the [ABP Commercial](https://commercial.abp.io/)) 4.1.0 RC. This blog post introduces the new features and important changes in this new version. |
|||
|
|||
> **The planned release date for the [4.1.0 final](https://github.com/abpframework/abp/milestone/47) version is January 4, 2021**. |
|||
|
|||
## Get Started with the 4.1 RC |
|||
|
|||
If you want to try the version `4.1.0` today, follow the steps below; |
|||
|
|||
1) **Upgrade** the ABP CLI to the version `4.1.0-rc.1` using a command line terminal: |
|||
|
|||
````bash |
|||
dotnet tool update Volo.Abp.Cli -g --version 4.1.0-rc.1 |
|||
```` |
|||
|
|||
**or install** if you haven't installed before: |
|||
|
|||
````bash |
|||
dotnet tool install Volo.Abp.Cli -g --version 4.1.0-rc.1 |
|||
```` |
|||
|
|||
2) Create a **new application** with the `--preview` option: |
|||
|
|||
````bash |
|||
abp new BookStore --preview |
|||
```` |
|||
|
|||
See the [ABP CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all the available options. |
|||
|
|||
> You can also use the *Direct Download* tab on the [Get Started](https://abp.io/get-started) page by selecting the **Preview checkbox**. |
|||
|
|||
## Breaking Changes |
|||
|
|||
This version has a minor breaking change if you'd injected a repository by class. This is not a problem for 99% of the applications. However, see [#6677](https://github.com/abpframework/abp/issues/6677) for the solution if that's a breaking change for you. |
|||
|
|||
## What's new with the ABP Framework 4.1 |
|||
|
|||
### Module Entity Extensions |
|||
|
|||
Module Entity Extension system provides a simple way of adding new properties to an existing entity defined by a module that is used by your application. This feature is now available also for the open source modules (identity and tenant-management). [The documentation](https://docs.abp.io/en/abp/latest/Module-Entity-Extensions) has been moved into the ABP Framework's documentation. |
|||
|
|||
**Example: Add "SocialSecurityNumber" property to the `IdentityUser` entity** |
|||
|
|||
````csharp |
|||
ObjectExtensionManager.Instance.Modules() |
|||
.ConfigureIdentity(identity => |
|||
{ |
|||
identity.ConfigureUser(user => |
|||
{ |
|||
user.AddOrUpdateProperty<string>( //property type: string |
|||
"SocialSecurityNumber", //property name |
|||
property => |
|||
{ |
|||
//validation rules |
|||
property.Attributes.Add(new RequiredAttribute()); |
|||
property.Attributes.Add( |
|||
new StringLengthAttribute(64) { |
|||
MinimumLength = 4 |
|||
} |
|||
); |
|||
|
|||
//...other configurations for this property |
|||
} |
|||
); |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
The new property becomes available on the UI, API and the database. You can even define navigation properties. This provides an easy way to extend existing modules while using them as NuGet packages. See [the document](https://docs.abp.io/en/abp/latest/Module-Entity-Extensions) for details. |
|||
|
|||
### Blazor UI Improvements |
|||
|
|||
Since the Blazor UI is relatively new in the ABP Framework, we continue to add features and make enhancements to fill the gap between other supported UI types. |
|||
|
|||
#### Bundling & Minification |
|||
|
|||
In the version 4.1, we had introduced the `abp bundle` command for the Blazor UI to add global script and style files of the depended modules into the `index.html`. It was a preparation for a real bundling & minification system. With the version 4.2, this command has been completed. |
|||
|
|||
Whenever you add a new module to your Blazor application, just type the `abp bundle` command in a command line terminal; |
|||
|
|||
* It finds all the global script/style files in your application and the modules your application directly or indirectly depends on, ordered by the module dependencies. |
|||
* Bundles all the scripts into a single file and minified the file (same for the styles). |
|||
* Add the single bundle file to the `index.html` file. |
|||
|
|||
Added a configuration into the `appsettings.json` file in the Blazor application in the application startup template to control the bundling mode: |
|||
|
|||
````js |
|||
{ |
|||
"AbpCli": { |
|||
"Bundle": { |
|||
"Mode": "BundleAndMinify" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
Possible values are; |
|||
|
|||
* `BundleAndMinify`: Bundle all the files into a single file and minify the content. |
|||
* `Bundle`: Bundle all files into a single file, but not minify. |
|||
* `None`: Add files individually, do not bundle. |
|||
|
|||
See the [Global Scripts & Styles](https://docs.abp.io/en/abp/4.1/UI/Blazor/Global-Scripts-Styles) document for details. |
|||
|
|||
#### SubmitButton |
|||
|
|||
`SubmitButton` is a new component that simplifies to save a form: |
|||
|
|||
````html |
|||
<SubmitButton Clicked="UpdateEntityAsync" /> |
|||
```` |
|||
|
|||
The main advantages of using this component instead of a standard `Button` with submit type is; It automatically blocks the submit button until the save operation has fully completed. This prevents multiple clicks by user. And it is shorter than doing all manually. See the [document](https://docs.abp.io/en/abp/4.1/UI/Blazor/SubmitButton). |
|||
|
|||
#### Other Blazor UI highlights |
|||
|
|||
* Implemented some **animations** (like opening/closing modals and dropdowns). |
|||
* Automatically **focus** to the first input when you open a modal form. |
|||
|
|||
Module extensibility system (mentioned above) for the Blazor UI is under development and not available yet. |
|||
|
|||
## What's new with the ABP Commercial 4.1 |
|||
|
|||
### Blazor UI Improvements |
|||
|
|||
We continue to complete missing modules and functionalities for the Blazor UI. |
|||
|
|||
#### Organization Unit Management |
|||
|
|||
Organization Management UI has been implemented for the Blazor UI. Example screenshot: |
|||
|
|||
 |
|||
|
|||
#### IdentityServer UI |
|||
|
|||
IdentityServer Management UI is also available for the Blazor UI now: |
|||
|
|||
 |
|||
|
|||
### Suite: Navigation Property Selection with Typeahead |
|||
|
|||
We had introduced auto-complete select style navigation property selection. With this release, it is fully supported by all the UI options. So, when you create an CRUD page with ABP Suite for entity that has 1 to Many relation to another entity, you can simply select the target entity with a typeahead style select component. Example screenshot: |
|||
|
|||
 |
|||
|
|||
### Spanish Language Translation |
|||
|
|||
We continue to add new language supports for the UI. In this version, translated the UI to **Spanish** language. |
|||
|
|||
 |
|||
|
|||
### Coming: Public Website with Integrated CMS Features |
|||
|
|||
In the next version, the application startup template will come with a public website application option. CMS Kit module will be installed in the website by default, that means newsletter, contact form, comments and some other new features will be directly usable in your applications. |
|||
|
|||
An early screenshot from the public website application home page: |
|||
|
|||
 |
|||
|
|||
## Other News |
|||
|
|||
### ABP Community Contents |
|||
|
|||
A lot of new contents have been published in the ABP Community Web Site in the last two weeks: |
|||
|
|||
* [How to Integrate the Telerik Blazor Components to the ABP Blazor UI](https://community.abp.io/articles/how-to-integrate-the-telerik-blazor-components-to-the-abp-blazor-ui-q8g31abb) by [EngincanV](https://github.com/EngincanV) |
|||
* [Using DevExpress Blazor UI Components With the ABP Framework](https://community.abp.io/articles/using-devexpress-blazor-ui-components-with-the-abp-framework-wrpoa8rw) by [@berkansasmaz](https://github.com/berkansasmaz) |
|||
* [Creating a new UI theme by copying the Basic Theme (for MVC UI)](https://community.abp.io/articles/creating-a-new-ui-theme-by-copying-the-basic-theme-for-mvc-ui-yt9b18io) by [@ebubekirdinc](https://github.com/ebubekirdinc) |
|||
* [Using Angular Material Components With the ABP Framework](https://community.abp.io/members/muhammedaltug) by [@muhammedaltug](https://github.com/muhammedaltug) |
|||
* [How to export Excel files from the ABP framework](https://community.abp.io/articles/how-to-export-excel-files-from-the-abp-framework-wm7nnw3n) by [bartvanhoey](https://github.com/bartvanhoey) |
|||
* [Creating an Event Organizer Application with the ABP Framework & Blazor UI](https://community.abp.io/articles/creating-an-event-organizer-application-with-the-blazor-ui-wbe0sf2z) by [@hikalkan](https://github.com/hikalkan) |
|||
|
|||
Thanks to all of the contributors. We are waiting for your contributions too. If you want to create content for the ABP Community, please visit [community.abp.io](https://community.abp.io/) website and submit your article. |
|||
|
|||
#### Be a Superhero on Day 1 with ABP.IO |
|||
|
|||
Thanks to [@lprichar](http://github.com/lprichar) prepared an awesome introduction video for the ABP.IO Platform: "[Be a Superhero on Day 1 with ABP.IO](https://www.youtube.com/watch?v=ea0Zx9DLcGA)". |
|||
|
|||
#### New Sample Application: Event Organizer |
|||
|
|||
This is a new example application developed using the ABP Framework and the Blazor UI. See [this article](https://community.abp.io/articles/creating-an-event-organizer-application-with-the-blazor-ui-wbe0sf2z) for a step by step implementation guide. |
|||
|
|||
 |
|||
|
|||
### Github Discussions |
|||
|
|||
We enabled the [GitHub Discussions for the abp repository](https://github.com/abpframework/abp/discussions) as another place to discuss ideas or get help for the ABP Framework. The ABP core team is spending time participating in discussions and answering to questions as much as possible. |
|||
|
|||
## About the Next Release(s) |
|||
|
|||
Beginning from the next version (4.2.0), we are starting to spend more effort on the **CMS Kit module**. The purpose of this module is to provide CMS primitives (e.g. **comments, tags, reactions, contents**...) and features (e.g. **blog, pages, surveys**) as pre-built and reusable components. Current blog module will be a part of the CMS Kit module. |
|||
|
|||
We will continue to prepare documents, guides, tutorials and examples. And surely, we will continue to make enhancements and optimizations on the current features. |
|||
|
|||
> The planned preview release date for the version 4.2.0 is January 14, 2021 and the final (stable) version release date is January 28, 2021. |
|||
|
|||
Follow the [GitHub milestones](https://github.com/abpframework/abp/milestones) for all the planned ABP Framework version release dates. |
|||
|
|||
## Feedback |
|||
|
|||
Please check out the ABP Framework 4.1.0 RC and [provide feedback](https://github.com/abpframework/abp/issues/new) to help us to release a more stable version. **The planned release date for the [4.1.0 final](https://github.com/abpframework/abp/milestone/45) version is January 4, 2021**. |
|||
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 865 KiB |
|
After Width: | Height: | Size: 268 KiB |
|
After Width: | Height: | Size: 27 KiB |
@ -0,0 +1,936 @@ |
|||
# Creating an Event Organizer Application with the ABP Framework & Blazor UI. |
|||
|
|||
## Introduction |
|||
|
|||
In this article, we will create an example application that is a simple **meeting/event organizer**: People create events and other people registers to the event. |
|||
|
|||
The application has been developed with **Blazor** as the UI framework and **MongoDB** as the database provider. |
|||
|
|||
> This tutorial is based on my notes that I'd created to implement this application in a workshop. It shows the necessary steps to build the application rather than detailed explanations. |
|||
|
|||
### Source Code |
|||
|
|||
Source code of the completed application is [available on GitHub](https://github.com/abpframework/abp-samples/tree/master/EventOrganizer). |
|||
|
|||
### Screenshots |
|||
|
|||
Here, the pages of the final application. |
|||
|
|||
**Home Page - Event List** |
|||
|
|||
 |
|||
|
|||
**Creating a new Event** |
|||
|
|||
 |
|||
|
|||
**Event Detail Page** |
|||
|
|||
 |
|||
|
|||
## Requirements |
|||
|
|||
The following tools are needed to be able to run the solution. |
|||
|
|||
* .NET 5.0 SDK |
|||
* Visual Studio 2019 16.8.0+ or another compatible IDE |
|||
* MongoDB Server (with MongoDB Compass) |
|||
|
|||
## Development |
|||
|
|||
### Creating a new Application |
|||
|
|||
* Use the following ABP CLI command: |
|||
|
|||
````bash |
|||
abp new EventOrganizer -u blazor -d mongodb |
|||
```` |
|||
|
|||
### Open & Run the Application |
|||
|
|||
* Open the solution in Visual Studio (or your favorite IDE). |
|||
* Run the `EventOrganizer.DbMigrator` application to seed the initial data. |
|||
* Run the `EventOrganizer.HttpApi.Host` application that starts the server side. |
|||
* Run the `EventOrganizer.Blazor` application to start the UI. |
|||
|
|||
### Apply the Custom Styles |
|||
|
|||
* Add styles to `wwwroot/main.css`: |
|||
|
|||
````css |
|||
body.abp-application-layout { |
|||
background-color: #222 !important; |
|||
font-size: 18px; |
|||
} |
|||
nav#main-navbar.bg-dark { |
|||
background-color: #222 !important; |
|||
box-shadow: none !important; |
|||
} |
|||
.event-pic { |
|||
width: 100%; |
|||
border-radius: 12px; |
|||
box-shadow: 5px 5px 0px 0px rgba(0,0,0,.5); |
|||
margin-bottom: 10px; |
|||
} |
|||
.event-link:hover, .event-link:hover *{ |
|||
text-decoration: none; |
|||
} |
|||
.event-link:hover .event-pic { |
|||
box-shadow: 5px 5px 0px 0px #ffd800; |
|||
} |
|||
.event-form { |
|||
background-color: #333 !important; |
|||
box-shadow: 5px 5px 0px 0px rgba(0,0,0,.5); |
|||
border-radius: 12px; |
|||
} |
|||
.table { |
|||
background: #fff; |
|||
border-radius: 12px; |
|||
box-shadow: 5px 5px 0px 0px rgba(0,0,0,.5); |
|||
} |
|||
.table th{ |
|||
border: 0 !important; |
|||
} |
|||
.modal { |
|||
color: #333; |
|||
} |
|||
.page-item:first-child .page-link { |
|||
margin-left: 0; |
|||
border-top-left-radius: 12px; |
|||
border-bottom-left-radius: 12px; |
|||
} |
|||
.page-item:last-child .page-link { |
|||
border-top-right-radius: 12px; |
|||
border-bottom-right-radius: 12px; |
|||
} |
|||
.btn { |
|||
border-radius: 8px; |
|||
} |
|||
.att-list { |
|||
list-style: none; |
|||
padding: 0; |
|||
} |
|||
.att-list li { |
|||
padding: 4px 0 0 0; |
|||
} |
|||
```` |
|||
|
|||
* `wwwroot/index.html`: Remove `bg-light` class from the `body` tag and add `bg-dark text-light`. |
|||
|
|||
### Domain Layer |
|||
|
|||
* Add the following `Event` aggregate (with `EventAttendee`) to the solution: |
|||
|
|||
**Event** |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class Event : FullAuditedAggregateRoot<Guid> |
|||
{ |
|||
public string Title { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
|
|||
public bool IsFree { get; set; } |
|||
|
|||
public DateTime StartTime { get; set; } |
|||
|
|||
public ICollection<EventAttendee> Attendees { get; set; } |
|||
|
|||
public Event() |
|||
{ |
|||
Attendees = new List<EventAttendee>(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
**EventAttendee** |
|||
|
|||
```csharp |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class EventAttendee : IHasCreationTime |
|||
{ |
|||
public Guid UserId { get; set; } |
|||
|
|||
public DateTime CreationTime { get; set; } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### MongoDB Mapping |
|||
|
|||
* Add the following property to the `EventOrganizerMongoDbContext`: |
|||
|
|||
````csharp |
|||
public IMongoCollection<Event> Events => Collection<Event>(); |
|||
```` |
|||
|
|||
### Clean Index.razor & Add the Header & "Create Event" button |
|||
|
|||
* Clean the `Index.razor` file. |
|||
* Replace the content with the following code: |
|||
|
|||
````html |
|||
@page "/" |
|||
@inherits EventOrganizerComponentBase |
|||
<Row Class="mb-4"> |
|||
<Column Class="text-left"> |
|||
<h1>Upcoming Events</h1> |
|||
</Column> |
|||
<Column Class="text-right"> |
|||
@if (CurrentUser.IsAuthenticated) |
|||
{ |
|||
<a class="btn btn-primary" href="/create-event"> |
|||
<i class="fa fa-plus"></i> @L["CreateEvent"] |
|||
</a> |
|||
} |
|||
</Column> |
|||
</Row> |
|||
```` |
|||
|
|||
* Open `Localization/EventOrganizer/en.json` in the `EventOrganizer.Domain.Shared` project and add the following entry: |
|||
|
|||
````json |
|||
"CreateEvent": "Create a new event!" |
|||
```` |
|||
|
|||
The Result (run the `EventOrganizer.Blazor` application to see): |
|||
|
|||
 |
|||
|
|||
### Event Creation |
|||
|
|||
* Create the Initial `IEventAppService` with the `CreateAsync` method: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public interface IEventAppService : IApplicationService |
|||
{ |
|||
Task<Guid> CreateAsync(EventCreationDto input); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Add `EventCreationDto` class: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class EventCreationDto |
|||
{ |
|||
[Required] |
|||
[StringLength(100)] |
|||
public string Title { get; set; } |
|||
|
|||
[Required] |
|||
[StringLength(2000)] |
|||
public string Description { get; set; } |
|||
|
|||
public bool IsFree { get; set; } |
|||
|
|||
public DateTime StartTime { get; set; } |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Implement the `EventAppService`: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class EventAppService : EventOrganizerAppService, IEventAppService |
|||
{ |
|||
private readonly IRepository<Event, Guid> _eventRepository; |
|||
|
|||
public EventAppService(IRepository<Event, Guid> eventRepository) |
|||
{ |
|||
_eventRepository = eventRepository; |
|||
} |
|||
|
|||
[Authorize] |
|||
public async Task<Guid> CreateAsync(EventCreationDto input) |
|||
{ |
|||
var eventEntity = ObjectMapper.Map<EventCreationDto, Event>(input); |
|||
await _eventRepository.InsertAsync(eventEntity); |
|||
return eventEntity.Id; |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Add AutoMapper mapping to the `EventOrganizerApplicationAutoMapperProfile` class: |
|||
|
|||
````csharp |
|||
using AutoMapper; |
|||
using EventOrganizer.Events; |
|||
|
|||
namespace EventOrganizer |
|||
{ |
|||
public class EventOrganizerApplicationAutoMapperProfile : Profile |
|||
{ |
|||
public EventOrganizerApplicationAutoMapperProfile() |
|||
{ |
|||
CreateMap<EventCreationDto, Event>(); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This will automatically create the HTTP (REST) API for the application service (run the `EventOrganizer.HttpApi.Host` application to see it on the Swagger UI): |
|||
|
|||
 |
|||
|
|||
* Create the `CreateEvent.razor` file: |
|||
|
|||
````csharp |
|||
@page "/create-event" |
|||
@inherits EventOrganizerComponentBase |
|||
<Heading Size="HeadingSize.Is3" Margin="Margin.Is5.FromTop.Is4.FromBottom" Class="text-center">Create Event</Heading> |
|||
<Row> |
|||
<Column ColumnSize="ColumnSize.Is6.Is3.WithOffset"> |
|||
<div class="p-lg-5 p-md-3 event-form"> |
|||
<EditForm Model="@Event" OnValidSubmit="Create"> |
|||
<Field> |
|||
<FieldLabel>@L["Title"]</FieldLabel> |
|||
<TextEdit @bind-Text="@Event.Title" /> |
|||
</Field> |
|||
<Field> |
|||
<FieldLabel>@L["Description"]</FieldLabel> |
|||
<MemoEdit @bind-Text="@Event.Description" /> |
|||
</Field> |
|||
<Field> |
|||
<Check TValue="bool" @bind-Checked="@Event.IsFree">@L["Free"]</Check> |
|||
</Field> |
|||
<Field> |
|||
<FieldLabel>@L["StartTime"]</FieldLabel> |
|||
<DateEdit TValue="DateTime" @bind-Date="@Event.StartTime" /> |
|||
</Field> |
|||
<Button Type="@ButtonType.Submit" Block="true" Color="@Color.Primary" Size="Size.Large">@L["Save"]</Button> |
|||
</EditForm> |
|||
</div> |
|||
</Column> |
|||
</Row> |
|||
```` |
|||
|
|||
* Create a partial `CreateEvent` class in the same folder, with the `CreateEvent.razor.cs` as the file name: |
|||
|
|||
````csharp |
|||
using System.Threading.Tasks; |
|||
using EventOrganizer.Events; |
|||
using Microsoft.AspNetCore.Components; |
|||
|
|||
namespace EventOrganizer.Blazor.Pages |
|||
{ |
|||
public partial class CreateEvent |
|||
{ |
|||
private EventCreationDto Event { get; set; } = new EventCreationDto(); |
|||
|
|||
private readonly IEventAppService _eventAppService; |
|||
private readonly NavigationManager _navigationManager; |
|||
|
|||
public CreateEvent( |
|||
IEventAppService eventAppService, |
|||
NavigationManager navigationManager) |
|||
{ |
|||
_eventAppService = eventAppService; |
|||
_navigationManager = navigationManager; |
|||
} |
|||
|
|||
private async Task Create() |
|||
{ |
|||
var eventId = await _eventAppService.CreateAsync(Event); |
|||
_navigationManager.NavigateTo("/events/" + eventId); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
The final UI is (run the `EventOrganizer.Blazor` application and click to the "Create Event" button): |
|||
|
|||
 |
|||
|
|||
### Upcoming Events (Home Page) |
|||
|
|||
* Open the `IEventAppService` and add a `GetUpcomingAsync` method to get the list of upcoming events: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public interface IEventAppService : IApplicationService |
|||
{ |
|||
Task<Guid> CreateAsync(EventCreationDto input); |
|||
|
|||
Task<List<EventDto>> GetUpcomingAsync(); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Add a `EventDto` class: |
|||
|
|||
````csharp |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class EventDto : EntityDto<Guid> |
|||
{ |
|||
public string Title { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
|
|||
public bool IsFree { get; set; } |
|||
|
|||
public DateTime StartTime { get; set; } |
|||
|
|||
public int AttendeesCount { get; set; } |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Implement the `GetUpcomingAsync` in the `EventAppService` class: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class EventAppService : EventOrganizerAppService, IEventAppService |
|||
{ |
|||
private readonly IRepository<Event, Guid> _eventRepository; |
|||
|
|||
public EventAppService(IRepository<Event, Guid> eventRepository) |
|||
{ |
|||
_eventRepository = eventRepository; |
|||
} |
|||
|
|||
[Authorize] |
|||
public async Task<Guid> CreateAsync(EventCreationDto input) |
|||
{ |
|||
var eventEntity = ObjectMapper.Map<EventCreationDto, Event>(input); |
|||
await _eventRepository.InsertAsync(eventEntity); |
|||
return eventEntity.Id; |
|||
} |
|||
|
|||
public async Task<List<EventDto>> GetUpcomingAsync() |
|||
{ |
|||
var events = await AsyncExecuter.ToListAsync( |
|||
_eventRepository |
|||
.Where(x => x.StartTime > Clock.Now) |
|||
.OrderBy(x => x.StartTime) |
|||
); |
|||
|
|||
return ObjectMapper.Map<List<Event>, List<EventDto>>(events); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Add the following line into the `EventOrganizerApplicationAutoMapperProfile` constructor: |
|||
|
|||
````csharp |
|||
CreateMap<Event, EventDto>(); |
|||
```` |
|||
|
|||
Run the `EventOrganizer.HttpApi.Host` application to see the new `upcoming` endpoint on the Swagger UI: |
|||
|
|||
 |
|||
|
|||
* Change the `Pages/Index.razor.cs` content in the `EventOrganizer.Blazor` project as shown below: |
|||
|
|||
```csharp |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using EventOrganizer.Events; |
|||
|
|||
namespace EventOrganizer.Blazor.Pages |
|||
{ |
|||
public partial class Index |
|||
{ |
|||
private List<EventDto> UpcomingEvents { get; set; } = new List<EventDto>(); |
|||
|
|||
private readonly IEventAppService _eventAppService; |
|||
|
|||
public Index(IEventAppService eventAppService) |
|||
{ |
|||
_eventAppService = eventAppService; |
|||
} |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
UpcomingEvents = await _eventAppService.GetUpcomingAsync(); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
* Change the `Pages/Index.razor` content in the `EventOrganizer.Blazor` project as shown below: |
|||
|
|||
````html |
|||
@page "/" |
|||
@inherits EventOrganizerComponentBase |
|||
<Row Class="mb-4"> |
|||
<Column Class="text-left"> |
|||
<h1>Upcoming Events</h1> |
|||
</Column> |
|||
<Column Class="text-right"> |
|||
@if (CurrentUser.IsAuthenticated) |
|||
{ |
|||
<a class="btn btn-primary" href="/create-event"> |
|||
<i class="fa fa-plus"></i> @L["CreateEvent"] |
|||
</a> |
|||
} |
|||
</Column> |
|||
</Row> |
|||
<Row> |
|||
@foreach (var upcomingEvent in UpcomingEvents) |
|||
{ |
|||
<Column Class="col-12 col-lg-4 col-md-6"> |
|||
<a class="mb-5 position-relative d-block event-link" href="/events/@upcomingEvent.Id"> |
|||
<div class="position-absolute text-right w-100 px-3 py-2" style="left: 0; top: 2px;"> |
|||
@if (upcomingEvent.IsFree) |
|||
{ |
|||
<Badge Color="Color.Success" Class="mr-1">FREE</Badge> |
|||
} |
|||
<span class="badge badge-warning font-weight-normal"> |
|||
<i class="fas fa-user-friends"></i> |
|||
<span class="font-weight-bold">@upcomingEvent.AttendeesCount</span> |
|||
</span> |
|||
</div> |
|||
<img src="https://picsum.photos/seed/@upcomingEvent.Id/400/300" class="event-pic"/> |
|||
<div class="px-3 py-1"> |
|||
<small class="font-weight-bold text-warning my-2 d-block text-uppercase">@upcomingEvent.StartTime.ToLongDateString()</small> |
|||
<p class="h4 text-light d-block mb-2">@upcomingEvent.Title</p> |
|||
<p class="text-light" style="opacity: .65;">@upcomingEvent.Description.TruncateWithPostfix(150)</p> |
|||
</div> |
|||
</a> |
|||
</Column> |
|||
} |
|||
</Row> |
|||
```` |
|||
|
|||
The new home page is shown below: |
|||
|
|||
 |
|||
|
|||
### Event Detail Page |
|||
|
|||
* Add `GetAsync`, `RegisterAsync`, `UnregisterAsync` and `DeleteAsync` methods to the `IEventAppService`: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public interface IEventAppService : IApplicationService |
|||
{ |
|||
Task<Guid> CreateAsync(EventCreationDto input); |
|||
|
|||
Task<List<EventDto>> GetUpcomingAsync(); |
|||
|
|||
Task<EventDetailDto> GetAsync(Guid id); |
|||
|
|||
Task RegisterAsync(Guid id); |
|||
|
|||
Task UnregisterAsync(Guid id); |
|||
|
|||
Task DeleteAsync(Guid id); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Add `EventDetailDto` class: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class EventDetailDto : CreationAuditedEntityDto<Guid> |
|||
{ |
|||
public string Title { get; set; } |
|||
|
|||
public string Description { get; set; } |
|||
|
|||
public bool IsFree { get; set; } |
|||
|
|||
public DateTime StartTime { get; set; } |
|||
|
|||
public List<EventAttendeeDto> Attendees { get; set; } |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Add `EventAttendeeDto` class: |
|||
|
|||
````csharp |
|||
using System; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class EventAttendeeDto |
|||
{ |
|||
public Guid UserId { get; set; } |
|||
|
|||
public string UserName { get; set; } |
|||
|
|||
public DateTime CreationTime { get; set; } |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Implement the new methods in the `EventAppService`: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EventOrganizer.Users; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
public class EventAppService : EventOrganizerAppService, IEventAppService |
|||
{ |
|||
private readonly IRepository<Event, Guid> _eventRepository; |
|||
private readonly IRepository<AppUser, Guid> _userRepository; |
|||
|
|||
public EventAppService(IRepository<Event, Guid> eventRepository, IRepository<AppUser, Guid> userRepository) |
|||
{ |
|||
_eventRepository = eventRepository; |
|||
_userRepository = userRepository; |
|||
} |
|||
|
|||
[Authorize] |
|||
public async Task<Guid> CreateAsync(EventCreationDto input) |
|||
{ |
|||
var eventEntity = ObjectMapper.Map<EventCreationDto, Event>(input); |
|||
await _eventRepository.InsertAsync(eventEntity); |
|||
return eventEntity.Id; |
|||
} |
|||
|
|||
public async Task<List<EventDto>> GetUpcomingAsync() |
|||
{ |
|||
var events = await AsyncExecuter.ToListAsync( |
|||
_eventRepository |
|||
.Where(x => x.StartTime > Clock.Now) |
|||
.OrderBy(x => x.StartTime) |
|||
); |
|||
|
|||
return ObjectMapper.Map<List<Event>, List<EventDto>>(events); |
|||
} |
|||
|
|||
public async Task<EventDetailDto> GetAsync(Guid id) |
|||
{ |
|||
var @event = await _eventRepository.GetAsync(id); |
|||
var attendeeIds = @event.Attendees.Select(a => a.UserId).ToList(); |
|||
var attendees = (await AsyncExecuter.ToListAsync(_userRepository.Where(u => attendeeIds.Contains(u.Id)))) |
|||
.ToDictionary(x => x.Id); |
|||
|
|||
var result = ObjectMapper.Map<Event, EventDetailDto>(@event); |
|||
|
|||
foreach (var attendeeDto in result.Attendees) |
|||
{ |
|||
attendeeDto.UserName = attendees[attendeeDto.UserId].UserName; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
[Authorize] |
|||
public async Task RegisterAsync(Guid id) |
|||
{ |
|||
var @event = await _eventRepository.GetAsync(id); |
|||
if (@event.Attendees.Any(a => a.UserId == CurrentUser.Id)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
@event.Attendees.Add(new EventAttendee {UserId = CurrentUser.GetId(), CreationTime = Clock.Now}); |
|||
await _eventRepository.UpdateAsync(@event); |
|||
} |
|||
|
|||
[Authorize] |
|||
public async Task UnregisterAsync(Guid id) |
|||
{ |
|||
var @event = await _eventRepository.GetAsync(id); |
|||
var removedItems = @event.Attendees.RemoveAll(x => x.UserId == CurrentUser.Id); |
|||
if (removedItems.Any()) |
|||
{ |
|||
await _eventRepository.UpdateAsync(@event); |
|||
} |
|||
} |
|||
|
|||
[Authorize] |
|||
public async Task DeleteAsync(Guid id) |
|||
{ |
|||
var @event = await _eventRepository.GetAsync(id); |
|||
|
|||
if (CurrentUser.Id != @event.CreatorId) |
|||
{ |
|||
throw new UserFriendlyException("You don't have the necessary permission to delete this event!"); |
|||
} |
|||
|
|||
await _eventRepository.DeleteAsync(id); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
* Add the following mappings into the `EventOrganizerApplicationAutoMapperProfile`: |
|||
|
|||
````csharp |
|||
CreateMap<Event, EventDetailDto>(); |
|||
CreateMap<EventAttendee, EventAttendeeDto>(); |
|||
```` |
|||
|
|||
Run the `EventOrganizer.HttpApi.Host` application to see the complete Event HTTP API in the Swagger UI: |
|||
|
|||
 |
|||
|
|||
* Create `EventDetail.razor` component with the following content: |
|||
|
|||
````html |
|||
@page "/events/{id}" |
|||
@inherits EventOrganizerComponentBase |
|||
@if (Event != null) |
|||
{ |
|||
<Row Class="mb-4"> |
|||
<Column Class="text-left"> |
|||
<h1>@Event.Title</h1> |
|||
</Column> |
|||
<Column Class="text-right pt-2"> |
|||
<a href="/" Class="btn btn-dark"><i class="fa fa-arrow-left"></i> Back</a> |
|||
@if (CurrentUser.IsAuthenticated && CurrentUser.Id == Event.CreatorId) |
|||
{ |
|||
<Button Color="Color.Danger" Clicked="Delete" Class="ml-1">Delete</Button> |
|||
} |
|||
</Column> |
|||
</Row> |
|||
<Row> |
|||
<Column Class="col-12 col-md-8"> |
|||
<div class="position-relative"> |
|||
<div class="position-absolute text-right w-100 px-3 py-2" style="left: 0; top: 2px;"> |
|||
@if (Event.IsFree) |
|||
{ |
|||
<Badge Color="Color.Success" Class="mr-1">FREE</Badge> |
|||
} |
|||
<span class="badge badge-warning font-weight-normal"> |
|||
<i class="fas fa-user-friends"></i> |
|||
<span class="font-weight-bold">@Event.Attendees.Count</span> |
|||
</span> |
|||
</div> |
|||
<img src="https://picsum.photos/seed/@Event.Id/800/600" class="event-pic" /> |
|||
<small class="font-weight-bold text-warning my-2 d-block text-uppercase">Start time: @Event.StartTime.ToLongDateString()</small> |
|||
<p style="opacity: .65;">@Event.Description</p> |
|||
</div> |
|||
</Column> |
|||
<Column Class="col-12 col-md-4"> |
|||
<div class="p-4 event-form"> |
|||
@if (CurrentUser.IsAuthenticated) |
|||
{ |
|||
<div> |
|||
@if (!IsRegistered) |
|||
{ |
|||
<Button Color="Color.Primary" Clicked="Register" Class="btn-block btn-lg">Register now!</Button> |
|||
} |
|||
else |
|||
{ |
|||
<p>You are registered in this event</p> |
|||
<Button Color="Color.Secondary" Clicked="UnRegister" Class="btn-block">Cancel registration!</Button> |
|||
} |
|||
</div> |
|||
} |
|||
else |
|||
{ |
|||
<a class="btn btn-primary" href="/authentication/login"> |
|||
<i class="fa fa-sign-in-alt"></i> Login to attend! |
|||
</a> |
|||
} |
|||
</div> |
|||
<div class="mt-4 event-form p-4"> |
|||
<span class="font-weight-bold"><i class="fas fa-user-friends"></i> Attendees <span class="float-right font-weight-normal" style="opacity:.65;">(@Event.Attendees.Count)</span></span> |
|||
<ul class="mt-1 mb-0 att-list"> |
|||
@foreach (var attendee in Event.Attendees) |
|||
{ |
|||
<li><i class="fa fa-check"></i> @attendee.UserName</li> |
|||
} |
|||
</ul> |
|||
</div> |
|||
</Column> |
|||
</Row> |
|||
} |
|||
```` |
|||
|
|||
* Create `EventDetail.razor.cs` file with the following content: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EventOrganizer.Events; |
|||
using Microsoft.AspNetCore.Components; |
|||
|
|||
namespace EventOrganizer.Blazor.Pages |
|||
{ |
|||
public partial class EventDetail |
|||
{ |
|||
[Parameter] |
|||
public string Id { get; set; } |
|||
|
|||
private EventDetailDto Event { get; set; } |
|||
private bool IsRegistered { get; set; } |
|||
|
|||
private readonly IEventAppService _eventAppService; |
|||
private readonly NavigationManager _navigationManager; |
|||
|
|||
public EventDetail( |
|||
IEventAppService eventAppService, |
|||
NavigationManager navigationManager) |
|||
{ |
|||
_eventAppService = eventAppService; |
|||
_navigationManager = navigationManager; |
|||
} |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
await GetEventAsync(); |
|||
} |
|||
|
|||
private async Task GetEventAsync() |
|||
{ |
|||
Event = await _eventAppService.GetAsync(Guid.Parse(Id)); |
|||
if (CurrentUser.IsAuthenticated) |
|||
{ |
|||
IsRegistered = Event.Attendees.Any(a => a.UserId == CurrentUser.Id); |
|||
} |
|||
} |
|||
|
|||
private async Task Register() |
|||
{ |
|||
await _eventAppService.RegisterAsync(Guid.Parse(Id)); |
|||
await GetEventAsync(); |
|||
} |
|||
|
|||
private async Task UnRegister() |
|||
{ |
|||
await _eventAppService.UnregisterAsync(Guid.Parse(Id)); |
|||
await GetEventAsync(); |
|||
} |
|||
|
|||
private async Task Delete() |
|||
{ |
|||
if (!await Message.Confirm("This event will be deleted: " + Event.Title)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
await _eventAppService.DeleteAsync(Guid.Parse(Id)); |
|||
_navigationManager.NavigateTo("/"); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
The resulting page is shown below: |
|||
|
|||
 |
|||
|
|||
### Integration Tests |
|||
|
|||
Create an `EventAppService_Tests` class in the `EventOrganizer.Application.Tests` project: |
|||
|
|||
````csharp |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace EventOrganizer.Events |
|||
{ |
|||
[Collection(EventOrganizerTestConsts.CollectionDefinitionName)] |
|||
public class EventAppService_Tests : EventOrganizerApplicationTestBase |
|||
{ |
|||
private readonly IEventAppService _eventAppService; |
|||
|
|||
public EventAppService_Tests() |
|||
{ |
|||
_eventAppService = GetRequiredService<IEventAppService>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Create_A_Valid_Event() |
|||
{ |
|||
// Create an event |
|||
|
|||
var eventId = await _eventAppService.CreateAsync( |
|||
new EventCreationDto |
|||
{ |
|||
Title = "My test event 1", |
|||
Description = "My test event description 1", |
|||
IsFree = true, |
|||
StartTime = DateTime.Now.AddDays(2) |
|||
} |
|||
); |
|||
|
|||
eventId.ShouldNotBe(Guid.Empty); |
|||
|
|||
// Get the event |
|||
|
|||
var @event = await _eventAppService.GetAsync(eventId); |
|||
@event.Title.ShouldBe("My test event 1"); |
|||
|
|||
// Get upcoming events |
|||
|
|||
var events = await _eventAppService.GetUpcomingAsync(); |
|||
events.ShouldContain(x => x.Title == "My test event 1"); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
## Source Code |
|||
|
|||
Source code of the completed application is [available on GitHub](https://github.com/abpframework/abp-samples/tree/master/EventOrganizer). |
|||
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 865 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 18 KiB |
@ -0,0 +1,110 @@ |
|||
## Using DevExpress Blazor UI Components With the ABP Framework |
|||
|
|||
Hi, in this step by step article, I will show you how to integrate [DevExpress](https://demos.devexpress.com/blazor/) blazor UI components into ABP Framework-based applications. |
|||
|
|||
 |
|||
|
|||
*(A screenshot from the example application developed in this article)* |
|||
|
|||
## Create the Project |
|||
|
|||
> ABP Framework offers startup templates to get into business faster. |
|||
|
|||
In this article, I will create a new startup template with EF Core as a database provider and Blazor for UI framework. But if you already have a project with Blazor UI, you don't need to create a new startup template, you can directly implement the following steps to your existing project. |
|||
|
|||
> If you already have a project with the Blazor UI, you can skip this section. |
|||
|
|||
* Before starting to development, we will create a solution named `DevExpressSample` (or whatever you want). We will create a new startup template with EF Core as a database provider and Blazor for UI framework by using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): |
|||
|
|||
````bash |
|||
abp new DevExpressSample -u blazor |
|||
```` |
|||
|
|||
 |
|||
|
|||
* Our project boilerplate will be ready after the download is finished. Then, we can open the solution in the Visual Studio (or any other IDE) and run the `DevExpressSample.DbMigrator` to create the database and seed initial data (which creates the admin user, admin role, permissions etc.) |
|||
|
|||
* After database and initial data created, |
|||
* Run the `DevExpressSample.HttpApi.Host` to see our server side working and |
|||
* Run the `DevExpressSample.Blazor` to see our UI working properly. |
|||
|
|||
> _Default login credentials for admin: username is **admin** and password is **1q2w3E\***_ |
|||
|
|||
## Install DevExpress |
|||
|
|||
You can follow [this documentation](https://docs.devexpress.com/Blazor/401986/getting-started/install-components-and-create-an-application/without-devexpress-installer/microsoft-templates) to install DevExpress packages into your computer. |
|||
|
|||
> Don't forget to add _"DevExpress NuGet Feed"_ to your **Nuget Package Sources**. |
|||
|
|||
### Adding DevExpress NuGet Packages |
|||
|
|||
Add the `DevExpress.Blazor` NuGet package to the `DevExpressSample.Blazor` project. |
|||
|
|||
``` |
|||
Install-Package DevExpress.Blazor |
|||
``` |
|||
|
|||
### Register DevExpress Resources |
|||
|
|||
1. Add the following line to the HEAD section of the `wwwroot/index.html` file within the `DevExpressSample.Blazor` project: |
|||
|
|||
```Razor |
|||
<head> |
|||
<!--...--> |
|||
<link href="_content/DevExpress.Blazor/dx-blazor.css" rel="stylesheet" /> |
|||
</head> |
|||
``` |
|||
|
|||
2. In the `DevExpressSampleBlazorModule` class, call the `AddDevExpressBlazor()` method from your project's `ConfigureServices()` method: |
|||
|
|||
```csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var environment = context.Services.GetSingletonInstance<IWebAssemblyHostEnvironment>(); |
|||
var builder = context.Services.GetSingletonInstance<WebAssemblyHostBuilder>(); |
|||
// ... |
|||
builder.Services.AddDevExpressBlazor(); |
|||
} |
|||
``` |
|||
|
|||
3. Register the **DevExpressSample.Blazor** namespace in the `_Imports.razor` file: |
|||
|
|||
```Razor |
|||
@using DevExpress.Blazor |
|||
``` |
|||
|
|||
### Result |
|||
|
|||
The installation step was done. You can use any DevExpress Blazor UI component in your application: |
|||
|
|||
Example: A Scheduler: |
|||
|
|||
 |
|||
|
|||
This example has been created by following [this documentation](https://demos.devexpress.com/blazor/SchedulerViewTypes). |
|||
|
|||
## The Sample Application |
|||
|
|||
We have created a sample application with [Data Grid](https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxDataGrid-1) example. |
|||
|
|||
### The Source Code |
|||
|
|||
You can download the source code from [here](https://github.com/abpframework/abp-samples/tree/master/DevExpress-Blazor). |
|||
|
|||
The related files for this example are marked in the following screenshots. |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
### Additional Notes |
|||
|
|||
#### Data Storage |
|||
|
|||
I've used an in-memory list to store data for this example, instead of a real database. Because it is not related to DevExpress usage. There is a `SampleDataService.cs` file in `Data` folder at `DevExpressSample.Application.Contracts` project. All the data is stored here. |
|||
|
|||
## Conclusion |
|||
|
|||
In this article, I've explained how to use [DevExpress](https://www.devexpress.com/blazor/) components in your application. ABP Framework is designed so that it can work with any UI library/framework. |
|||
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 480 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 880 KiB |
@ -0,0 +1,534 @@ |
|||
# How to Integrate the Telerik Blazor Components to the ABP Blazor UI? |
|||
|
|||
## Introduction |
|||
|
|||
Hi, in this step by step article, we will see how we can integrate the Telerik Blazor Components to our Blazor UI. |
|||
|
|||
## Creating the Solution |
|||
|
|||
> ABP Framework offers startup templates to get into business faster. |
|||
|
|||
In this article, I will create a new startup template with EF Core as a database provider and Blazor for UI framework. But if you already have a project with Blazor UI, you don't need to create a new startup template, you can directly implement the following steps to your existing project. |
|||
|
|||
> If you already have a project with the Blazor UI, you can skip this section. |
|||
|
|||
* Before starting to development, we will create a solution named `TelerikComponents` (or whatever you want). We will create a new startup template with EF Core as a database provider and Blazor for UI framework by using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): |
|||
|
|||
```bash |
|||
abp new TelerikComponents --ui blazor --database-provider ef |
|||
``` |
|||
|
|||
* Our project boilerplate will be ready after the download is finished. Then, we can open the solution in the Visual Studio (or any other IDE) and run the `TelerikComponents.DbMigrator` to create the database and seed initial data (which creates the admin user, admin role, permissions, etc.) |
|||
|
|||
* After the database and initial data created, |
|||
* Run the `TelerikComponents.HttpApi.Host` to see our server-side working and |
|||
* Run the `TelerikComponents.Blazor` to see our UI working. |
|||
|
|||
> _Default login credentials for admin: username is **admin** and password is **1q2w3E\***_ |
|||
|
|||
## Starting the Development |
|||
|
|||
### Pre-requisite |
|||
|
|||
* First thing we need to do is downloading the [Progress Control Panel](https://www.telerik.com/download-trial-file/v2/control-panel?_ga=2.212029332.1667119438.1607582144-1944255175.1605161949) to get Telerik Blazor Components on our development machine. |
|||
|
|||
* If you will use the Telerik Blazor Components for the first time or you don't have an active license you can click [here](https://www.telerik.com/login/v2/download-b?ReturnUrl=https%3a%2f%2fwww.telerik.com%2fdownload-trial-file%2fv2-b%2fui-for-blazor%3f_ga%3d2.212029332.1667119438.1607582144-1944255175.1605161949#register) to download free trial. |
|||
|
|||
> You can find the more installation details from [here](https://docs.telerik.com/blazor-ui/getting-started/client-blazor?_ga=2.55603115.1667119438.1607582144-1944255175.1605161949&_gac=1.261851647.1607669357.CjwKCAiAq8f-BRBtEiwAGr3DgUDhBT25rs7hU0EQ8K-AfeUVxs3hSoIuIAuBOZ17CNPI4ZEArORPExoCyd4QAvD_BwE#step-0---download-the-components). |
|||
|
|||
>**Notes:** To download Telerik Blazor packages via NuGet, we need to setup Telerik NuGet package source. We can state it in the installer as below. In this way, we can download the required Telerik Blazor packages via NuGet. |
|||
|
|||
 |
|||
|
|||
### Step 1 (Configurations) |
|||
|
|||
* We need to install the `Telerik.UI.for.Blazor` Nuget package to our Blazor project (`*.Blazor`). We need to choose package source to **telerik.com** for Visual Studio to see this package. |
|||
|
|||
* If you use trial version of Telerik, you can download **Telerik.UI.for.Blazor.Trial** package via NuGet. |
|||
|
|||
* After the installation finished, we need to open **index.html** (it's under *wwwroot* folder) to add css and js files in our application. |
|||
|
|||
* Add the following lines just before the closing head tag (**/head**). |
|||
|
|||
```html |
|||
... |
|||
<link rel="stylesheet" href="https:unpkg.com/@progress/kendo-theme-default@latest/dist/all.css" /> |
|||
<script src="_content/Telerik.UI.for.Blazor/js/telerik-blazor.js" defer></script> |
|||
<!-- For Trial licenses use |
|||
<script src="_content/Telerik.UI.for.Blazor.Trial/js/telerik-blazor.js" defer></script> |
|||
--> |
|||
</head> |
|||
``` |
|||
|
|||
* After that, we need to add the Telerik Blazor Components to our application's service collection. So just open the `TelerikComponentsBlazorModule` and update the `ConfigureServices` method with the following content. |
|||
|
|||
```csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var environment = context.Services.GetSingletonInstance<IWebAssemblyHostEnvironment>(); |
|||
var builder = context.Services.GetSingletonInstance<WebAssemblyHostBuilder>(); |
|||
|
|||
ConfigureAuthentication(builder); |
|||
ConfigureHttpClient(context, environment); |
|||
ConfigureBlazorise(context); |
|||
ConfigureRouter(context); |
|||
ConfigureUI(builder); |
|||
ConfigureMenu(context); |
|||
ConfigureAutoMapper(context); |
|||
|
|||
//add this line to be able to use the components |
|||
builder.Services.AddTelerikBlazor(); |
|||
} |
|||
``` |
|||
|
|||
* After the service added we will continue by opening **_Imports.razor** file and add the global using statements as below. This will bring our Telerik components into scope throughout the application. |
|||
|
|||
 |
|||
|
|||
* After all of these steps, the Telerik UI Components are ready to be used anywhere in our application. We can use the Telerik Blazor Components by wrapping our components or pages between `<TelerikRootComponent>` and `</TelerikRootComponent>` tags. |
|||
|
|||
### Step 2 - Checking Configurations |
|||
|
|||
* We should check, have we done the right configurations or not. For that, we can open the **Index.razor** file and we can update the component with the following content. |
|||
|
|||
```razor |
|||
@page "/" |
|||
@using Volo.Abp.MultiTenancy |
|||
@inherits TelerikComponentsComponentBase |
|||
@inject ICurrentTenant CurrentTenant |
|||
@inject AuthenticationStateProvider AuthenticationStateProvider |
|||
@using System.Timers |
|||
|
|||
@implements IDisposable |
|||
|
|||
<TelerikRootComponent> |
|||
<div class="progress-bar-wrapper"> |
|||
<h5 class="progress-info-title">Telerik Progress Bar Component</h5> |
|||
<TelerikProgressBar Value="@ProgressValue" Max="100"></TelerikProgressBar> |
|||
</div> |
|||
</TelerikRootComponent> |
|||
|
|||
@code { |
|||
private const int TimerInterval = 1000; |
|||
private const int TotalTime = 10 * TimerInterval; |
|||
private double ProgressValue = 0; |
|||
private int ProgressStep = 100 / (TotalTime / TimerInterval); |
|||
private Timer Timer { get; set; } = new Timer(); |
|||
|
|||
private void Dispose() |
|||
{ |
|||
StopProgress(); |
|||
Timer?.Close(); |
|||
} |
|||
|
|||
protected override void OnAfterRender(bool firstRender) |
|||
{ |
|||
if (Timer.Enabled == false) |
|||
{ |
|||
Timer.Interval = TimerInterval; |
|||
Timer.Elapsed -= OnTimerElapsed; |
|||
Timer.Elapsed += OnTimerElapsed; |
|||
Timer.AutoReset = true; |
|||
Timer.Start(); |
|||
} |
|||
} |
|||
|
|||
private void OnTimerElapsed(Object source, ElapsedEventArgs e) |
|||
{ |
|||
if (ProgressValue < 100) |
|||
{ |
|||
UpdateProgress(); |
|||
} |
|||
else |
|||
{ |
|||
StopProgress(); |
|||
} |
|||
} |
|||
|
|||
private void UpdateProgress() |
|||
{ |
|||
ProgressValue += ProgressStep; |
|||
|
|||
InvokeAsync(StateHasChanged); |
|||
} |
|||
|
|||
private void StopProgress() |
|||
{ |
|||
Timer?.Stop(); |
|||
} |
|||
} |
|||
|
|||
<style> |
|||
.progress-info-title { |
|||
font-weight: bold; |
|||
font-size: 1.4em; |
|||
} |
|||
</style> |
|||
``` |
|||
|
|||
* In here, we've just added the `TelerikProgressBar` component to check the integration configured properly. |
|||
|
|||
* When we run `*.HttpApi.Host` and `*.Blazor` projects, we should see that the `TelerikProgressBar` component works and has similar view as the below gif. |
|||
|
|||
 |
|||
|
|||
* If you haven't seen this component like above, you should check the above configurations and be assure every step done as stated. |
|||
|
|||
### Step 3 - Using The Telerik Blazor Components (Sample Application) |
|||
|
|||
* Let's create a sample application for use other Telerik Blazor Components (like DataGrid). |
|||
|
|||
* We will use [jsonplaceholder](https://jsonplaceholder.typicode.com/) as **mock data** to the listing, adding, updating and deleting posts. |
|||
|
|||
* Firstly, we can create a folder named `Posts` and inside this folder, we can create the classes which are highlighted in the following screenshot. |
|||
|
|||
 |
|||
|
|||
* After classes created we can fill the classes with the following contents. |
|||
|
|||
**Post.cs** |
|||
```csharp |
|||
using System; |
|||
|
|||
namespace TelerikComponents.Posts |
|||
{ |
|||
[Serializable] |
|||
public class Post |
|||
{ |
|||
public int Id { get; set; } |
|||
|
|||
public string Title { get; set; } |
|||
|
|||
public string Body { get; set; } |
|||
|
|||
public int UserId { get; set; } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**Comment.cs** |
|||
```csharp |
|||
using System; |
|||
|
|||
namespace TelerikComponents.Posts |
|||
{ |
|||
[Serializable] |
|||
public class Comment |
|||
{ |
|||
public int PostId { get; set; } |
|||
|
|||
public int Id { get; set; } |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
public string Email { get; set; } |
|||
|
|||
public string Body { get; set; } |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**IPostAppService.cs** |
|||
```csharp |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace TelerikComponents.Posts |
|||
{ |
|||
public interface IPostAppService : IApplicationService |
|||
{ |
|||
Task<List<Post>> GetPostsAsync(); |
|||
|
|||
Task<Post> AddPostAsync(Post post); |
|||
|
|||
Task<Post> UpdatePostAsync(int postId, Post post); |
|||
|
|||
Task DeletePostAsync(int postId); |
|||
|
|||
Task<Comment> GetFirstCommentByPostIdAsync(int postId); |
|||
} |
|||
} |
|||
``` |
|||
* In here, we basically created two class (which are **Post** and **Comment**). These classes are used to hold data returned as JSON. |
|||
|
|||
* After that, we need to implement `IPostAppService`. For achieve this, we can create a folder named `Posts` in **\*.Application** layer and inside this folder we can create a class named `PostAppService` with the following content. |
|||
|
|||
**PostAppService.cs** |
|||
```csharp |
|||
using System.Collections.Generic; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace TelerikComponents.Posts |
|||
{ |
|||
public class PostAppService : ApplicationService, IPostAppService |
|||
{ |
|||
private JsonSerializerOptions _options = new JsonSerializerOptions |
|||
{ |
|||
IncludeFields = true, |
|||
PropertyNameCaseInsensitive = true |
|||
}; |
|||
|
|||
public async Task<List<Post>> GetPostsAsync() |
|||
{ |
|||
var url = "https://jsonplaceholder.typicode.com/posts"; |
|||
List<Post> _posts = new List<Post>(); |
|||
|
|||
using (var client = new HttpClient()) |
|||
{ |
|||
var result = await client.GetAsync(url); |
|||
|
|||
if (result.IsSuccessStatusCode) |
|||
{ |
|||
var content = await result.Content.ReadAsStringAsync(); |
|||
var deserializedPosts = JsonSerializer.Deserialize<List<Post>>(content, _options); |
|||
|
|||
_posts = deserializedPosts; |
|||
} |
|||
} |
|||
|
|||
return _posts; |
|||
} |
|||
|
|||
public async Task<Post> AddPostAsync(Post post) |
|||
{ |
|||
var url = "https://jsonplaceholder.typicode.com/posts"; |
|||
Post addedPost = null; |
|||
|
|||
using (var client = new HttpClient()) |
|||
{ |
|||
var serializePost = JsonSerializer.Serialize(post); |
|||
var content = new StringContent(serializePost, Encoding.UTF8, "application/json"); |
|||
var result = await client.PostAsync(url, content); |
|||
|
|||
if (result.IsSuccessStatusCode) |
|||
{ |
|||
var response = await result.Content.ReadAsStringAsync(); |
|||
addedPost = JsonSerializer.Deserialize<Post>(response); |
|||
} |
|||
} |
|||
|
|||
return addedPost; |
|||
} |
|||
|
|||
public async Task<Post> UpdatePostAsync(int postId, Post post) |
|||
{ |
|||
var url = $"https://jsonplaceholder.typicode.com/posts/{postId}"; |
|||
Post updatedPost = null; |
|||
|
|||
using (var client = new HttpClient()) |
|||
{ |
|||
var serializePost = JsonSerializer.Serialize(post); |
|||
var content = new StringContent(serializePost, Encoding.UTF8, "application/json"); |
|||
var result = await client.PutAsync(url, content); |
|||
|
|||
if (result.IsSuccessStatusCode) |
|||
{ |
|||
var response = await result.Content.ReadAsStringAsync(); |
|||
updatedPost = JsonSerializer.Deserialize<Post>(response); |
|||
} |
|||
} |
|||
|
|||
return updatedPost; |
|||
} |
|||
|
|||
public async Task DeletePostAsync(int postId) |
|||
{ |
|||
var url = $"https://jsonplaceholder.typicode.com/posts/{postId}"; |
|||
|
|||
using (var client = new HttpClient()) |
|||
{ |
|||
await client.DeleteAsync(url); |
|||
} |
|||
} |
|||
|
|||
public async Task<Comment> GetFirstCommentByPostIdAsync(int postId) |
|||
{ |
|||
var url = $"https://jsonplaceholder.typicode.com/posts/{postId}/comments"; |
|||
|
|||
List<Comment> _comments = new List<Comment>(); |
|||
|
|||
using (var client = new HttpClient()) |
|||
{ |
|||
var result = await client.GetAsync(url); |
|||
|
|||
if (result.IsSuccessStatusCode) |
|||
{ |
|||
var content = await result.Content.ReadAsStringAsync(); |
|||
var deserializedPosts = JsonSerializer.Deserialize<List<Comment>>(content, _options); |
|||
|
|||
_comments = deserializedPosts; |
|||
} |
|||
} |
|||
|
|||
return _comments[0]; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
* In here, we've implemented `IPostAppService` methods by using [jsonplaceholder](https://jsonplaceholder.typicode.co) API. These endpoints provide us basic crud functionallity. |
|||
|
|||
* After the implemenation, we can start to create the user interface. |
|||
|
|||
#### Blazor UI |
|||
|
|||
* We can create **/Posts** page for listing, updating, deleting and creating our posts. So, create a razor page named `Posts.razor` under **Pages** folder in `*.Blazor` project. |
|||
|
|||
**Posts.razor** |
|||
```razor |
|||
@page "/Posts" |
|||
@using TelerikComponents.Posts |
|||
@using IconName = Telerik.Blazor.IconName |
|||
@inject IPostAppService PostAppService |
|||
|
|||
<h3>Posts</h3> |
|||
|
|||
<TelerikRootComponent> |
|||
<TelerikGrid Data="@GridData" |
|||
OnUpdate="@UpdateHandler" |
|||
OnDelete="@DeleteHandler" |
|||
OnCreate="@CreateHandler" |
|||
@ref="@Grid" |
|||
Pageable="true" |
|||
Groupable="true" |
|||
Sortable="true" |
|||
FilterMode="GridFilterMode.FilterMenu" |
|||
Resizable="true" |
|||
Reorderable="true" |
|||
EditMode="GridEditMode.Popup" |
|||
SelectionMode="GridSelectionMode.Single" |
|||
PageSize="5" |
|||
Navigable="true"> |
|||
<GridColumns> |
|||
<GridColumn Field="@nameof(Post.Title)"/> |
|||
<GridColumn Field="@nameof(Post.Body)"/> |
|||
<GridCommandColumn Width="190px"> |
|||
<GridCommandButton Command="Save" Icon="save" ShowInEdit="true">Update</GridCommandButton> |
|||
<GridCommandButton Command="Edit" Icon="edit">Edit</GridCommandButton> |
|||
<GridCommandButton Command="Delete" Icon="delete" Primary="true">Delete</GridCommandButton> |
|||
<GridCommandButton Icon="@IconName.Window" OnClick="(e) => PostDetailAsync(e)">Display Comment</GridCommandButton> |
|||
<GridCommandButton Command="Cancel" Icon="cancel" ShowInEdit="true">Cancel</GridCommandButton> |
|||
</GridCommandColumn> |
|||
</GridColumns> |
|||
<GridToolBar> |
|||
<GridCommandButton Command="Add" Icon="add">Add Post</GridCommandButton> |
|||
</GridToolBar> |
|||
</TelerikGrid> |
|||
|
|||
@* Modal *@ |
|||
<TelerikWindow Class="demo-window" Width="500px" Height="250px" Centered="true" @bind-Visible=@ModalVisible Modal="true"> |
|||
<WindowTitle> |
|||
<strong>Comment</strong> |
|||
</WindowTitle> |
|||
<WindowActions> |
|||
<WindowAction Name="Close" /> |
|||
</WindowActions> |
|||
<WindowContent> |
|||
<p><b>Email:</b> @Comment.Email</p> |
|||
<p> |
|||
<b>Message:</b> @Comment.Body |
|||
</p> |
|||
</WindowContent> |
|||
</TelerikWindow> |
|||
</TelerikRootComponent> |
|||
``` |
|||
|
|||
**Post.razor.cs** |
|||
```csharp |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Telerik.Blazor.Components; |
|||
using TelerikComponents.Posts; |
|||
|
|||
namespace TelerikComponents.Blazor.Pages |
|||
{ |
|||
public partial class Posts |
|||
{ |
|||
private List<Post> GridData { get; set; } |
|||
private TelerikGrid<Post> Grid { get; set; } |
|||
private bool ModalVisible { get; set; } = false; |
|||
private Comment Comment { get; set; } |
|||
|
|||
public Posts() |
|||
{ |
|||
Comment = new Comment(); |
|||
} |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
await LoadDataAsync(); |
|||
} |
|||
|
|||
private async Task LoadDataAsync() |
|||
{ |
|||
GridData = await PostAppService.GetPostsAsync(); |
|||
} |
|||
|
|||
private async Task UpdateHandler(GridCommandEventArgs args) |
|||
{ |
|||
var post = (Post) args.Item; |
|||
|
|||
await PostAppService.UpdatePostAsync(post.Id, post); |
|||
|
|||
var matchingPost = GridData.FirstOrDefault(x => x.Id == post.Id); |
|||
|
|||
if (matchingPost != null) |
|||
{ |
|||
matchingPost.Body = post.Body; |
|||
matchingPost.Title = post.Title; |
|||
} |
|||
} |
|||
|
|||
private async Task DeleteHandler(GridCommandEventArgs args) |
|||
{ |
|||
var post = (Post) args.Item; |
|||
|
|||
GridData.Remove(post); |
|||
} |
|||
|
|||
private async Task CreateHandler(GridCommandEventArgs args) |
|||
{ |
|||
var post = (Post) args.Item; |
|||
|
|||
var addedPost = await PostAppService.AddPostAsync(post); |
|||
|
|||
GridData.Insert(0, addedPost); |
|||
} |
|||
|
|||
private async Task PostDetailAsync(GridCommandEventArgs args) |
|||
{ |
|||
var post = (Post) args.Item; |
|||
|
|||
Comment = await PostAppService.GetFirstCommentByPostIdAsync(post.Id); |
|||
|
|||
ModalVisible = true; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
* In here, we've used `TelerikGrid` component. |
|||
|
|||
* The `Telerik Grid` is a powerful component, which allows you to visualize and edit data via its table representation. It provides a variety of options about how to present and perform operations over the underlying data, such as paging, sorting, filtering and editing. |
|||
|
|||
* The Blazor UI Grid allows flexible customization of its items exposing rows, columns and edit templates for this purpose. |
|||
|
|||
### Final Result |
|||
|
|||
* After all of these steps, we can finally run our application. |
|||
* Run `*.HttpApi.Host` project for use the required endpoints, |
|||
* Run `*.Blazor` project for see the Blazor UI. |
|||
|
|||
* When we navigate to `Posts` route, we should see the following screenshot in this page. |
|||
|
|||
 |
|||
|
|||
## Conclusion |
|||
|
|||
In this article, I've tried to explain how we can integrate [Telerik Blazor Component](https://www.telerik.com/blazor-ui) to our Blazor UI. ABP Framework designed as modular, so that it can work with any UI library/framework. |
|||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 4.1 MiB |
|
After Width: | Height: | Size: 4.7 MiB |
|
After Width: | Height: | Size: 3.5 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 870 KiB |
@ -0,0 +1,25 @@ |
|||
# Blazor UI: SubmitButton component |
|||
|
|||
`SubmitButton` is a simple wrapper around `Button` component. It is used to be placed inside of page Form or Modal dialogs where it can response to user actions and to be activated as a default button by pressing an ENTER key. Once clicked it will go into the `disabled` state and also it will show a small loading indicator until clicked event is finished. |
|||
|
|||
## Quick Example |
|||
|
|||
```html |
|||
<SubmitButton Clicked="@YourSaveOperation" /> |
|||
``` |
|||
|
|||
Notice that we didn't specify any text, like `Save Changes`. This is because `SubmitButton` will by default pull text from the localization. If you want to change that you either specify a localization key or you can add custom content. |
|||
|
|||
### With localization key |
|||
|
|||
```html |
|||
<SubmitButton Clicked="@YourSaveOperation" SaveResourceKey="YourSaveName" /> |
|||
``` |
|||
|
|||
### With custom content |
|||
|
|||
```html |
|||
<SubmitButton Clicked="@YourSaveOperation"> |
|||
@L["Save"] |
|||
</SubmitButton> |
|||
``` |
|||
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 20 KiB |