示例#1
0
        private static void WriteListViewModel(TypeScriptCodeBuilder b, ClassViewModel model)
        {
            string name = model.ViewModelClassName;

            string viewModelName = $"{model.ListViewModelClassName}ViewModel";
            string metadataName  = $"$metadata.{name}";

            using (b.Block($"export class {viewModelName} extends ListViewModel<$models.{name}, $apiClients.{name}ApiClient>"))
            {
                foreach (var method in model.ClientMethods.Where(m => m.IsStatic))
                {
                    string signature =
                        string.Concat(method.ClientParameters.Select(f => $", {f.Name}: {new VueType(f.Type).TsType("$models")} | null"));

                    // "item" or "list"
                    var transportTypeSlug = method.TransportType.ToString().Replace("Result", "").ToLower();

                    b.DocComment(method.Comment, true);
                    b.Line($"public {method.JsVariable} = this.$apiClient.$makeCaller(\"{transportTypeSlug}\", ");
                    b.Indented($"(c{signature}) => c.{method.JsVariable}({string.Join(", ", method.ClientParameters.Select(p => p.Name))}))");
                }

                b.Line();
                using (b.Block($"constructor()"))
                {
                    b.Line($"super({metadataName}, new $apiClients.{name}ApiClient())");
                }
            }
            b.Line();
        }
示例#2
0
        private static void WriteApiEndpointFunction(TypeScriptCodeBuilder b, ClassViewModel model, MethodViewModel method)
        {
            var    returnIsListResult = method.ReturnsListResult;
            string signature          =
                string.Concat(method.ClientParameters.Select(f => $"{f.Name}: {new VueType(f.Type).TsType("$models")} | null, "))
                + "$config?: AxiosRequestConfig";

            if (method.IsModelInstanceMethod)
            {
                signature = $"id: {new VueType(model.PrimaryKey.Type).TsType(null)}, " + signature;
            }

            using (b.Block($"public {method.JsVariable}({signature})"))
            {
                string resultType = method.TransportTypeGenericParameter.IsVoid
                    ? $"{method.TransportType}<void>"
                    : $"{method.TransportType}<{new VueType(method.TransportTypeGenericParameter).TsType("$models")}>";

                b.Line($"const $method = this.$metadata.methods.{method.JsVariable}");
                using (b.Block($"const $params = this.$mapParams($method,", ')'))
                {
                    if (method.IsModelInstanceMethod)
                    {
                        b.Line($"id,");
                    }
                    foreach (var param in method.ClientParameters)
                    {
                        b.Line($"{param.JsVariable},");
                    }
                }

                b.Line("return AxiosClient");
                using (b.Indented())
                {
                    b.Line($".{method.ApiActionHttpMethodName.ToLower()}(");
                    b.Indented($"`/${{this.$metadata.controllerRoute}}/{method.Name}`,");
                    switch (method.ApiActionHttpMethod)
                    {
                    case DataAnnotations.HttpMethod.Get:
                    case DataAnnotations.HttpMethod.Delete:
                        b.Indented($"this.$options(undefined, $config, $params)");
                        break;

                    default:
                        b.Indented($"qs.stringify($params),");
                        b.Indented($"this.$options(undefined, $config)");
                        break;
                    }
                    b.Line(")");

                    b.Line($".then<AxiosResponse<{resultType}>>(r => this.$hydrate{method.TransportType}(r, $method.return))");
                }
            }

            // Line between methods
            b.Line();
        }
示例#3
0
        private void WriteDataSources(TypeScriptCodeBuilder b)
        {
            var dataSources   = Model.ClientDataSources(Services.ReflectionRepository).ToList();
            var defaultSource = dataSources.SingleOrDefault(s => s.IsDefaultDataSource);

            using (b.Block($"export namespace {Model.ClientTypeName}DataSources"))
            {
                if (defaultSource == null)
                {
                    b.Line($"export class {DataSourceFactory.DefaultSourceName} extends Coalesce.DataSource<{ViewModelFullName}> {{ }}");
                }

                foreach (var source in dataSources)
                {
                    b.DocComment(source.Comment);
                    using (b.Block($"export class {source.ClientTypeName} extends Coalesce.DataSource<{ViewModelFullName}>"))
                    {
                        if (source.DataSourceParameters.Any())
                        {
                            foreach (PropertyViewModel prop in source.DataSourceParameters)
                            {
                                b.DocComment(prop.Comment);
                                b.Line($"public {prop.JsVariable}: {prop.Type.TsKnockoutType(true)} = {prop.Type.ObservableConstructorCall()};");
                            }
                            using (b.Block("public saveToDto = () =>"))
                            {
                                b.Line("var dto: any = {};");
                                foreach (PropertyViewModel prop in source.DataSourceParameters)
                                {
                                    if (prop.Type.IsDate)
                                    {
                                        b.Line($"if (!this.{prop.JsVariable}()) dto.{prop.JsonName} = null;");
                                        b.Line($"else dto.{prop.JsonName} = this.{prop.JsVariable}()!.format('YYYY-MM-DDTHH:mm:ss{(prop.Type.IsDateTimeOffset ? "ZZ" : "")}');");
                                    }
                                    else
                                    {
                                        b.Line($"dto.{prop.JsonName} = this.{prop.JsVariable}();");
                                    }
                                }
                                b.Line("return dto;");
                            }
                        }
                    }

                    // Case-sensitive comparison intended here. We always need a data source cased EXACTLY as "Default".
                    if (source == defaultSource && !source.ClientTypeName.Equals(DataSourceFactory.DefaultSourceName, StringComparison.InvariantCulture))
                    {
                        b.Line($"export const {DataSourceFactory.DefaultSourceName} = {source.ClientTypeName};");
                    }
                }
            }
        }
示例#4
0
 public override void BuildOutput(TypeScriptCodeBuilder b)
 {
     using (b.Block($"module {ViewModelModuleName}"))
     {
         using (b.Block($"export class {Model.ViewModelClassName} extends {Model.ViewModelGeneratedClassName}"))
         {
             b.Line();
             using (b.Block($"constructor(newItem?: object, parent?: Coalesce.BaseViewModel | {ListViewModelModuleName}.{Model.ListViewModelClassName})"))
             {
                 b.Line("super(newItem, parent);");
                 b.Line();
             }
         }
     }
 }
示例#5
0
        /// <summary>
        /// Write the metadata for all data sources of a class
        /// </summary>
        private void WriteDataSourcesMetadata(TypeScriptCodeBuilder b, ClassViewModel model)
        {
            using (b.Block("dataSources:", ','))
            {
                var dataSources = model.ClientDataSources(this.Model);
                foreach (var source in dataSources)
                {
                    WriteDataSourceMetadata(b, model, source);
                }

                // Not sure we need to explicitly declare the default source.
                // We can just use the absense of a data source to represent the default.

                /*
                 * var defaultSource = dataSources.SingleOrDefault(s => s.IsDefaultDataSource);
                 * if (defaultSource != null)
                 * {
                 *  var name = defaultSource.ClientTypeName.ToCamelCase();
                 *  b.Line($"get default() {{ return this.{name} }},");
                 * }
                 * else
                 * {
                 *  using (b.Block($"default:", ','))
                 *  {
                 *      b.StringProp("type", "dataSource");
                 *      b.StringProp("name", "default");
                 *      b.StringProp("displayName", "Default");
                 *      b.Line("params: {}");
                 *  }
                 * }
                 */
            }
        }
示例#6
0
 private void WriteEnumNamespace(TypeScriptCodeBuilder b)
 {
     using (b.Block($"export namespace {Model.ViewModelGeneratedClassName}"))
     {
         foreach (PropertyViewModel prop in Model.ClientProperties.Where(f => f.Type.IsEnum))
         {
             using (b.Block($"export enum {prop.Name}Enum"))
             {
                 foreach (var kvp in prop.Type.EnumValues)
                 {
                     b.Line($"{kvp.Value} = {kvp.Key},");
                 }
             }
         }
     }
 }
示例#7
0
        private static void WriteListViewModel(TypeScriptCodeBuilder b, ClassViewModel model)
        {
            string name = model.ViewModelClassName;

            string viewModelName = $"{model.ListViewModelClassName}ViewModel";
            string metadataName  = $"metadata.{name}";

            using (b.Block($"export class {viewModelName} extends ListViewModel<models.{name}, apiClients.{name}ApiClient>"))
            {
                using (b.Block($"constructor()"))
                {
                    b.Line($"super({metadataName}, new apiClients.{name}ApiClient())");
                }
            }
            b.Line();
        }
示例#8
0
 public override void BuildOutput(TypeScriptCodeBuilder b)
 {
     using (b.Block($"module {ViewModelModuleName}"))
     {
         b.DocComment($"External Type {Model.ViewModelClassName}");
         WriteViewModelClass(b);
     }
 }
示例#9
0
 /// <summary>
 /// Write the metadata for a specific parameter to a specific method
 /// </summary>
 private void WriteMethodParameterMetadata(TypeScriptCodeBuilder b, MethodViewModel method, ParameterViewModel parameter)
 {
     using (b.Block($"{parameter.JsVariable}:", ','))
     {
         WriteValueCommonMetadata(b, parameter);
         b.StringProp("role", "value");
     }
 }
示例#10
0
        /// <summary>
        /// Write metadata common to all type representations,
        /// like properties, method parameters, method returns, etc.
        /// </summary>
        private void WriteTypeCommonMetadata(TypeScriptCodeBuilder b, TypeViewModel type)
        {
            void WriteTypeDiscriminator(string propName, TypeViewModel t)
            {
                var kind = t.TsTypeKind;

                switch (kind)
                {
                case TypeDiscriminator.Unknown:
                    // We assume any unknown props are strings.
                    b.Line("// Type not supported natively by Coalesce - falling back to string.");
                    b.StringProp(propName, "string");
                    break;

                default:
                    b.StringProp(propName, kind.ToString().ToLowerInvariant());
                    break;
                }
            }

            void WriteTypeDef(string propName, TypeViewModel t)
            {
                var kind = t.TsTypeKind;

                switch (kind)
                {
                case TypeDiscriminator.Enum:
                    b.Line($"get {propName}() {{ return domain.enums.{t.Name} }},");
                    break;

                case TypeDiscriminator.Model:
                case TypeDiscriminator.Object:
                    b.Line($"get {propName}() {{ return {GetClassMetadataRef(t.ClassViewModel)} }},");
                    break;
                }
            }

            WriteTypeDiscriminator("type", type);
            WriteTypeDef("typeDef", type);

            // For collections, write the references to the underlying type.
            if (type.TsTypeKind == TypeDiscriminator.Collection)
            {
                if (type.PureType.TsTypeKind == TypeDiscriminator.Collection)
                {
                    throw new InvalidOperationException("Collections of collections aren't supported by Coalesce as exposed types");
                }

                using (b.Block($"itemType:", ','))
                {
                    b.StringProp("name", "$collectionItem");
                    b.StringProp("displayName", "");
                    b.StringProp("role", "value");
                    WriteTypeCommonMetadata(b, type.PureType);
                }
            }
        }
示例#11
0
        private void WriteExternalTypeMetadata(TypeScriptCodeBuilder b, ClassViewModel model)
        {
            using (b.Block($"export const {model.ViewModelClassName} = domain.types.{model.ViewModelClassName} ="))
            {
                WriteCommonClassMetadata(b, model);
                b.StringProp("type", "object");

                WriteClassPropertiesMetadata(b, model);
            }
        }
示例#12
0
 private void WriteClassPropertiesMetadata(TypeScriptCodeBuilder b, ClassViewModel model)
 {
     using (b.Block("props:", ','))
     {
         foreach (var prop in model.ClientProperties)
         {
             WriteClassPropertyMetadata(b, model, prop);
         }
     }
 }
示例#13
0
 /// <summary>
 /// Write the metadata for all methods of a class
 /// </summary>
 private void WriteClassMethodMetadata(TypeScriptCodeBuilder b, ClassViewModel model)
 {
     using (b.Block("methods:", ','))
     {
         foreach (var method in model.ClientMethods)
         {
             WriteClassMethodMetadata(b, model, method);
         }
     }
 }
示例#14
0
        public void WriteMethod_SaveToDto(TypeScriptCodeBuilder b)
        {
            b.DocComment($"Saves this object into a data transfer object to send to the server.");
            using (b.Block($"public saveToDto = (): any =>"))
            {
                b.Line("var dto: any = {};");
                if (Model.PrimaryKey != null)
                {
                    b.Line($"dto.{Model.PrimaryKey.JsonName} = this.{Model.PrimaryKey.JsVariable}();");
                }

                b.Line();

                // Objects are skipped in the loop because objects will be examined
                // at the same time that their corresponding foreign key is examined.
                foreach (PropertyViewModel prop in Model.ClientProperties.Where(f => f.IsClientWritable && f.Object == null))
                {
                    if (prop.Type.IsDate)
                    {
                        b.Line($"if (!this.{prop.JsVariable}()) dto.{prop.JsonName} = null;");
                        b.Line($"else dto.{prop.JsonName} = this.{prop.JsVariable}()!.format('YYYY-MM-DDTHH:mm:ss{(prop.Type.IsDateTimeOffset ? "ZZ" : "")}');");
                    }
                    else if (prop.IsForeignKey)
                    {
                        var navigationProp = prop.ReferenceNavigationProperty;

                        b.Line($"dto.{prop.JsonName} = this.{prop.JsVariable}();");
                        // If the Id isn't set, use the object and see if that is set. Allows a child to get an Id after the fact.
                        using (b.Block($"if (!dto.{prop.JsonName} && this.{navigationProp.JsVariable}())"))
                        {
                            b.Line($"dto.{prop.JsonName} = this.{navigationProp.JsVariable}()!.{navigationProp.Object.PrimaryKey.JsVariable}();");
                        }
                    }
                    else
                    {
                        b.Line($"dto.{prop.JsonName} = this.{prop.JsVariable}();");
                    }
                }
                b.Line();
                b.Line($"return dto;");
            }
        }
示例#15
0
        public override void BuildOutput(TypeScriptCodeBuilder b)
        {
            using (b.Block($"module {ViewModelModuleName}"))
            {
                b.Line();
                WriteViewModelClass(b);

                b.Line();
                WriteEnumNamespace(b);
            }
        }
示例#16
0
        public override void BuildOutput(TypeScriptCodeBuilder b)
        {
            using (b.Block($"module {ListViewModelModuleName}"))
            {
                b.Line();
                WriteDataSources(b);

                b.Line();
                WriteListViewModelClass(b);
            }
        }
示例#17
0
        private void WriteServiceMetadata(TypeScriptCodeBuilder b, ClassViewModel model)
        {
            using (b.Block($"export const {model.ClientTypeName} = domain.services.{model.ClientTypeName} ="))
            {
                b.StringProp("name", model.ClientTypeName.ToCamelCase());
                b.StringProp("displayName", model.DisplayName);

                b.StringProp("type", "service");
                b.StringProp("controllerRoute", model.ApiRouteControllerPart);

                WriteClassMethodMetadata(b, model);
            }
        }
示例#18
0
        /// <summary>
        /// Write the metadata for an entire method
        /// </summary>
        private void WriteClassMethodMetadata(TypeScriptCodeBuilder b, ClassViewModel model, MethodViewModel method)
        {
            using (b.Block($"{method.JsVariable}:", ','))
            {
                b.StringProp("name", method.JsVariable);
                b.StringProp("displayName", method.DisplayName);
                b.StringProp("transportType", method.TransportType.ToString().Replace("Result", "").ToLower());
                b.StringProp("httpMethod", method.ApiActionHttpMethod.ToString().ToUpperInvariant());

                using (b.Block("params:", ','))
                {
                    // TODO: should we be writing out the implicit 'id' param as metadata? Or handling some other way?
                    // If we do keep it as metadata, should probably add some prop to mark it as what it is.
                    if (method.IsModelInstanceMethod)
                    {
                        using (b.Block($"id:", ','))
                        {
                            b.StringProp("name", "id");
                            b.StringProp("displayName", "Primary Key"); // TODO: Is this what we want? Also, i18n.
                            b.StringProp("role", "value");
                            WriteTypeCommonMetadata(b, model.PrimaryKey.Type);
                        }
                    }

                    foreach (var param in method.ClientParameters)
                    {
                        WriteMethodParameterMetadata(b, method, param);
                    }
                }

                using (b.Block("return:", ','))
                {
                    b.StringProp("name", "$return");
                    b.StringProp("displayName", "Result"); // TODO: i18n
                    b.StringProp("role", "value");
                    WriteTypeCommonMetadata(b, method.ResultType);
                }
            }
        }
示例#19
0
        private void WriteViewModelClass(TypeScriptCodeBuilder b)
        {
            using (b.Block($"export class {Model.ViewModelGeneratedClassName}"))
            {
                if (Model.PrimaryKey != null)
                {
                    // ID of the object.
                    b.Line("public myId: any = 0;");
                }

                b.Line("public parent: any;");
                b.Line("public parentCollection: any;");

                b.Line();
                b.Line("// Observables");
                foreach (PropertyViewModel prop in Model.ClientProperties)
                {
                    b.Line($"public {prop.JsVariable}: {prop.Type.TsKnockoutType(true)} = {prop.Type.ObservableConstructorCall()};");
                    if (prop.Type.IsEnum)
                    {
                        b.Line($"public {prop.JsTextPropertyName} = {prop.Type.ObservableConstructorCall()};");
                    }
                }

                WriteMethod_LoadFromDto(b);

                WriteMethod_SaveToDto(b);

                b.Line();
                using (b.Block("constructor(newItem?: any, parent?: any)"))
                {
                    b.Line("this.parent = parent;");
                    b.Line();
                    b.Line("if (newItem) {");
                    b.Line("    this.loadFromDto(newItem);");
                    b.Line("}");
                }
            }
        }
示例#20
0
        private static void WriteViewModel(TypeScriptCodeBuilder b, ClassViewModel model)
        {
            string name          = model.ViewModelClassName;
            string viewModelName = $"{name}ViewModel";
            string metadataName  = $"$metadata.{name}";

            b.Line($"export interface {viewModelName} extends $models.{name} {{}}");
            using (b.Block($"export class {viewModelName} extends ViewModel<$models.{name}, $apiClients.{name}ApiClient>"))
            {
                // This is an alternative to calling defineProps() on each class that causes larger and more cluttered files:
                //foreach (var prop in model.ClientProperties)
                //{
                //    b.Line($"get {prop.JsVariable}() {{ return this.$data.{prop.JsVariable} }}");
                //    b.Line($"set {prop.JsVariable}(val) {{ this.$data.{prop.JsVariable} = val }}");
                //}

                foreach (var method in model.ClientMethods.Where(m => !m.IsStatic))
                {
                    string signature =
                        string.Concat(method.ClientParameters.Select(f => $", {f.Name}: {new VueType(f.Type).TsType("$models")} | null"));

                    // "item" or "list"
                    var transportTypeSlug = method.TransportType.ToString().Replace("Result", "").ToLower();

                    b.DocComment(method.Comment, true);
                    b.Line($"public {method.JsVariable} = this.$apiClient.$makeCaller(\"{transportTypeSlug}\", ");
                    b.Indented($"(c{signature}) => c.{method.JsVariable}(this.$primaryKey{string.Concat(method.ClientParameters.Select(p => ", " + p.Name))}))");
                }

                b.Line();
                using (b.Block($"constructor(initialData?: $models.{name})"))
                {
                    b.Line($"super({metadataName}, new $apiClients.{name}ApiClient(), initialData)");
                }
            }
            b.Line($"defineProps({viewModelName}, $metadata.{name})");
            b.Line();
        }
示例#21
0
        private static void WriteApiClientClass(TypeScriptCodeBuilder b, ClassViewModel model, string baseClass)
        {
            string clientName = $"{model.ClientTypeName}ApiClient";

            using (b.Block($"export class {clientName} extends {baseClass}"))
            {
                b.Line($"constructor() {{ super($metadata.{model.ClientTypeName}) }}");

                foreach (var method in model.ClientMethods)
                {
                    WriteApiEndpointFunction(b, model, method);
                }
            }
        }
示例#22
0
        private static void WriteApiEndpointFunction(TypeScriptCodeBuilder b, ClassViewModel model, MethodViewModel method)
        {
            var    returnIsListResult = method.ReturnsListResult;
            string signature          =
                string.Concat(method.ClientParameters.Select(f => $"{f.Name}: {new VueType(f.Type).TsType("$models")} | null, "))
                + "$config?: AxiosRequestConfig";

            if (method.IsModelInstanceMethod)
            {
                signature = $"id: {new VueType(model.PrimaryKey.Type).TsType(null)}, " + signature;
            }

            string resultType = method.TransportTypeGenericParameter.IsVoid
                ? $"{method.TransportType}<void>"
                : $"{method.TransportType}<{new VueType(method.TransportTypeGenericParameter).TsType("$models")}>";

            using (b.Block($"public {method.JsVariable}({signature}): AxiosPromise<{resultType}>"))
            {
                b.Line($"const $method = this.$metadata.methods.{method.JsVariable}");
                using (b.Block($"const $params = "))
                {
                    if (method.IsModelInstanceMethod)
                    {
                        b.Line($"id,");
                    }
                    foreach (var param in method.ClientParameters)
                    {
                        b.Line($"{param.JsVariable},");
                    }
                }

                b.Line("return this.$invoke($method, $params, $config)");
            }

            // Line between methods
            b.Line();
        }
示例#23
0
        public override void BuildOutput(TypeScriptCodeBuilder b)
        {
            using (b.Block($"module {ModuleName(ModuleKind.Service)}"))
            {
                using (b.Block($"export class {Model.ServiceClientClassName}"))
                {
                    b.Line();
                    b.Line($"public readonly apiController: string = \"/{Model.ApiRouteControllerPart}\";");

                    b.DocComment($"Configuration for all instances of {Model.ServiceClientClassName}. Can be overidden on each instance via instance.coalesceConfig.");
                    b.Line($"public static coalesceConfig = new Coalesce.ServiceClientConfiguration<{Model.ServiceClientClassName}>(Coalesce.GlobalConfiguration.serviceClient);");

                    b.DocComment($"Configuration for this {Model.ServiceClientClassName} instance.");
                    b.Line($"public coalesceConfig: Coalesce.ServiceClientConfiguration<{Model.ServiceClientClassName}>");
                    b.Indented($"= new Coalesce.ServiceClientConfiguration<{Model.ServiceClientClassName}>({Model.ServiceClientClassName}.coalesceConfig);");

                    b.Line();
                    foreach (var method in Model.ClientMethods)
                    {
                        WriteClientMethodDeclaration(b, method, Model.ServiceClientClassName);
                    }
                }
            }
        }
示例#24
0
        private static void WriteViewModel(TypeScriptCodeBuilder b, ClassViewModel model)
        {
            string name          = model.ViewModelClassName;
            string viewModelName = $"{name}ViewModel";
            string metadataName  = $"metadata.{name}";

            b.Line($"export interface {viewModelName} extends models.{name} {{}}");
            using (b.Block($"export class {viewModelName} extends ViewModel<models.{name}, apiClients.{name}ApiClient>"))
            {
                // This is an alternative to calling defineProps() on each class that causes larger and more cluttered files:
                //foreach (var prop in model.ClientProperties)
                //{
                //    b.Line($"get {prop.JsVariable}() {{ return this.$data.{prop.JsVariable} }}");
                //    b.Line($"set {prop.JsVariable}(val) {{ this.$data.{prop.JsVariable} = val }}");
                //}

                using (b.Block($"constructor(initialData?: models.{name})"))
                {
                    b.Line($"super({metadataName}, new apiClients.{name}ApiClient(), initialData)");
                }
            }
            b.Line($"defineProps({viewModelName}, metadata.{name})");
            b.Line();
        }
示例#25
0
        /// <summary>
        /// Write the metadata for all data sources of a class
        /// </summary>
        private void WriteDataSourceMetadata(TypeScriptCodeBuilder b, ClassViewModel model, ClassViewModel source)
        {
            // TODO: Should we be camel-casing the names of data sources in the metadata?
            // TODO: OR, should we be not camel casing the members we place on the domain[key: string] objects?
            using (b.Block($"{source.ClientTypeName.ToCamelCase()}:", ','))
            {
                b.StringProp("type", "dataSource");

                WriteCommonClassMetadata(b, source);

                if (source.IsDefaultDataSource)
                {
                    b.Line("isDefault: true,");
                }

                using (b.Block("params:", ','))
                {
                    foreach (var prop in source.DataSourceParameters)
                    {
                        WriteClassPropertyMetadata(b, model, prop);
                    }
                }
            }
        }
示例#26
0
        private void WriteClassPropertyMetadata(TypeScriptCodeBuilder b, ClassViewModel model, PropertyViewModel prop)
        {
            using (b.Block($"{prop.JsVariable}:", ','))
            {
                WriteValueCommonMetadata(b, prop);

                switch (prop.Role)
                {
                case PropertyRole.PrimaryKey:
                    // TS Type: "PrimaryKeyProperty"
                    b.StringProp("role", "primaryKey");
                    break;

                case PropertyRole.ForeignKey:
                    // TS Type: "ForeignKeyProperty"
                    var navProp = prop.ReferenceNavigationProperty;
                    b.StringProp("role", "foreignKey");
                    b.Line($"get principalKey() {{ return {GetClassMetadataRef(navProp.Object)}.props.{navProp.Object.PrimaryKey.JsVariable} as PrimaryKeyProperty }},");
                    b.Line($"get principalType() {{ return {GetClassMetadataRef(navProp.Object)} }},");
                    b.Line($"get navigationProp() {{ return {GetClassMetadataRef(model)}.props.{navProp.JsVariable} as ModelReferenceNavigationProperty }},");
                    break;

                case PropertyRole.ReferenceNavigation:
                    // TS Type: "ModelReferenceNavigationProperty"
                    b.StringProp("role", "referenceNavigation");
                    b.Line($"get foreignKey() {{ return {GetClassMetadataRef(model)}.props.{prop.ForeignKeyProperty.JsVariable} as ForeignKeyProperty }},");
                    b.Line($"get principalKey() {{ return {GetClassMetadataRef(prop.Object)}.props.{prop.Object.PrimaryKey.JsVariable} as PrimaryKeyProperty }},");
                    break;

                case PropertyRole.CollectionNavigation:
                    // TS Type: "ModelCollectionNavigationProperty"
                    b.StringProp("role", "collectionNavigation");
                    b.Line($"get foreignKey() {{ return {GetClassMetadataRef(prop.Object)}.props.{prop.InverseProperty.ForeignKeyProperty.JsVariable} as ForeignKeyProperty }},");
                    break;

                default:
                    b.StringProp("role", "value");
                    break;
                }

                // We store the negative case instead of the positive
                // because there are likely going to be more that are serializable than not.
                if (!prop.IsClientSerializable)
                {
                    b.Line("dontSerialize: true,");
                }
            }
        }
示例#27
0
        private void WriteApiBackedTypeMetadata(TypeScriptCodeBuilder b, ClassViewModel model)
        {
            using (b.Block($"export const {model.ViewModelClassName} = domain.types.{model.ViewModelClassName} ="))
            {
                WriteCommonClassMetadata(b, model);
                b.StringProp("type", "model");
                b.StringProp("controllerRoute", model.ApiRouteControllerPart);
                b.Line($"get keyProp() {{ return this.props.{model.PrimaryKey.JsVariable} }}, ");

                WriteClassPropertiesMetadata(b, model);

                WriteClassMethodMetadata(b, model);

                WriteDataSourcesMetadata(b, model);
            }
        }
示例#28
0
        private void WriteListViewModelClass(TypeScriptCodeBuilder b)
        {
            using (b.Block($"export class {Model.ListViewModelClassName} extends Coalesce.BaseListViewModel<{ViewModelFullName}>"))
            {
                b.Line($"public readonly modelName: string = \"{Model.ClientTypeName}\";");
                b.Line($"public readonly apiController: string = \"/{Model.ApiRouteControllerPart}\";");
                b.Line($"public modelKeyName: string = \"{Model.PrimaryKey.JsVariable}\";");
                b.Line($"public itemClass: new () => {ViewModelFullName} = {ViewModelFullName};");

                b.Line();
                b.Line("public filter: {");
                foreach (var prop in Model.BaseViewModel.ClientProperties.Where(f => f.IsUrlFilterParameter))
                {
                    b.Indented($"{prop.JsonName}?: string;");
                }
                b.Line("} | null = null;");

                b.DocComment("The namespace containing all possible values of this.dataSource.");
                b.Line($"public dataSources: typeof {Model.ClientTypeName}DataSources = {Model.ClientTypeName}DataSources;");

                b.DocComment("The data source on the server to use when retrieving objects. Valid values are in this.dataSources.");
                b.Line($"public dataSource: Coalesce.DataSource<{ViewModelFullName}> = new this.dataSources.{DataSourceFactory.DefaultSourceName}();");

                b.DocComment($"Configuration for all instances of {Model.ListViewModelClassName}. Can be overidden on each instance via instance.coalesceConfig.");
                b.Line($"public static coalesceConfig = new Coalesce.ListViewModelConfiguration<{Model.ListViewModelClassName}, {ViewModelFullName}>(Coalesce.GlobalConfiguration.listViewModel);");

                b.DocComment($"Configuration for this {Model.ListViewModelClassName} instance.");
                b.Line($"public coalesceConfig: Coalesce.ListViewModelConfiguration<{Model.ListViewModelClassName}, {ViewModelFullName}>");
                b.Indented($"= new Coalesce.ListViewModelConfiguration<{Model.ListViewModelClassName}, {ViewModelFullName}>({Model.ListViewModelClassName}.coalesceConfig);");

                // Write client methods
                b.Line();
                foreach (var method in Model.ClientMethods.Where(m => m.IsStatic))
                {
                    b.Line();
                    WriteClientMethodDeclaration(b, method, Model.ListViewModelClassName);
                }

                b.Line();
                b.Line($"protected createItem = (newItem?: any, parent?: any) => new {ViewModelFullName}(newItem, parent);");

                b.Line();
                b.Line("constructor() {");
                b.Indented("super();");
                b.Line("}");
            }
        }
示例#29
0
        private void WriteEnumMetadata(TypeScriptCodeBuilder b, TypeViewModel model)
        {
            using (b.Block($"export const {model.Name} = domain.enums.{model.Name} ="))
            {
                b.StringProp("name", model.Name.ToCamelCase());
                b.StringProp("displayName", model.DisplayName);
                b.StringProp("type", "enum");

                string enumShape = string.Join("|", model.EnumValues.Select(ev => $"\"{ev.Value}\""));
                b.Line($"...getEnumMeta<{enumShape}>([");
                foreach (var value in model.EnumValues)
                {
                    // TODO: allow for localization of displayName
                    b.Indented($"{{ value: {value.Key}, strValue: '{value.Value}', displayName: '{value.Value.ToProperCase()}' }},");
                }
                b.Line("]),");
            }
        }
示例#30
0
        private void WriteMethod_LoadFromDto(TypeScriptCodeBuilder b)
        {
            b.DocComment(new[] {
                "Load the ViewModel object from the DTO.",
                "@param data: The incoming data object to load.",
                "@param force: Will override the check against isLoading that is done to prevent recursion. False is default.",
                "@param allowCollectionDeletes: Set true when entire collections are loaded. True is the default. ",
                "In some cases only a partial collection is returned, set to false to only add/update collections.",
            });
            using (b.Block("public loadFromDto = (data: any, force: boolean = false, allowCollectionDeletes: boolean = true): void =>", ';'))
            {
                b.Line("if (!data || (!force && this.isLoading())) return;");
                b.Line("this.isLoading(true);");

                b.Line("// Set the ID ");
                b.Line($"this.myId = data.{Model.PrimaryKey.JsonName};");
                b.Line($"this.{Model.PrimaryKey.JsVariable}(data.{Model.PrimaryKey.JsonName});");

                b.Line("// Load the lists of other objects");
                foreach (PropertyViewModel prop in Model.ClientProperties.Where(p => p.Type.IsCollection))
                {
                    using (b.Block($"if (data.{prop.JsonName} != null)"))
                    {
                        if (prop.Object.PrimaryKey != null)
                        {
                            b.Line("// Merge the incoming array");
                            b.Line($"Coalesce.KnockoutUtilities.RebuildArray(this.{prop.JsVariable}, data.{prop.JsonName}, '{prop.Object.PrimaryKey.JsonName}', {prop.Object.ViewModelClassName}, this, allowCollectionDeletes);");
                            if (prop.IsManytoManyCollection)
                            {
                                b.Line("// Add many-to-many collection");
                                b.Line("let objs: any[] = [];");
                                using (b.Block($"$.each(data.{prop.JsonName}, (index, item) =>", ");"))
                                {
                                    b.Line($"if (item.{prop.ManyToManyCollectionProperty.JsonName}) {{");
                                    b.Indented($"objs.push(item.{prop.ManyToManyCollectionProperty.JsonName});");
                                    b.Line($"}}");
                                }
                                b.Line($"Coalesce.KnockoutUtilities.RebuildArray(this.{prop.ManyToManyCollectionName.ToCamelCase()}, objs, '{prop.ManyToManyCollectionProperty.ForeignKeyProperty.JsVariable}', {prop.ManyToManyCollectionProperty.Object.ViewModelClassName}, this, allowCollectionDeletes);");
                            }
                        }
                        else if (prop.PureType.IsPrimitive)
                        {
                            b.Line($"this.{prop.JsVariable}(data.{prop.JsVariable});");
                        }
                        else
                        {
                            b.Line($"Coalesce.KnockoutUtilities.RebuildArray(this.{prop.JsVariable}, data.{prop.JsonName}, null, {prop.Object.ViewModelClassName}, this, allowCollectionDeletes);");
                        }
                    }
                }

                // Objects are loaded first so that they are available when the IDs get loaded.
                // This handles the issue with populating select lists with correct data because we now have the object.
                foreach (PropertyViewModel prop in Model.ClientProperties.Where(p => p.IsPOCO))
                {
                    b.Line($"if (!data.{prop.JsonName}) {{ ");
                    using (b.Indented())
                    {
                        if (prop.ForeignKeyProperty != null)
                        {
                            // Prop is a reference navigation prop. The incoming foreign key doesn't match our existing foreign key, so clear out the property.
                            // If the incoming key and existing key DOES match, we assume that the data just wasn't loaded, but is still valid, so we do nothing.
                            b.Line($"if (data.{prop.ForeignKeyProperty.JsonName} != this.{prop.ForeignKeyProperty.JsVariable}()) {{");
                            b.Indented($"this.{prop.JsVariable}(null);");
                            b.Line("}");
                        }
                        else
                        {
                            b.Line($"this.{prop.JsVariable}(null);");
                        }
                    }
                    b.Line("} else {");
                    using (b.Indented())
                    {
                        b.Line($"if (!this.{prop.JsVariable}()){{");
                        b.Indented($"this.{prop.JsVariable}(new {prop.Object.ViewModelClassName}(data.{prop.JsonName}, this));");
                        b.Line("} else {");
                        b.Indented($"this.{prop.JsVariable}()!.loadFromDto(data.{prop.JsonName});");
                        b.Line("}");
                        if (prop.Object.IsDbMappedType)
                        {
                            b.Line($"if (this.parent instanceof {prop.Object.ViewModelClassName} && this.parent !== this.{prop.JsVariable}() && this.parent.{prop.Object.PrimaryKey.JsVariable}() == this.{prop.JsVariable}()!.{prop.Object.PrimaryKey.JsVariable}())");
                            b.Line("{");
                            b.Indented($"this.parent.loadFromDto(data.{prop.JsonName}, undefined, false);");
                            b.Line("}");
                        }
                    }
                    b.Line("}");
                }

                b.Line();
                b.Line("// The rest of the objects are loaded now.");
                foreach (PropertyViewModel prop in Model.ClientProperties.Where(p => p.Object == null && !p.IsPrimaryKey))
                {
                    if (prop.Type.IsDate)
                    {   // Using valueOf/getTime here is a 20x performance increase over moment.isSame(). moment(new Date(...)) is also a 10x perf increase.
                        b.Line($"if (data.{prop.JsonName} == null) this.{prop.JsVariable}(null);");
                        b.Line($"else if (this.{prop.JsVariable}() == null || this.{prop.JsVariable}()!.valueOf() != new Date(data.{prop.JsonName}).getTime()){{");
                        b.Indented($"this.{prop.JsVariable}(moment(new Date(data.{prop.JsonName})));");
                        b.Line("}");
                    }
                    else
                    {
                        b.Line($"this.{prop.JsVariable}(data.{prop.JsonName});");
                    }
                }

                b.Line("if (this.coalesceConfig.onLoadFromDto()){");
                b.Indented("this.coalesceConfig.onLoadFromDto()(this as any);");
                b.Line("}");
                b.Line("this.isLoading(false);");
                b.Line("this.isDirty(false);");
                b.Line("if (this.coalesceConfig.validateOnLoadFromDto()) this.validate();");
            }
        }