jQuery is a library - a big collection of "classes" and methods you can use anywhere. It makes no assumptions about how your application is built, has no internal DOM tree representation, etc.

Angular JS is an MVC framework - it forces your application to be structured in a very specific way, it manipulates DOM tree of the page according to specified rules. And well, it also provides a big collection of "classes" and methods you can use in your the code, but only the in parts you wrote in compliance with Angular architecture (scopes/controllers/services).

Do you have a call to CreateBusinessService somewhere in your code? It allows doing business service calls from non-Ensebble context (by well making current job Ensemble job). It's called like this (from Demo.ZenService.Zen.WeatherReportForm):
 

Set tSC=##class(Ens.Director).CreateBusinessService("Zen Service",.tService)

Where first arguments is a host name of a business service. In your case the error appeared because the first argument is equal to EnsLib.SOAP.GenericService which is not present in currently running Ensemble production. You need to do one of the:

  • Check that you're running a correct production
  • Change first argument of CreateBusinessService method from EnsLib.SOAP.GenericService to something that exists in current production
  • Create business service in production with the name EnsLib.SOAP.GenericService

I'd like to add some commentary to Angular 1/2 comparison.

Angular 1:

- easier to hook to cache (can be used by calling the library from a csp and calling services cache rest)

That's same as with Angular 2.

- flatter learning curve if jquery is already known

- being so similar to jquery

jQuery has little in common with Angular. Angular (1 or 2) is an MVC framework and jQuery is the utilities library.

- not needed for really small projects

Yes, for both Angular 1 and 2.

- easier to maintain, perhaps, for someone who knows web, html and jquery

Highly debatable. Learning curve for Angular 1 is fairly steep.

Angular 2.

- does not work in browsers older than IE9

Angular 1 also does not work in browsers older than IE9

- keeps changing, it is not completely stable yet?

It has a stable version for a few months already.

Now, with that said, I'd like to recommend Angular 2. The main advantage in my opinion is that many fundamental problems present in Angular 1 were solved in Angular 2. Here's some links elaborating on differences between Angular 1 and 2:

  • Short comparison of differences between Angular 1 and 2 is available here.
  • More detailed comparison is available here.

Thank you!

I copied your example, but SQL returns results saved on disk, which is expected as the data property is triggered computed

Class CS.Persistent Extends %Persistent
{

Property data As CS.Serial [ SqlComputeCode = {set {*} = ##class(CS.Persistent).dataGetStatic()}, SqlComputed ];

/// data getter method in SQL context
ClassMethod dataGetStatic() As %List
{
    quit $lb(2017,1)
}

/// do ##class(CS.Persistent).test()
ClassMethod test()
{
    do ..%KillExtent()
    do $system.SQL.PurgeForTable($classname())
    set obj = ##class(CS.Persistent).%New()
    set obj.data.Month=-1
    set obj.data.Year=0
    set sc = obj.%Save()
    write !,"Save: ",$s($$$ISOK(sc):"OK", 1:$System.Status.GetErrorText(sc)),!
    zw ^CS.PersistentD
    do ##class(%SQL.Statement).%ExecDirect(,"select * from "_$classname()).%Display()
}
}

I execute in a terminal:

do ##class(CS.Persistent).test()

And I receive the following output:

Save: OK
^CS.PersistentD=1
^CS.PersistentD(1)=$lb("",$lb(0,-1))
ID      data_Month      data_Year
1       -1              0

Can I make a serial class always computed?

I want to receive the following output:

Save: OK
^CS.PersistentD=1
^CS.PersistentD(1)=$lb("",$lb(0,-1))
ID      data_Month      data_Year
1       1               2017

Another approach would be using dynamic dispatch:

Let's say you have this method:

/// @NiceTest1
ClassMethod Test()
{
    Write 1
}

Using Class Generators you generate 2 additional methods:

/// @NiceTest1
ClassMethod OnBeforeTest()
{
    Write "Before NiceTest1"
}

/// @NiceTest1
ClassMethod OnAfterTest()
{
    Write "After NiceTest1"
}

and you add dynamic dispatch method (and dynamic dispatch classmethod) to the class:

/// Is used to implement an unknown method call.  It is also used
/// to resolve an unknown multidimensional property reference (to get the value
/// of a property) because that syntax is identical to a method call.
Method %DispatchMethod(Method As %String, Args...) [ ServerOnly = 1 ]
{
    // Amethod -> method
    set Method = $e(Method, 2, *)
    do $method(, "OnBefore" _ Method, Args...)
    do $method(, Method, Args...)
    do $method(,"OnAfter" _ Method,Args...)
}

So, as a whole it can look like this:

Class Utils.Annotations1 Extends %RegisteredObject
{

Method OnBeforeTest()
{
    Write "Init NiceTest1",!
}

/// @NiceTest1
Method Test()
{
    Write 1,!
}

Method OnAfterTest()
{
    Write "End NiceTest1",!
}

/// Is used to implement an unknown method call. 
Method %DispatchMethod(Method As %String, Args...) [ ServerOnly = 1 ]
{
    set Method = ..getMethodName(Method)
    do $method(, "OnBefore" _ Method, Args...)
    do $method(, Method, Args...)
    do $method(,"OnAfter" _ Method,Args...)
}

/// Is used to implement an unknown class method call
ClassMethod %DispatchClassMethod(Class As %String, Method As %String, Args...) [ ServerOnly = 1 ]
{
    set Method = ..getMethodName(Method)
    do $classmethod(, "OnBefore" _ Method, Args...)
    do $classmethod(, Method, Args...)
    do $classmethod(,"OnAfter" _ Method,Args...)
}

ClassMethod getMethodName(method As %String) As %String
{
    // Amethod -> method
    set method = $e(method, 2, *)
    return method
}

}

Works like this:

USER>do a.ATest()
Init NiceTest1
1
End NiceTest1
 
USER>do a.Test()
1

You can modify method code during compilation (obviously dangerous) using generators. Here's the sample that annotates all methods, containing @ in description:

Class Utils.Annotations
{

/// @NiceTest1
ClassMethod Test()
{
    Write 1
}

ClassMethod OnCompile() As %Status [ CodeMode = objectgenerator ]
{
    #dim sc As %Status = $$$OK
    for i=1:1:%class.Methods.Count() {
        #dim method As %Dictionary.MethodDefinition = %class.Methods.GetAt(i)
        if method.Description [ "@" {
            set sc = ..annotate(method)
            quit:$$$ISERR(sc)
        }
    }
    quit:$$$ISERR(sc) sc
    set sc = %class.%Save()
    quit sc
}

ClassMethod annotate(method As %Dictionary.MethodDefinition) As %Status
{
    #dim sc As %Status = $$$OK
    set code = method.Implementation.Read($$$MaxCacheInt)
    set code = ..cleadOldAnnotation(code)
    set code = ..addNewAnnotation(code, $e(method.Description, 2,*))
    
    do method.Implementation.Clear()
    do method.Implementation.Write(code)
    
    quit sc
}

ClassMethod cleadOldAnnotation(code As %String) As %String
{
    if $find(code, "//init annotation end") {
        set code = $e(code, $find(code, "//init annotation end") + 2, $find(code, "//complete annotation start") - $l("//complete annotation start") - 2)
    }
    return code
}

ClassMethod addNewAnnotation(code As %String, annotation As %String) As %String
{
    #define Tab $c(9)
    set code =  $$$Tab _ "Write ""Init " _ annotation _ """" _ $$$NL _
                $$$Tab _ "//init annotation end" _ $$$NL _
                code _
                $$$Tab _ "//complete annotation start"_ $$$NL _
                $$$Tab _ "Write ""End " _ annotation _ """"
    return code
}

}

Compiles into:

/// @NiceTest1
ClassMethod Test()
{
    Write "Init NiceTest1"
    //init annotation end
    Write 1
    //complete annotation start
    Write "End NiceTest1"
}

GitHub

Yes, you can query everything  you see in Message Viewer and Event Viewer. Their code is defined in EnsPortal.MessageViewer.zen and EnsPortal.EventLog.zen respectively. You can see the queries these pages use and adapt them, or inherit from them and publish it as another separate web application.

Also, here's modification example - Ensemble Log Viewer with namespace support.