Good afternoon. Please tell me where I can find more detailed documentation on writing a custom Angular component  for Freedom UI to work with a list of data obtained by querying the database using set filters, calling and handling the necessary events.

Like 0

Like

3 comments

(Weird, sometimes replies in the community forums don't show up until I make a second comment)

Раян Фарлі,

Thanks a lot Ryan!

Show all comments

Hi Creatio Community!
I’m building a custom Freedom UI component and need some advice on how to pass data from the component back to the parent page.
I have a custom component with some internal logic and a variable whose value changes based on user interaction inside the component (e.g. clicks, selections).
Now I want to pass this variable's value back to the parent page where the component is used — for example, to trigger some business logic or store it in a page field.
Is there a way to work with the page context from within the custom component?
Or maybe there's a recommended pattern for passing data upward from a component to the parent page in Freedom UI?

Any examples or best practices would be greatly appreciated!

Thanks in advance

Like 2

Like

0 comments
Show all comments

Hi Creatio Community!
I’m currently working on a custom Angular component (Freedom UI) and ran into an issue I hope someone has already dealt with. I created a custom table component that loads data from a web service — the structure is quite complex with nested records, so I chose to use a service instead of standard object bindings.

I'm receiving the current record ID like this:

@Input()
@CrtInput()
public recordId!: { value: string; displayValue: string };

This is how I set recordId into component

{
	"operation": "insert",
	"name": "CustomTable",
	"values": {
		"recordId": "$UsrContractConditionsGroupDS_UsrContract_c00dfo4",
		"type": "usr.Table"
	},
	"parentName": "ListOfTarrifGroupByPeriod_ExpansionPanel",
	"propertyName": "items",
	"index": 0
},

When I open the page for the first time, everything works fine — the recordId arrives as expected. 

But when I back to section and open this record again, the component throws an error because recordId is undefined, and my table is empty.

I’m calling the service directly in ngOnInit().

Could you please advise on the correct way to store the record ID in the component so that it doesn’t get lost when opening the page a second time?

 

Like 1

Like

2 comments
Best reply

Hello Artem,

 

You can store record ID in the custom attribute and populate its value when the page is opening in the request crt.HandleViewModelInitRequest.

 

viewModelConfigDiff: /**SCHEMA_VIEW_MODEL_CONFIG_DIFF*/[
	{
		"operation": "merge",
		"path": [
			"attributes"
		],
		"values": {
			"RecordId": {}
			...
		}
	}
	...
]
handlers: [
	{
		request: "crt.HandleViewModelInitRequest",
		handler: async (request, next) => {
			await next?.handle(request);
			request.$context.RecordId = request.$context.Id;
		}
	}
]

Then you can use this custom attribute in your component

 

{
	"operation": "insert",
	"name": "CustomTable",
	"values": {
		"recordId": "$RecordId",
		"type": "usr.Table"
	},
	"parentName": "ListOfTarrifGroupByPeriod_ExpansionPanel",
	"propertyName": "items",
	"index": 0
},

Hello Artem,

 

You can store record ID in the custom attribute and populate its value when the page is opening in the request crt.HandleViewModelInitRequest.

 

viewModelConfigDiff: /**SCHEMA_VIEW_MODEL_CONFIG_DIFF*/[
	{
		"operation": "merge",
		"path": [
			"attributes"
		],
		"values": {
			"RecordId": {}
			...
		}
	}
	...
]
handlers: [
	{
		request: "crt.HandleViewModelInitRequest",
		handler: async (request, next) => {
			await next?.handle(request);
			request.$context.RecordId = request.$context.Id;
		}
	}
]

Then you can use this custom attribute in your component

 

{
	"operation": "insert",
	"name": "CustomTable",
	"values": {
		"recordId": "$RecordId",
		"type": "usr.Table"
	},
	"parentName": "ListOfTarrifGroupByPeriod_ExpansionPanel",
	"propertyName": "items",
	"index": 0
},

Iryna Oriyenko,

Thank you very much!

Show all comments

Hello community

I am encountering an issue when using the Clio utility to create a custom Angular module. When attempting to configure the workspace, I am facing errors, and I would appreciate your assistance in resolving them.

The errors I receive are as follows:

 

When running the command clio dconf -e dev-env:

[WAR] - Downloading of cor libraries requires cliogate version 2.0.0.0 or higher.

 

When running the command clio restorew -e dev-env:

[ERR] - To use this command, you need to install the cliogate package version 2.0.0.0 or higher.

However, when I run the command clio ver, the output shows:

 

clio:   8.0.1.21

[INF] - gate:   2.0.0.29

dotnet:   8.0.14

 

My connection to dev-env is stable, and I can see the list of packages

It appears that I have the correct version of the cliogate package installed (2.0.0.29), but I am still facing issues. 

Has anyone encountered a similar situation?

What could be causing this behavior in Clio?

Are there perhaps some configuration nuances that I'm missing?

Unfortunately, I haven’t been able to find any solutions or descriptions of similar errors online. Thank you in advance for any ideas or recommendations!

Like 1

Like

9 comments
Best reply

Artem Ivzhenko,

Hello, the issue was resolved in the clio version 8.0.1.27. Thanks for your attention. In this version, we fixed the situation when the config file URL uses a form like 'HTTP(s): my-creatoio.com/' with '/' in the end.

The command "clio ver" shows the version of the Clio command line itself. The error is mentioning the version of cliogate, which is a package installed on the Creatio system. 

To check the version of cliogate, use: 

clio ver --gate -e dev-env

To install or update the cliogate on the system, use: 

clio install-gate -e dev-env

Ryan

Sorry, missed that you mentioned the version of cliogate is up to date already. I've not experienced that issue. 

It seems there might be an issue with the connection between a clio and your dev. Try installing the gate again:

clio install-gate some_application_name

And then restart the dev:
clio restart some_application_name

 

This message only appears when dev-env does not have cliogate package installed,m  you can validate it with  `clio packages -f cliogate`. 

You should see something like this. If nothing comes back, then you need to reinstall cliogate. 

Use `clio gate -e dev-env` to reinstall cliogate

Dmytro Vovchenko,

I got an exception on this:


clio install-gate -e dev-env
Uploading...
   at System.Net.HttpWebRequest.GetResponse()
   at Creatio.Client.CreatioClient.Login(Int32 requestTimeout)
   at Creatio.Client.CreatioClient.InitAuthCookie(Int32 requestTimeout)
   at Creatio.Client.CreatioClient.get_AuthCookie()
   at Creatio.Client.CreatioClient.CreateCreatioRequest(String url, String requestData, Int32 requestTimeout)
   at Creatio.Client.CreatioClient.UploadFile(String url, String filePath, Int32 defaultTimeout)
   at Clio.Common.CreatioClientAdapter.UploadFile(String url, String filePath)
   at Clio.Package.BasePackageInstaller.UploadPackage(String filePath, EnvironmentSettings environmentSettings)
   at Clio.Package.BasePackageInstaller.InstallPackedPackage(String filePath, EnvironmentSettings environmentSettings, PackageInstallOptions packageInstallOptions)
   at Clio.Package.BasePackageInstaller.InstallPackage(String packagePackedFileOrFolderPath, EnvironmentSettings environmentSettings, PackageInstallOptions packageInstallOptions)
   at Clio.Package.BasePackageInstaller.InternalInstall(String packagePath, EnvironmentSettings environmentSettings, PackageInstallOptions packageInstallOptions, String reportPath)
   at Clio.Package.PackageInstaller.Install(String packagePath, EnvironmentSettings environmentSettings, PackageInstallOptions packageInstallOptions, String reportPath)
   at Clio.Command.PushPackageCommand.Execute(PushPkgOptions options)

Artem Ivzhenko,

according to the trace of the error the request to login either timed out or returned an error. This might be also related to the fact that dev apps are shut down automatically in case there is no activity in the dev app for the past 60 minutes. Try the following steps:

  1. Open the login page of the dev app in the UI and make sure it's loaded.
  2. Wait for 1-2 minutes (for the app to completely initialize after starting).
  3. Try executing the cliogate install once again.

Please let us know about the result.

Oleg Drobina,

I followed the steps you outlined above and I still get the same error.
I don't understand why a connection error occurs, especially since the ping command returns:
[INF] - Done ping-app.
At the same time, other developers connected to the same environment are working without any issues.
I am not connected to a VPN or proxy server.

Oleg Drobina,

In my understanding, a timeout error should not occur immediately after running the command, but rather after some time has passed, since a timeout cannot happen within just one second.
In my case, I receive the error right after launching the command, without any delay.

Artem Ivzhenko,

Hello, the issue was resolved in the clio version 8.0.1.27. Thanks for your attention. In this version, we fixed the situation when the config file URL uses a form like 'HTTP(s): my-creatoio.com/' with '/' in the end.

Show all comments

Hi,

I'm developing my own custom Angular component for Creatio. I would like to use some of the standard components within it, such as a lookup field. How can I achieve this?

Is it possible to create a custom component that acts as a container for other elements, similar to crt.FlexContainer, which defines an items property for child components?

 

Like 1

Like

1 comments

Hello Eryk,

Unfortunately, it's impossible to reuse components in custom angular components for now, but Creatio plans to add such functionality in future releases.

Show all comments

Cheers to all.

I'm developing my custom UI component in Angular and I need to get some data from Creatio database inside of my component. I discovered there is EntitySchemaQuery class in Creatio's sdk, so I decided to use it, but I'm stuck when I need to retreive data with that ESQ as I didn't find method for that. In classic ESQ there was getEntityCollection method for that. Could you please suggest how do I retrieve data from the database? Either using ESQ or other class, but user's authorization matters.

Here is the code of component.

import { Component, OnInit, Input, Output, 
        ViewEncapsulation, EventEmitter, SimpleChanges  } from '@angular/core';
import { CrtViewElement, CrtInput, CrtOutput, EntitySchemaQuery,
          ComparisonType,  AggregationType, AggregationEvalType, isGuid} from '@creatio-devkit/common';
 
@Component({
  selector: 'usr-input',
  templateUrl: './input.component.html',
  styleUrls: ['./input.component.scss'],
  encapsulation: ViewEncapsulation.ShadowDom
})
/* Add the CrtViewElement decorator to the InputComponent component. */
@CrtViewElement({
  selector: 'usr-input',
  type: 'usr.Input'
})
export class InputComponent implements OnInit {
  constructor() {}
 
  @Input("recordId")
  @CrtInput()
  /* The input recordId. */
  public recordId!: string;
 
  /* Add decorators to the EventEmitter<string>() event. */
  @Output()
  @CrtOutput()
  /* Track input value changes. */
  public valueChange = new EventEmitter<string>();
 
  ngOnInit(): void {
  }
 
 
  ngOnChanges(changes: SimpleChanges) {
    console.log(changes);
 
    if (isGuid(changes["recordId"]?.currentValue)) {
      let esq = new EntitySchemaQuery("ClvObject");
      esq.addAggregationFunctionColumn("Id", AggregationType.Count, "AppCount", AggregationEvalType.All);
      esq.filters.addSchemaColumnFilterWithParameter(ComparisonType.Equal, "ClvProject", this.recordId, "currentFilter");
 
      let record = esq.getMetadata();
      console.log(record);
    }
  }
}

 

Like 2

Like

3 comments

The EntitySchemaQuery classes in the devkit don't do anything, at least not how it is exposed. The ESQ classes require an executor class that actually *runs* the query and provides the connection. We don't have access to this, the executor that runs the ESQ is not part of the devkit sdk. This is intentional/by design since they want us to use the new Model classes instead. This better anyways, the Model class is far easier and more intuitive IMO.

The intended way to get data now is the Model class, which is in the devkit sdk. Here's some articles on the topic: 

Model Query Using Filters: https://customerfx.com/article/querying-data-using-filter-conditions-vi…

Model Query for a Single Record Given it's Id: https://customerfx.com/article/retrieving-a-record-via-the-model-class-…

Also, the model class does inserts/updates/deletes:

Inserting a Record: https://customerfx.com/article/inserting-a-record-from-client-side-code…

Updating a Record: https://customerfx.com/article/updating-a-record-from-client-side-code-…

Deleting a Record: https://customerfx.com/article/deleting-a-record-from-client-side-code-…

Copying a Record: https://customerfx.com/article/copying-a-record-from-client-side-code-u…

You can also use the Model class to get an object's schema:

Get Object Schema: https://customerfx.com/article/getting-an-object-schema-using-the-model…

Ryan

Hi, Ryan. Thanks for quick reply.

Maybe you could suggest how do I need to prepare aggregate columns for the query using Model class? I tried it as described below

  async ngOnChanges(changes: SimpleChanges) {
    console.log(changes);
 
    if (isGuid(changes["recordId"]?.currentValue)) {
      const dataModel = await Model.create("ClvObject");
      const filters = new FilterGroup();
      filters.addSchemaColumnFilterWithParameter(ComparisonType.Equal, "ClvProject", this.recordId, "currentFilter");
      const records = await dataModel.load({
//        attributes: ["Id", "ClvName", "ClvType", "ClvProject.ClvCommissioning"],
        attributes: [{ 
          aggregationConfig: {
            aggregationFunction: AggregationFunction.Count,
          },
          type: "aggregation",
          path: "Id",
          name: "AppCount",
          caption: "AppCountCaption",
          dataValueType: DataValueType.Integer
        }],
        parameters: [{
            type: ModelParameterType.Filter,
            value: filters
        }]
      });
      console.log(records);
 
    }
  }

But instead of getting one record with one column "AppCount" containig 2, I get two records each containg "Id" and "AppCount" 

Ok< I found out the way. Looks like aggregationConfig isn't yet working well and functionConfig should be used instead. Here is working exampe.

  async ngOnChanges(changes: SimpleChanges) {
    console.log(changes);
 
    if (isGuid(changes["recordId"]?.currentValue)) {
 
      const dataModel = await Model.create("ClvObject");
      const filters = new FilterGroup();
      filters.addSchemaColumnFilterWithParameter(ComparisonType.Equal, "ClvProject", this.recordId, "currentFilter");
      const records = await dataModel.load({
//        attributes: ["Id", "ClvName", "ClvType", "ClvProject.ClvCommissioning"],
        attributes: [
          { 
            type: "function",
            path: "Id",
            name: "AppCount",
            caption: "AppCountCaption",
            dataValueType: DataValueType.Integer,
            functionConfig: {
              aggregation: AggregationFunction.Count,
              type: "aggregation",
              aggregationEval: "all",
            }
          }],
          parameters: [{
              type: ModelParameterType.Filter,
              value: filters
          }
        ]
      });
      console.log(records);
 
    }
  }

And result

Show all comments

Hi,



I have been working on adding new custom components using the new freedom UI following the below Creatio article.

https://academy.creatio.com/docs/developer/front_end_development_freedom_ui/remote_module/implement_a_remote_module/overview



After doing some additional research I was able to add some custom components successfully. But I'm kind of stuck on where I need to pass the data from Creatio's side to that specific angular component. Please refer to the below screenshots of my code snippets and if anyone has any idea on what I'm missing can you guide me or provide some resources to get more idea on this?

 

Below is the "card.component.html" file:

<mat-card class="card-class" *ngFor="let val of valuelist" >
    <mat-card-header></mat-card-header>
    <mat-card-content class="content-wrapper">
        <div fxFill fxLayout="row">
            <div fxFlex="30" fxLayout="space-around center">
                <div fxFlex fxLayoutAlign>
                    <img mat-card-sm-image src="https://material.angular.io/assets/img/examples/shiba2.jpg" alt="Free Image" class="responsive-image"  >
                </div>
                <div fxFlex fxLayoutAlign>
                    <img mat-card-sm-image src="https://material.angular.io/assets/img/examples/shiba2.jpg" alt="Free Image" class="responsive-image" >
                </div>
            </div>
            <div fxFlex="55" fxLayout="column" fxLayoutAlign="center start" class="contentN" >
                <mat-card-title>{{val}}</mat-card-title>
                <mat-card-subtitle>{{val}}</mat-card-subtitle>
            </div>
            <div fxFlex="15" fxLayout fxLayoutAlign="center center" class="contentP">
                <img mat-card-sm-image src="https://material.angular.io/assets/img/examples/shiba2.jpg" alt="Free Image" class="responsive-image" >
            </div>
        </div>
    </mat-card-content>
    <mat-card-footer></mat-card-footer>
</mat-card>

 As in the above, from using ngFor I'm adding cards based on the data which are coming from the "valuelist". Below is the example of the "card.component.ts" file:



 

import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { CrtInput, CrtOutput,CrtInterfaceDesignerItem, CrtViewElement } from '@creatio-devkit/common';
 
 
@Component({
  selector: 'mlcgd-card',
  templateUrl: './card.component.html',
  styleUrls: ['./card.component.scss'],
  //encapsulation: ViewEncapsulation.ShadowDom
})
 
/* Add the CrtViewElement decorator to the Textpromt component. */
@CrtViewElement({
  selector: 'mlcgd-card',
  type: 'mlcgd.Card'
})
 
/* Add the CrtViewElement decorator to the InputComponent component. */
@CrtInterfaceDesignerItem({
  /* Manage the element layout in the library of the Freedom UI Designer. */
  toolbarConfig: {
    caption: 'Card View',
    name: 'CardView',
    /* The path to the component image. */
    icon: require('!!raw-loader?{esModule:false}!./icon.svg'),
    defaultPropertyValues: {
      label: 'Card View'
    }
  }
})
 
export class CardComponent {
 
  valuelist:string[] = [];
 
  constructor() {}
 
  @Input()
  @CrtInput()
  /* ...............The input value. */
  public value: string = 'Lakindu, Chinthana, Deshan';
 
  @Output()
  @CrtOutput()
  /* ................Track input value changes. */
  public valueChange = new EventEmitter<string>();
 
  ngOnInit(): void {
    this.valuelist = this.value.split(',')
  }
}

As you can see in the above code snippet I have a string called "value" and I have hardcoded 3 values for that. What I need to achieve is without hardcoding the data I need to get those data from the Creatio's end like from a field. So to do that I need to modify the code which is gonna add to the viewconfig array in the client schema. Please refer to the below Code snippet. And I need help with modifying it accordingly to pass the data from Creatio to the component.

 

{
				"operation": "insert",
				"name": "Card_hs3upn6",
				"values": {
					"type": "mlcgd.Card",
					"label": "Card View",
					"value": "$MLCGDName"
				},
				"parentName": "FlexContainer_08awxs2",
				"propertyName": "items",
				"index": 0
			}

Kind Regards,

Lakindu

Like 4

Like

5 comments

Hello,

You can find an example of working with the field value in these articles.

https://academy.creatio.com/docs/developer/front_end_development_freedo…

https://academy.creatio.com/docs/developer/front_end_development_freedo…

They described information on how to create a validator/converter for the value in a field.

Hello,



Thanks for sharing this but I'm afraid this might not be the one that I'm looking for. Anyways do you have anything related to dashboards? Like adding a new dashboard element using a remote module?



I was able to add some components there but for the moment I am kind of stuck at passing the data to the created component using the remote module.

Unfortunately, no, we don't have such examples.

To be honest, the remote module is quite a fresh feature, which means that currently, it doesn't have much logic. If possible, it would be better to use other methods to add your business logic.

Lakindu Yasomith,

Maybe this article can help you:

https://academy.creatio.com/docs/developer/front_end_development_freedo…

 

If not, please let us know. Maybe you can describe your case in more detail for support@creatio.com.

Lakindu Yasomith,

Hi,

 

I'm also working on a custom component and have the same issue.

Did you find a solution to this?

 

Br,

Robert

Show all comments

I am working on a use case to use angular component inside creatio. I have followed this link .It seems like it is old documentation most of the js files mentioned in that link is not there in the angular package. Can anyone helo me on this 

TIA

Like 1

Like

1 comments

Hello Pavan,

 

Can you please describe this question a little bit in details? At which particular step do you face issues?

Show all comments

Hi,

I've created a remote angular module for Freedom UI, following the tutorial: https://academy.creatio.com/docs/developer/front_end_development_freedo…

I added my component to Freedom UI library. Thus, I'm able to drag'n'drop my component to page area.

The problem is that my component have some configuration params (a string and an integer), that I can't fill using the no-code designer. I have to do it manually, from source code.

I would like to add such configuration options to component to make it possible to configure a component instance in no-code way.

For example, when I create button I can change its label or action performed on click event. I would like to achieve something similar with my component.

I use Creatio 8.0.7

I will be really grateful for your answers :)

Eryk

 

Like 3

Like

6 comments

Could anyone help me? :)

Hi,

Can you please give more detail on these configuration params you are talking about? Some screenshots would be nice.

Dmytro Vovchenko,



Yes, sure :)

Some elements, like an input, have many configuration options:

 

And my component not:

 

So, I'd like to add similar configuration options to my custom component to reduce amount of coding while using custom angular components.

 

Hi,

Unfortunately for now the system does not support such no-code option.

However, already informed our developers about this option and they will work on this feature in future versions.

Thank you for helping us improve the system.

Hi, has anything changed since last time? :)

Hello,



After reviewing all the information, we inform you that at the moment the system still does not support this kind of no-code option. However, this particular request has been passed on to the responsible team for consideration and implementation in future releases. Task number: PR-29433.

Show all comments

Hello,

I followed this article to add Angular component, but failed to build it.

https://academy.creatio.com/documents/technic-sdk/7-16/creating-angular…

 

My window.ng.core.VERSION.major = 9 (Creation 7.16.3). So I tried @angular/cli@9. But it seems that ngx-build-plus doesn't support Angular 9 yet.

$ yarn build:my-app:externals
Unknown option: '--extra-webpack-config'
Unknown option: 'webpack.externals.js'
Unknown option: '--single-bundle'
error Command failed with exit code 1.

Thanks.

Like 0

Like

2 comments

I tried ngx-build-plus@~9 and it works now.

Van Ly,

 

Nice to know that! Yes, you are correct, Creatio uses Angular 9, so here is the package you can use for the 9th version: https://www.npmjs.com/package/ngx-build-plus/v/9.0.6

 

As well as, please make sure that you've indicated the proper build in the "angular.json" file. 

 

Regards,

Anastasiia

Show all comments