Quantcast
Channel: Top 20 tutorials Blogs from GrapeCity
Viewing all 93 articles
Browse latest View live

LearnWijmo Released!

$
0
0

I am very excited because today we released the first version of a project I’ve been working on for a few months. It’s called LearnWijmo, and it’s a tool that I hope will make it easier for people to learn how to use our JavaScript UI library.

LearnWijmo2

If you are curious or impatient, and don’t care to read about LearnWijmo’s background, motivation, and design process, feel free to click this link and jump in right now. You can always come back later.

I have been using and developing software for quite a few years, and documentation has always been a major topic for me. So let me recap very quickly why we decided to create LearnWijmo and why it is built the way it is.

The old days: CHM files

In the old days, things were simple. You wrote a long Word document, ran it through a tool to create a help file, and shipped the help file with the product. The tool took care of building an outline, an index, and all inter-topic links. This worked well as long as you had a good team of technical writers and didn’t release too many products too often. Maintaining the source documentation up-to-date and in sync with the product was a big challenge.

Automated Documentation: The Good and the Bad

Things started to improve when Sun created JavaDoc, a set of tools and standards designed to make software documentation easier (circa 1996). The idea was to embed the documentation in comments right in the code, and to use a tool to extract and format those comments into help systems. The idea is comments are much easier to maintain, and better integrated with the code. The idea worked, and other companies soon followed the example. Microsoft for example introduced XML documentation comments in C# from its first release.

Embedding the documentation in the source code is a great idea because it makes it simpler to keep the documentation in sync with the product. But it poses a new set of challenges:

  1. It encourages the creation of “automatic” comments, a knee-jerk reaction that tempts developers to write comments such as “isAsync: gets or sets a value that determines whether the process should be performed asynchronously”. Doh…
  2. It shifts the documentation task from technical writers to developers, who have a completely different skill set. Asking tech writers to review the documentation and submit changes to developers periodically is a possible solution to this problem; another is to give tech writers access to your source control system so they can edit the comments themselves. Either way, it’s not as simple as simply writing some comments and hoping that will lead to stellar-quality documentation.
  3. Even when done perfectly, source code comments only address the reference documentation, which defines each part of the product’s API. It does not help much with the narrative documentation, which explains how to use the product and can include step-by-step instructions.

Supporting Materials

There’s no question automated documentation is here to stay. It has its challenges and limitations, but it does solve the problem it was designed to solve, which is generating and maintaining reference documentation that is complete, accurate, and up-to-date.

The next challenge is how to create additional content to support the reference documentation. I believe this requires a combination of elements:

  1. Frequent blogs, focused articles that explain new concepts and contain information that can be used to solve specific problems.
  2. A good set of sample applications, designed to demonstrate typical usage scenarios and how to combine multiple products and features to achieve a certain result.

We have been working very hard to generate (and link to) quality blogs and samples that help users understand our products and the concepts behind them. We will continue to do so because both the existing API documentation and narrative content like blog posts are important to a complete understanding of Wijmo.

The Missing Piece

Even with a good set of reference documentation, blogs, and samples, we felt there was something missing. A piece that would bring all these things together and play the role of an old-style printed manual or a course, something you could browse through to get a general understanding of the product, or dive into to understand the details.

Our first attempt was an “Explorer” application divided into sections to highlight each important feature of our products. This idea worked fairly well. You can run the Explorer and check it out for yourself.

The main problem with the “Explorer” is that it showed what you could do with the controls, but not how. To see the source code, you had to download the application or search through a project tree. We ran into another problem as we added more controls, features, and framework support to Wijmo: the Explorer sample started to grow too much and became hard to maintain.

We also created a series of “101” samples that focus on single modules, to show source code side-by-side with running examples. For example, we have InputIntro, FlexGridIntro, and FlexChartIntro samples that follow this pattern. These samples do a great job of showing the basic functionality of each control, along with the source code required. The problem with the 101 samples is that they are a little hard to maintain, and do not allow users to experiment easily. To try out an idea, you still have to download and modify the code.

Requirements

After talking to customers and to members of our own teams, we decided it was time to create something new. Something that would make it an order of magnitude easier for people to learn what Wijmo is and how to use it. We started with some requirements:

  • Useful
    We wanted users to be able to see running examples along with the source code. We also wanted to allow users to experiment by making changes to the code and see the results instantly.
  • Comprehensive
    We wanted a tool that would cover all Wijmo modules and controls, to whatever level of detail we (and our users) felt appropriate. More complex controls and features should be backed by more documentation and samples.
  • Flexible
    We wanted to create something that could be expanded easily as we added more features to the product. Adding new topics or re-structuring existing ones should be easy. We wanted to be able to easily add topics or add depth to existing ones.
  • Scalable
    We wanted to create a small, efficient application in the spirit of the web: Distributed, granular, easily accessible to users and content contributors.
  • Maintainable
    We wanted the application to use the latest release of our products by default. Whenever we release a new version, the app would automatically upgrade to it. We work very hard to avoid breaking changes, and this provides even more encouragement. We also wanted to be able to add content to the application very easily, often in response to questions asked by users.
  • Contextual
    We wanted the application to show related topics together, in the same hierarchy. It should be possible to re-use content, with different items in the content outline pointing to the same topic, but viewed as part of a different context.

Architecture and Implementation

After much thought and debate, we decided on a very simple and powerful design. The LearnWijmo application would have a hierarchical table of contents on the left. Each topic in the table of contents would have a corresponding fiddle that would be displayed when the topic is selected.

That’s all. I told you it was simple!

We did discuss which fiddle tool we would use. This was a big decision because we knew we would be creating a lot of these examples, and really didn’t want to switch tools any time soon. We settled on JSFiddle because:

  • JSFiddle has been around for a while. It is mature and is consistently ranked as one of the top fiddle tools.
  • We have been using fiddles in our on-line documentation since our first release, initially as links and later embedding them. The tool has always served us well, and has become better with time.
  • JSFiddle has built-in support for TypeScript, React/JSX, and Angular 2 (2.0.0 for now). So if we decide to create fiddles to demonstrate our interop modules, that should be easy.

We also discussed which framework, if any, we would use in our fiddles. We decided not to use any, sticking to pure JavaScript whenever possible. This ensures the fiddles focus on the controls rather than on the framework. Most of the examples are so small they don’t really benefit much from frameworks.

First Release

We have been working on this concept for a few months. A lot of time was spent creating outlines that introduce each module and control in a logical and comprehensive way, and even more time creating some 300 fiddles packed with useful code examples.

Here is a screenshot of the app in case you are not connected to the web and can’t open it now:

LearnWijmoBlogGraphic2

The first public version of the LearnWijmo app covers the following Wijmo modules:

  1. Core (wijmo.js)
  2. Input (wijmo.input.js)
  3. Grid (wijmo.grid.js)
  4. Chart (wijmo.chart.js)

We have published the LearnWijmo app as a sample as well. Feel free to check the source code, it’s a good example of how to use and style our new TreeView control. The app also has an interesting routing method that does not require any frameworks or plug-ins, and supports topic-specific URLs.

We will soon add more modules, including Gauges, Navigation, and Olap. We will also keep working on the current modules, hopefully guided by input from our customers.

I hope you enjoy our new LearnWijmo tool, and that you help us improve it! Thanks!

The post LearnWijmo Released! appeared first on Wijmo.


Two ES6 Features You Should Be Using Today

$
0
0

We’ve all been following the progress of the JavaScript language, from its humble beginnings (ECMAStript 3, in 1999) to the version that is part of the HTML5 standard (ECMAScript 5, 2011), to the latest and greatest version (ECMAScript 6, aka Harmony, ES2015, ES6 Harmony) that everyone is talking about. (For more details, see https://en.wikipedia.org/wiki/ECMAScript.)

With each new release, there are tons of improvements and additions to the language. Some of these improvements are probably very relevant to your work, and could make your life easier and improve the quality of the code you write.

Unfortunately, not all are available today on all browsers, so you may have to use polyfills or tools like TypeScript or Babel if you want your apps to run in IE11 (which still has about 12% marketshare).

In our company, we have been using TypeScript for a long time, which allowed us to use essential new features such as classes and modules while still targeting ECMAScript 5 so our apps run in IE 9.

In this blog, I want to mention two ES6 features not as well-known as classes and modules, but extremely useful and usable today: arrow functions and the let keyword.

Arrow Functions

Arrow functions consist of replacing the traditional “function(parameters) {}” function expression syntax with the more succinct “() => {}” syntax. They have two main advantages:

  1. Shorter syntax, and
  2. Lexical binding for the “this” value.

The second advantage is the more interesting one. Consider these two code snippets:

// old style
this.start();
var self = this;
setTimeout(function() {
  self.stop();
}, 100);

The “self” variable is needed to keep a reference to the current context, because “this” within the function body means something else (not very well-defined). Now check out the same thing written using an arrow function:

// new style
this.start();
setTimeout(() => {
  this.stop();
}, 100);

This is shorter, and the “this” variable retains its lexical meaning as most people would expect. The result is shorter and clearer code. Enough with those “self” and “that” variables already!

Arrow functions were added to ES6 and have become one of its most popular new features. They are not supported in IE11 and earlier, but if you use TypeScript you can and should start using them today.

You can read more about arrow functions in the Mozilla Developer Network:

https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Functions

The “let” keyword

I never thought there was anything wrong with the “var” keyword. I used it all the time, and rarely ran into any issues. You know, things like:

var t = startTime;
this.on(‘tick’, () => {
  ... code that uses t ...
  if (this.altitude() <= 0) {
    var t = doSomething();
    ...
  }
});

Of course, the “t” variable declared in the function body is the same as the one declared at the top of the block. Until recently, I was happy to call this a bug, blame the developer (myself) and change the name of the inner variable.

But, there are some other issues with “var” that are less common and a lot harder to track down and fix. For example:

var messages = 'Hello,JavaScript,World'.split(',');
for (var i = 0; i < messages.length; i++) {
    setTimeout(function() {
        alert(messages[i]);
    })
}

This doesn’t work as most people would expect. It is not a bug, it is a consequence of how the “var” keyword works. Namely, there’s a single variable here, and it runs through the entire for loop before executing the first “timeOut”.

At this point you could do some research and find a solution, which will typically look like this:

for (var i = 0; i < messages.length; i++) {
    (function(index) {
        setTimeout(function() {
            alert(messages[index]);
        });
     })(i);
}

Which does work, but is nearly impossible to read (for me at least).

Oh, well... just another JavaScript quirk we have to live with, right?

Not really. I recently read an excellent article in the ES6 In Depth series: let and const. Basically, it explains that the “var” keyword has implementation quirks that could not be fixed without breaking existing code, so “let” was added to replace it. You can use it to fix the example above like this:

let messages = 'Hello,JavaScript,World'.split(',');
for (let i = 0; i < messages.length; i++) {
    setTimeout(() => {
        alert(messages[i]);
    })
}

If you simply replace all instances of “var” with “let” in your code, chances are it will run exactly as before. If you're working on a large project and using TypeScript, chances are you'll find a couple of bugs as well, like variables being re-declared as we mentioned above.

After reading the article, I switched to “let”. Now the TypeScript compiler can check more of my code and help prevent more subtle bugs before they happen.

The “let” keyword is supported by IE11 and better. If you are using TypeScript and targeting ES3, it will generate “vars” for you so your app will run in IE9 as well.

The post Two ES6 Features You Should Be Using Today appeared first on Wijmo.

Using TypeScript Enums in Angular Templates

$
0
0

TypeScript offers a very convenient feature, Enum:

It allows you to specify a possible property value from a predefined set of values using meaningful names, instead of the numeric constants that are usually used in this case. For example, we can specify the FlexChart.chartType property value with the following line of code:

chart.chartType = ChartType.Column;

But, the problem with Enum type properties arises in Angular templates, where you may want to specify their values, but can’t. The only way is to use numeric constants corresponding to the enum members, but this approach produces unclear markup, and it might be annoying to figure out their values:

<wj-flex-chart [chartType]="2" …>

The source of the problem is that it is impossible to reference anything except for the component properties in Angular template expressions. But, the properties are still available, so let’s utilize this! We can just add a property that stores a reference to the necessary Enum type, like the ChartType property here:

import * as wjcChart from 'wijmo/wijmo.chart';
…
export class AppCmp {
    ChartType = wjcChart.ChartType;

And now we can reference enum members in template bindings:

<wj-flex-chart [chartType]="ChartType.Column" …>

Wijmo specific notes

Note that this problem is not so acute when it comes to Wijmo components, where you can assign the string name of an enum member to a property. This approach will work, because all Wijmo enum type properties can convert such strings to actual enum values. For example:

<wj-flex-chart [chartType]=" 'Column' " …>

But, this approach may cause problems in case of two way binding scenarios. If you want to bind a menu of possible chart types to the chart’s chartType property, for example. You may get one of those lovely “Expression has changed after it was checked” exceptions, or a selected menu item will not be shown in the menu header. The code that uses true enum values is free of these potential problems:

Component:
chartType = wjcChart.ChartType.Column;
Template:
<wj-flex-chart [itemsSource]="data"
               [chartType]="chartType"
               [bindingX]="'country'"
               style="height: 200px">
    <wj-flex-chart-series [name]="'Downloads'" [binding]="'downloads'"></wj-flex-chart-series>
</wj-flex-chart>
<wj-menu [(value)]="chartType" [header]="'Chart Type'">
    <wj-menu-item [value]="ChartType.Column">Column</wj-menu-item>
    <wj-menu-item [value]="ChartType.Scatter">Scatter</wj-menu-item>
    <wj-menu-item [value]="ChartType.Line">Line</wj-menu-item>
</wj-menu>

The menu item values here are defined as true enum values, and the menu value property is two-way bound to the chart’s chartType property via the component’s chartType property.

Resources

The whole working example can be found here:

http://plnkr.co/edit/3Leyp7ksKqK4XJFeUxxd?p=preview

Want to try it out yourself with Wijmo? Download Wijmo 5

The post Using TypeScript Enums in Angular Templates appeared first on Wijmo.

Implementing modal dialog functions with Promise based dialog results in Angular

$
0
0

Tributes

Special thanks to Roy Drenker, who inspired me to the implementation demonstrated in this article.

The problem

When it comes to modal dialogs in your Web application, you of course want something as easy to use as the native JavaScript confirm function:

if (confirm(‘Do you agree?’)) { do_it(); }

The problem with the native alert/confirm/prompt functions is that they are very limited in their functionality and customization options.

Being an Angular developer, you might dream about the possibility of creating dialogs whose content and behavior can be defined by convenient Angular means. Perhaps showing the dialog could be wrapped in a single function call, and asynchronous processing of dialog results could be implemented with simple and clear code.

In this article, I’ll share with you a relatively simple implementation of a dialog function library that tries to match these requirements.

Dialog content and behavior in this implementation can be represented by an arbitrary Angular component. A dialog can be shown by a dedicated function that returns a dialog result as a Promise object.

Promise

This article doesn’t try to explain Promises. It supposes that you have a basic knowledge of this technology. If it’s not the case, there are plenty of good articles around that explain Promise in fine detail, for example this one:

https://developers.google.com/web/fundamentals/getting-started/primers/promises

But because the article requires very basic knowledge of Promises, I’ll remind you of these basics:
Promise is an object that allows one piece of code that waits for an asynchronous execution result (I call it Promise client) to communicate with the other piece of code that produces this result (I call it Promise provider). The Promise provider creates the Promise object, and Promise client can “subscribe” to this result using code like this:

promise.then(resultCallback).catch(rejectCallback)

The line above is non-blocking; it returns control immediately and execution continues on the next line of code.

When a Promise provider realizes that it has a result (for example an XMLHttpRequest is complete), it performs special actions that causes Promise to call Promise client’s resultCallback with result data. In a case where the Promise provider detects that something went wrong (for example an XMLHttpRequest has failed), it calls the rejectCallback with error data.

The result data passed to the resultCallback can in turn be a Promise, which means that you can create chains of the .then().catch() calls:

promise.then(result1Callback).then(result2Callback).catch(reject1Callback)
	.then(result3Callback).catch(reject2Callback)

Basic architecture

The core showComponentInPopup function

The core of the implementation is a low level showComponentInPopup function which is capable of:

  • Creating an instance of an arbitrary Angular component, initializing its properties with specific values, and showing it in a dialog window (probably modal) on a screen.
  • Returning a result of a user interaction with a dialog as a Promise.

So you could write something like this:

showComponentInPopup(dialogWindowProperties, componentToShow, componentProperties)
	.then(result => {/*process dialog result*/})
.catch(error => {/*process error*/});

Specific dialog functions

Having this showComponentInPopup function in place, we can create a library of the specific dialog functions like:

yesNoDialog('Do you really want it?')
.then(() => /*do something if Yes*/)
.catch(() => /*do something if Not*/);

or
loginDialog(ownerElement)
.then((data) => /*process successful authorization*/)
.catch((error) => /*process authorization failure*/);

…and a lot of others.

For this, we should:

  • Create a specific Angular component that will represent the dialog content, contain the result of a user’s dialog interaction, and maybe the result of the dialog’s interaction with some services.
  • Create a shortcut function that will call the showComponentInPopup function and pass the component type and its initialization property values as the function parameters.

Implementation

We’ll use the following plunker as a working example of the implementation discussed in this article:
http://plnkr.co/edit/znCi0O6Wh3x3Te7b1rqr?p=preview

showComponentInPopup function

The function, as well as the library of the shortcut functions, is implemented in the app/services/Dialogs.ts file of the sample. You can find its full code following:

import { Component, EventEmitter, Inject, ElementRef, Injector, forwardRef, ViewChild,
    Input, ComponentFactoryResolver, ViewContainerRef, NgModule} from '@angular/core';
import * as ng2Core from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import * as wjcInput from 'wijmo/wijmo.input';
import { WjInputModule } from 'wijmo/wijmo.angular2.input';
/*
* Shows an Angular component in Wijmo Popup.
*
* @param popupOptions A hash object defining Popup property values, the same as the 'options' parameter of Popup constructor.
* @param componentType The type of the component to show in popup.
* @param componentProperties A hash object containing the component property value.
* @param vcr An ViewContainerRef object of the component calling this function, can be acquired via component's
*        constructor parameter.
* @param cmpResolver A ComponentFactoryResolver object of the component calling this function, can be acquired via component's
*        constructor parameter.
*/
export function showComponentInPopup(popupOptions: any, componentType: any, componentProperties: any,
    vcr: ViewContainerRef, cmpResolver: ComponentFactoryResolver): Promise<any> {
    // create and initialize Wijmo Popup
    let hostEl = document.createElement('div'),
        popup = new wjcInput.Popup(hostEl);
    popup.hideTrigger = wjcInput.PopupTrigger.None;
    popup.initialize(popupOptions);
    // create the dialog content component instance,
    // and initialize its properties
    let cmpRef = vcr.createComponent(
        cmpResolver.resolveComponentFactory(componentType));
    if (componentProperties) {
        for (let prop in componentProperties) {
            cmpRef.instance[prop] = componentProperties[prop];
        }
    }
    // add the component to the Popup
    popup.content = cmpRef.location.nativeElement;
    // add handler to the popup.hidden event that will
    // destroy the popup and contained component
    let hiddenEh = () => {
        popup.hidden.removeHandler(hiddenEh);
        cmpRef.destroy();
        popup.owner = null;
        hostEl.parentElement.removeChild(hostEl);
        popup.dispose();
    }
    popup.hidden.addHandler(hiddenEh);
    // show the popup
    popup.show(false);
    // the function's return value, assigned with a Promise
    // representing dialog result
    let ret = (<IPromiseResult>cmpRef.instance).result;
    // Add this .then.catch branch to the returning promise
    // that hides the popup after the component's 'result'
    // will be resolved or rejected.
    // Note that this branch is not visible to the client code.
    ret.then((data) => {
        popup.hide();
        return data;
    })
    .catch((error) => {
        popup.hide();
    })
    return ret;
}

Let’s walk through this implementation and discuss some of its key parts.

Popup

We need some UI means which are capable of showing content in a modal “window”. In our example, we use a Wijmo Popup control for this purpose.

http://wijmo.com/5/docs/topic/wijmo.input.Popup.Class.html

You are free to use any other tools like Bootstrap modal popups. The first parameter of the showComponentInPopup function is popupOptions, where you can pass a hash object with Popup specific property values. For example, you may want to show a dialog anchored to some DOM element, instead of centering it on the screen. In this case, you may pass

{ owner: anchorElement }

as the parameter value, which will instruct the Popup to visually stick to the anchorElement.
The following piece of code creates and initializes a Popup instance with the passed Popup property values:
let hostEl = document.createElement('div'),
        popup = new wjcInput.Popup(hostEl);
    popup.hideTrigger = wjcInput.PopupTrigger.None;
    popup.initialize(popupOptions);
Component as a dialog content

Now we should instantiate a component which is used as dialog content and initialize its properties. The function defines two obvious parameters related to the content component – componentType, which receives the type (a reference to a constructor function) of a component, and componentProperties, where you can pass a hash object with the component property values.

But there are also two not so obvious parameters in the function definition:

vcr: ViewContainerRef,
cmpResolver: ComponentFactoryResolver

These parameters are references to the Angular services necessary to dynamically (in code) create an instance of a component. They can be obtained by injecting them as parameters of a constructor of a component that will call dialog functions. Creating components dynamically is described in the Angular documentation here:
https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html

In fact, this translates into the following line in our code:

let cmpRef = vcr.createComponent(
        cmpResolver.resolveComponentFactory(componentType));

This code creates a component and assigns the associated ComponentRef instance (holding some useful information about the component) to the cmpRef variable. The cmpRef.instance property contains a reference to the instantiated component, and cmpRef.location.nativeElement can be used to retrieve the root DOM element representing the instantiated component’s template.
After the component has been created, we assign its properties with the values passed in the componentProperties parameter:
if (componentProperties) {
        for (let prop in componentProperties) {
            cmpRef.instance[prop] = componentProperties[prop];
        }
    }

After that we add the component’s DOM to the Popup, so that the popup’s content will be the component’s template:
popup.content = cmpRef.location.nativeElement;

After that we can show the component on the screen:
popup.show(false);
Promise as a dialog result

Any JavaScript custom dialog implementation is asynchronous by its nature. You can’t implement it in the same way as the native alert, confirm, and prompt functions, where code execution is blocked until a user presses a button in the dialog. Our dialogs have to return their results at unpredictable moments dictated by a user, or even by some asynchronous services in more complex scenarios. For example, when a user presses a confirmation button in a LoginDialog, the latter could send an authentication request to the server, and wait for a response. Only after the response is received will the dialog result be ready for further processing.

The modern and probably most convenient way to handle asynchronous results is provided by Promises. If an asynchronous function result is a Promise object, the client code should just add a .then(…).catch(…) chain of calls to the function, where the handler functions processing the results are passed as parameters. This is the approach we’ll use in our implementation.

The dialog results are created by our custom dialog content components and are specific to the components’ semantics. The result can be exposed as a component property of the Promise type. Our showComponentInPopup function should know what the property is, to be able to read this result from an arbitrary component and return it as a function result. That is, we need to sign a contract between the function and the content component that will allow the function to know how to retrieve the result from the component.
We make this contract formal by introducing the following interface:

export interface IPromiseResult {
    readonly result: Promise<any>;
}

Any dialog content component should implement this interface, which will store the dialog result as a Promise object returning by the result property.

After putting this agreement into play, we can easily retrieve the dialog result from the component and return it as the function result:

    let ret = (<IPromiseResult>cmpRef.instance).result;
    return ret;

But our showComponentInPopup function must implement one more obvious piece of functionality – it has to close the popup after the dialog result is ready (that is the Promise will be resolved or rejected). It would be wrong to put this responsibility to a code that calls the function.

For this, the function adds its own .then().catch() chain to the returning Promise, where it hides the popup. The complete code snippet that deals with this looks as follows:

    let ret = (<IPromiseResult>cmpRef.instance).result;
    // Add this .then.catch branch to the returning promise
    // that hides the popup after the component's 'result'
    // will be resolved or rejected.
    // Note that this branch is not visible to the client code.
    ret.then((data) => {
        popup.hide();
    })
    .catch((error) => {
        popup.hide();
    })
    return ret;

So, the function has added a .then().catch() chain to the returning promise, and an application code that calls the function will definitely add a similar then().catch() chain to the same Promise, in order to process the dialog results. Two different chains on the same Promise? Yes, and it’s absolutely legal! With this, we come to a consideration of the technique called Promise branching. Let’s investigate it.

The diagram below graphically depicts the situation:

Promise Architecture

When result is ready (Promise resolved or rejected), then both .then().catch() chains will be executed (the topmost probably the first), and perform their job without interfering with each other – showComponentInPopup will hide the popup, and the chain in the application code will perform the required processing.

Dialog functions library

Now, having the showComponentInPopup function in place, let’s see how we can implement a dialog function library using it. The library is represented by the Dialogs class with static methods. You may find the implementation in the app/services/Dialogs.ts file.

Confirmation dialogs

This is an analogue of the native confirm function, representing a dialog with a confirmation text and two buttons (e.g. Ok/Cancel) that allows a user to confirm or reject an action. We’ll implement two different functions, okCancelDialog with Ok/Cancel buttons, and yesNoDialog with Yes/No buttons. The confirmation text is passed as the functions’ parameter.

The first step in the process of creating a dialog function is to implement a component that represents the dialog content. For this we create the OkCancelCmp component, whose code looks like:

@Component({
    selector: 'ok-cancel-cmp',
    template: `
<div style="padding:10px 10px">
        {{question}}
<div>
            <button (click)="clicked(true)">{{okCaption}}</button>
            <button (click)="clicked(false)">{{cancelCaption}}</button>
        </div>
</div>
`
})
export class OkCancelCmp implements IPromiseResult {
    question: string;
    okCaption = 'Ok';
    cancelCaption = 'Cancel';
    private _result: Promise<any>;
    private _resolve: any;
    private _reject: any;
    constructor() {
        this._result = new Promise((resolve, reject) => {
            this._resolve = resolve;
            this._reject = reject;
        });
    }
    get result(): Promise<any> {
        return this._result;
    }
    clicked(ok: boolean) {
        if (ok) {
            this._resolve();
        } else {
            this._reject();
        }
    }
}

The confirmation text and button captions are represented by the question, okCaption, and cancelCaption properties (the latter two are assigned with ‘Ok’ and ‘Cancel’ by default). They can be changed by assigning them with other values.

The dialog result, according to our contract represented by the IPromiseResult interface, is exposed as the result property of the Promise type. Because this dialog result supposes only two states, confirmed or canceled, depending on which button user has pressed, it’s convenient to consider the confirmation as a resolved Promise, and cancellation as a rejected Promise. So, an application code that uses the dialog might looks like this:

okCancelDialog('Do this?').then(okDelegate).catch(cancelDelegate);

The okDelegate function will be called if a user presses the Ok button, and cancelDelegate will be called if Cancel was pressed.

We create the Promise stored in the result property in the component constructor:

    constructor() {
        this._result = new Promise((resolve, reject) => {
            this._resolve = resolve;
            this._reject = reject;
        });
    }

The Promise constructor accepts a function with two parameters, resolve and reject, that will receive references to callback functions that client code has specified in .then() and .catch() calls. That is, provided the example above, okDelegate will be passed as the resolve parameter value, and cancelDelegate will be passed in the reject parameter.

Our responsibility as the Promise object provider is to:

  • Call resolve when Promise result is ready (i.e. user has pressed the Ok button). This will execute the delegate passed by the client code in the .then() call.
  • Call reject when Promise is rejected (i.e. user has pressed the Cancel button). This will execute the delegate passed by the client code in the .catch() call.

For this, we store resolve and reject parameter values in the component private properties (_resolve and _reject respectively). Then, we add the clicked event handler method which is bound to the buttons in the template, like here:

            <button (click)="clicked(true)">{{okCaption}}</button>
            <button (click)="clicked(false)">{{cancelCaption}}</button>

In this method we just call _resolve or _reject depending on the passed parameter value (ok or not ok).

    clicked(ok: boolean) {
        if (ok) {
            this._resolve();
        } else {
            this._reject();
        }
    }

That’s it, we’re done with the dialog content component implementation.
Now we can easily add the dialog functions.

okCancelDialog:

    static okCancelDialog(question: string, vcr: ViewContainerRef,
          cmpResolver: ComponentFactoryResolver): Promise<any> {
        return showComponentInPopup({}, OkCancelCmp,
          { question: question },
            vcr, cmpResolver);
    }

It calls showComponentInPopup and passes the content component type (OkCancelCmp) as a parameter. It also passes the component property values, only the question property value in this case, whose value it receives in the same named function parameter:
{ question: question }

And it passes ViewContainerRef and ComponentFactoryResolver objects that we discussed above, which it receives in function parameters.

yesNoDialog:

    static yesNoDialog(question: string, vcr: ViewContainerRef,
          cmpResolver: ComponentFactoryResolver): Promise<any> {
        return showComponentInPopup({}, OkCancelCmp,
          { question: question, okCaption: 'Yes', cancelCaption: 'No' },
            vcr, cmpResolver);
    }

This is a variation of the okCancelDialog function with different button captions – Yes and No. The only difference here is that in addition to the question property value, we also pass button caption values to the content component:
{ question: question, okCaption: 'Yes', cancelCaption: 'No' },
Making the library an NgModule

There is one specific nuance with dynamically created Angular components that we should address. It lies in that any component created by the ViewContainerRef.createComponent method should be added to some NgModule’s entryComponents metadata property. To satisfy this requirement, we create a special DialogsModule module in the app/services/Dialogs.ts file, which references all the components used as dialog content in our dialog function library. The definition looks like the following code:

@NgModule({
    imports: [BrowserModule, WjInputModule],
    declarations: [LoginCmp, OkCancelCmp],
    entryComponents: [LoginCmp, OkCancelCmp],
})
export class DialogsModule {
}

Having this NgModule, any application module that uses the library just needs to import DialogsModule in its NgModule definition.
Using the confirmation dialog functions in an application.

The function’s usage is demonstrated in the app/app.ts file, AppCmp component.
First, we need to import DialogsModule in the NgModule that AppCmp belongs to:

import { DialogsModule, Dialogs } from './services/Dialogs';
...
@NgModule({
    imports: [WjInputModule, WjGridModule, BrowserModule, FormsModule,
      DialogsModule],
    declarations: [AppCmp],
    providers: [DataSvc],
    bootstrap: [AppCmp]
})
export class AppModule {
}

Then, we need to obtain references to the ViewContainerRef and ComponentFactoryResolver Angular services, as was discussed above. We do it by injecting them in the component’s constructor parameters:

constructor( @Inject(ViewContainerRef) private _vcr: ViewContainerRef,
  @Inject(ComponentFactoryResolver) private _cmpResolver: ComponentFactoryResolver) {
}

Now we can use them:
    yesNoDialog() {
        Dialogs.yesNoDialog('Do you really want it?', this._vcr, this._cmpResolver)
          .then(() => alert(`You answered YES`))
          .catch(() => alert(`You answered NO`));
    }

This call will alert ‘You answered YES’ if users clicks the Yes button, and ‘You answered NO’ if user clicks the No button.
    okCancelDialog() {
        Dialogs.okCancelDialog('Do you confirm this action?',
              this._vcr, this._cmpResolver)
          .then(() => alert(`You confirmed action`))
          .catch(() => alert(`You cancelled action`));
    }

This call will alert ‘You confirmed action’ if users clicks the Ok button, and ‘You cancelled action’ if user clicks the Cancel button.
Using the confirmation dialog functions in an application

The functions usage is demonstrated in the app/app.ts file, AppCmp component.
First, we need to import the DialogsModule in the NgModule that AppCmp belongs to:

import { DialogsModule, Dialogs } from './services/Dialogs';
…
@NgModule({
    imports: [WjInputModule, WjGridModule, BrowserModule, FormsModule,
      DialogsModule],
    declarations: [AppCmp],
    providers: [DataSvc],
    bootstrap: [AppCmp]
})
export class AppModule {
}

Then, we need to obtain references to the ViewContainerRef and ComponentFactoryResolver Angular services, as was discussed above. We do it by injecting them in the component’s constructor parameters:
Login dialog simulator

This is not a fully functional login dialog implementation, for simplicity’s sake we offer just a simulator of such a dialog with the following core functionality – when a user presses a confirmation button in the dialog, the latter sends a request to a server, and depending on the authentication results (success or failure) resolves or rejects the Promise representing the dialog result.

Our login simulator dialog contains two buttons for this – Simulate success and Simulate failure.
The dialog content is represented by the LoginCmp component that has the following implementation:

export class LoginCmp implements IPromiseResult {
    private _result: Promise<any>;
    onResult = new EventEmitter();
    constructor() {
        this._result = new Promise((resolve, reject) => {
            this.onResult.subscribe((success) => {
                if (success) {
                    resolve('Done');
                } else {
                    reject('Access denied');
                }
            });
        });
    }
    get result(): Promise<any> {
        return this._result;
    }
    simulate(success: boolean) {
        setTimeout(() => {
            this.onResult.next(success);
        }, 0);
    }
}

We use a different technique here to resolve or reject the resulting promise. The component declares the onResult event:
    onResult = new EventEmitter();

This event will be triggered when authentication results have been received from a server. The event value keeps the authentication results (a success/failure boolean value in our case). The event is triggered in the simulate method, with a timeout that simulates an asynchronous communication with the server.

The result Promise is created in the constructor. The Promise callback function subscribes to the onResult event and resolves or rejects it depending on the event data value. The Promise is resolved with a data (the ‘Done’ string), or rejected with an error data (‘Access denied’), that can be inspected by the code that calls the dialog.

The content component is done, the last step is to create a dialog function, loginDialog:

    static loginDialog(anchor: HTMLElement, vcr: ViewContainerRef,
          cmpResolver: ComponentFactoryResolver): Promise<any> {
       return showComponentInPopup({ owner: anchor }, LoginCmp, {},
            vcr, cmpResolver);
    }

In contrast to the okCancelDialog function that displays the dialog centered on the screen, for loginDialog we want to be able to show it visually anchored to some element, e.g. to a button that triggered the dialog. The element can be specified in the anchor parameter, and we assign it to the owner property of the Popup by passing Popup options object as the first parameter of the showComponentInPopup function:
{ owner: anchor }

Now we can use the function in our application.

HTML:

<button #button1 (click)="loginDialog(button1)">
    Login
</button>

Click event handler:

    loginDialog(owner: HTMLElement) {
        Dialogs.loginDialog(owner, this._vcr, this._cmpResolver)
          .then((data) => alert(`Logged in! Status: ${data}`))
          .catch((error) => alert(`Login failed. Error: ${error}`));
    }

Note that we specify the button1 element as the owner of the dialog, so the dialog will appear visually anchored to the button, instead of being centered on the screen.

The .then() handler function uses the data parameter that holds a resolved promise data, and the .catch() handler function uses the error parameter containing an error information for the rejected Promise.

Conclusion

In this article, we discovered one possible way to implement modal dialogs shown by function calls, whose content and behavior are defined by arbitrary Angular components, and whose dialog results are represented by Promise objects. You can use this solution for your personal needs.

As a tool for showing dialogs we used a Wijmo Popup control:
http://wijmo.com/5/docs/topic/wijmo.input.Popup.Class.html

The article was accompanied by the following sample:
http://plnkr.co/edit/znCi0O6Wh3x3Te7b1rqr?p=preview

Get a Wijmo Free Trial>>

The post Implementing modal dialog functions with Promise based dialog results in Angular appeared first on Wijmo.

Using ASP.NET Core MVC and Web API to Render SSAS Cubes (Part 1)

$
0
0

Wijmo Enterprise includes an OLAP module for creating web-based pivot tables and charts with all data aggregation performed on the client side. While this approach is suitable for handling thousands of records, it’s not practical for large data sets containing hundreds of millions of records. Specifically, if your data is stored in SQL Server Analysis Services (SSAS) cubes, you will want to take advantage of the server-side processing that is already in place. Fortunately, as of the 2017 v1 release, Wijmo’s pivot controls can now consume SSAS cubes when used in conjunction with the ASP.NET MVC Edition of ComponentOne Studio.

Since the server-side data access assemblies depend on the full .NET Framework, you will need to use a Windows-based Microsoft development environment to achieve the functionality described here. This blog post uses Visual Studio 2017 running on Windows 10, version 1703. See Part 2 for guidance on how to implement the same functionality using Visual Studio Code, Microsoft’s free, open-source editor.

Prerequisites

If you haven’t already, download and install a version of Visual Studio 2017. If you normally use Wijmo outside of Visual Studio, you can get started with the free community edition.

Next, download and install ComponentOne Studio. Create a free GrapeCity account if you haven’t already done so, as you will need it later to generate a runtime license key. If prompted, download and install .NET Framework 3.5. During installation of ComponentOne Studio, make sure that you check the boxes labeled ASP.NET MVC and WebApi. You don’t need to perform a separate installation of Wijmo, as the necessary files are copied to your machine when you install the ASP.NET MVC Edition.

If you’re not familiar with the MVC (Model-View-Controller) pattern, check out this overview, which covers MVC design principles as well as the specifics of the ASP.NET Core implementation.

Create a New Project

To get started, open Visual Studio 2017 and select File > New > Project. When the New Project dialog opens, you will see a tree control on the left side. Select the node Installed > Templates > Visual C# > Web. You will see a list of templates with similar names.

At first glance, it’s not at all obvious which template you should choose. Just trust me on this one and select the template named ASP.NET Core Web Application (.NET Framework). Note that the equivalent template based on .NET Core will not work because the ComponentOne Web API assemblies currently depend on the full .NET Framework. Give the project a meaningful name (CubeDemo, in this example), and press OK. After the next dialog opens, choose the Web Application template and press OK. In my experience, it’s easier to add Web API functionality to an MVC application rather than the other way around.

Press F5 to build and run the application, which will open the home page in a browser.

Make note of the localhost port number assigned by Visual Studio (53217, in this example), as you will need it later to test your data connection. Close the browser, then return to Visual Studio and press Shift + F5 to stop the web server process.

Add NuGet Packages

Since you created the project from a standard Microsoft template, you will need to add the ComponentOne packages manually. In Solution Explorer, right-click the project node and select Manage NuGet Packages. Change the NuGet package source to GrapeCity, click the Browse link, and then type api into the search box. Install the package named C1.AspNetCore.Api.DataEngine.

If prompted to review changes, click OK. Note that you do not need to install the package named C1.AspNetCore.Api separately, as it will be installed as a dependency of the data engine package.

Generate GrapeCity Runtime Licenses

Before you can use the ComponentOne packages you added in the previous section, you will need to generate runtime licenses for your application. On the Visual Studio Tools menu, click GrapeCity License Manager, then enter the email address and password for your GrapeCity account. When the following screen opens, click the button named Generate App (runtime) Licenses.

On the next screen, select Eval from the Serial Number dropdown, then check the App Name box.

To complete the process, click the button named Generate App (runtime) Licenses. If all goes well, you should see a screen like the one below. Click Done to dismiss the dialog.

Register a Cube Provider

After installing the required NuGet packages and generating runtime licenses, you’re ready to register a cube data provider. Open the file Startup.cs and insert the following code before the call to app.UseStaticFiles:

app.UseDataEngineProviders().AddCube(
    "cube",
    @"Data Source=http://ssrs.componentone.com/OLAP/msmdpump.dll;Provider=msolap;Initial Catalog=AdventureWorksDW2012Multidimensional",
    "Adventure Works"
);

The AddCube method takes three parameters:

  1. The name of the cube that you will use later when specifying data service URL strings.
  2. The connection string for the cube on the server. This example uses the standard Adventure Works cube hosted on the componentone.com domain, which is accessible via anonymous authentication.
  3. The name of the cube as defined on the server.

To test the connection to the cube, press F5 to run the application, then enter the following URL in a new tab, replacing 53217 with the port number noted earlier:

http://localhost:53217/api/dataengine/cube/fields

In this URL, the api/dataengine fragment denotes a data engine provider implemented by the C1.AspNetCore.Api.DataEngine package. The cube fragment matches the first parameter in the call to AddCube. The fields fragment directs the data provider to return the cube’s schema in JSON format. If the connection is successful, the browser should display the following content:

Close the browser and stop debugging by pressing Shift + F5 in Visual Studio. At this point, you are ready to create a client test page that interacts with the server to visually render the cube.

Add a Client Test Page

Now that you have a functional server application that can access cube data, you can write a client test page containing data visualization controls that communicate with the server. Note that the project contains a web root folder named wwwroot. Any assets that you place in this folder can be accessed with a relative path to that root. This means that you can add a standard HTML page to your project and bypass the MVC conventions for client pages.

Right-click the wwwroot folder, then select New Folder. Name the new folder cube. Add a new file named index.html to this folder, and insert the following content:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="/css/site.min.css" />
    <link rel="stylesheet" href="http://cdn.wijmo.com/5.20171.300/styles/wijmo.min.css" />
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.min.js"></script>
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.input.min.js"></script>
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.grid.min.js"></script>
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.grid.filter.min.js"></script>
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.olap.min.js"></script>
</head>
<body>
    <div class="container body-content">
        <div class="row">
            <div class="col-sm-3">
                <div id="pivotPanel">
                </div>
            </div>
            <div class="col-sm-9">
                <div id="pivotGrid">
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript">
        var panel = new wijmo.olap.PivotPanel("#pivotPanel");
        var grid = new wijmo.olap.PivotGrid("#pivotGrid");
        var engine = new wijmo.olap.PivotEngine();
        engine.itemsSource = "http://localhost:5000/api/dataengine/cube";
        engine.rowFields.push(new wijmo.olap.CubePivotField(engine, "[Customer].[Country]", "Country"));
        engine.columnFields.push(new wijmo.olap.CubePivotField(engine, "[Customer].[Occupation]", "Occupation"));
        engine.valueFields.push(new wijmo.olap.CubePivotField(engine, "[Measures].[Customer Count]", "Customer Count"));
        panel.engine = engine;
        grid.itemsSource = panel;
    </script>
</body>
</html>

The <head> tag includes local references to Bootstrap stylesheets, followed by Wijmo CDN references to stylesheets and scripts. The <body> tag defines a two-column layout using Bootstrap styles, with <div> placeholders named pivotPanel and pivotGrid. The <script> block defines the behavior of the page. First, the PivotPanel and PivotGrid data visualization controls are instantiated:

var panel = new wijmo.olap.PivotPanel("#pivotPanel");
var grid = new wijmo.olap.PivotGrid("#pivotGrid");

Next, a PivotEngine is created and bound to the server instance by setting its itemsSource property to the appropriate URL:

var engine = new wijmo.olap.PivotEngine();
engine.itemsSource = "http://localhost:5000/api/dataengine/cube";

The initial settings of the row, column, and value fields are specified at the pivot engine level:

engine.rowFields.push(new wijmo.olap.CubePivotField(engine, "[Customer].[Country]", "Country"));
engine.columnFields.push(new wijmo.olap.CubePivotField(engine, "[Customer].[Occupation]", "Occupation"));
engine.valueFields.push(new wijmo.olap.CubePivotField(engine, "[Measures].[Customer Count]", "Customer Count"));

Finally, the pivot panel is bound to the pivot engine, and the pivot grid is bound to the pivot panel:

panel.engine = engine;
grid.itemsSource = panel;

One minor server tweak remains. Open the file Startup.cs and insert the following line of code before the call to app.UseStaticFiles:

app.UseDefaultFiles();

This lets you specify the path to your test page in the browser without explicitly appending the index.html filename.

Run the Project

Save all files, press F5 to run the project, and then enter the following URL in the browser:

http://localhost:5000/cube

You should see the pivot panel populated with the available fields, and the pivot grid should display rows of countries and columns of occupations.

Conclusion

In this blog post, you learned how to use ASP.NET Core MVC in conjunction with ComponentOne Studio to render SSAS cubes in Wijmo pivot controls. If you’re familiar with Wijmo, but not web development with Visual Studio, this post should give you a sense of what you can accomplish on that platform. If you already know how to develop MVC applications using the Microsoft stack, but aren’t familiar with ComponentOne Studio, this post provides a concise set of steps for adding Web API data providers capable of serving SSAS cubes.

The post Using ASP.NET Core MVC and Web API to Render SSAS Cubes (Part 1) appeared first on Wijmo.

Using ASP.NET Core MVC and Web API to Render SSAS Cubes (Part 2)

$
0
0

In Part 1 of this blog post, you learned how to use Wijmo Enterprise pivot controls in conjunction with the ASP.NET MVC Edition of ComponentOne Studio to consume and render SQL Server Analysis Services (SSAS) cubes. Part 1 used Microsoft’s full-featured, professional development environment, Visual Studio 2017. In Part 2, you will learn how to implement the same functionality using Visual Studio Code, Microsoft’s free, open-source editor.

Although there are versions of Visual Studio Code available for Linux and Mac OS X, the ComponentOne server-side data access assemblies depend on the full .NET Framework. Therefore, the steps in this tutorial will only work on a Windows machine. Windows 10 is assumed, although earlier versions should also work with the appropriate framework downloads.

Prerequisites

If you haven’t already, download and install the Windows version of Visual Studio Code.

If you have Visual Studio 2017 installed, you should already have the necessary components for .NET Core as well as reference assemblies for the .NET Framework. If you do not have Visual Studio 2017 installed, do the following:

  1. Visit the download page for .NET Core. Make sure that you choose the SDK download, not the runtime, and click the link for the Windows (x64) Installer.
  2. Determine which version of the .NET Framework you will target. Run the winver command to see which version of Windows 10 you are running. For version 1703, the framework version is 4.7. For version 1607, the framework version is 4.6.2.
  3. Visit http://microsoft.com/net/targeting and download the appropriate .NET Framework Developer Pack. (This may require a restart first.)

Finally, download and install ComponentOne Studio. Create a free GrapeCity account if you haven’t already done so, as you will need it later to generate a runtime license key. If prompted, download and install .NET Framework 3.5. During installation of ComponentOne Studio, make sure that you check the boxes labeled ASP.NET MVC and WebApi. You do not need to perform a separate installation of Wijmo, as the necessary files are copied to your machine when you install the ASP.NET MVC Edition.

If you’re not familiar with the MVC (Model-View-Controller) pattern, check out this overview, which covers MVC design principles as well as the specifics of the ASP.NET Core implementation.

Create a New Project

To get started, open a command prompt and change the current directory, if desired. Execute the following commands:

mkdir CubeDemo
cd CubeDemo
dotnet new mvc

The last line creates a new ASP.NET Core MVC project. The first time you run this command, it may take a few minutes to populate your local package cache. To open the newly created project in Visual Studio Code, run this command:

code .

When the editor has finished loading, its left pane will display the files in your project.

Click the main project file, CubeDemo.csproj, to open it for editing. If this is the first time you opened a .NET Core project in Visual Studio Code, you will see the following prompt:

Click Show Recommendations to switch to the Extensions pane:

Click the Install button for C#, then click Reload to activate it. Click Reload Window when prompted to complete the installation.

By default, the newly created project targets .NET Core 1.1. However, the ComponentOne data access assemblies require the full .NET Framework, so you will need to modify the project file. Go back to the CubeDemo.csproj tab and change the target framework from netcoreapp1.1 to net47 (or net462, depending upon which version of Windows 10 you are running), and then add the win7-x64 runtime identifier:

<PropertyGroup>
  <TargetFramework>net47</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
</PropertyGroup>

Next, open the integrated terminal in Visual Studio Code (Ctrl + `). If this is first time you executed this command, you will see the following prompt:

Click OK, Never Show Again to keep using Windows PowerShell, then type the command dotnet restore to update the NuGet packages for the new target framework.

Create a Debug Configuration

Open the Debug pane by clicking its icon. The dropdown control in the toolbar shows No Configurations, so you will need to create one.

Click the gear icon to the right of the dropdown. When prompted, select the .NET Core environment. Edit the file launch.json and replace both occurrences of:

${workspaceRoot}/bin/Debug/<target-framework>/<project-name.dll>

with the following, where the last two path fragments must match the contents of CubeDemo.csproj, which you edited earlier:

${workspaceRoot}/bin/Debug/net47/win7-x64/CubeDemo.exe

Save all open files, then press F1 to open the command palette. Type the word task, then select the command Tasks: Configure Task Runner.

A list of environments will appear. Select .NET Core from the list. Return to the integrated terminal and re-enter the command dotnet restore.

On the Debug toolbar, switch the launch target to .NET Core Launch (web), then press F5. If you see the following error message, click Close to dismiss it.

After dismissing the error, press F1 to open the command palette, then type debug, then select the command Debug: Download .NET Core Debugger. If you see the following error message, you can click Close and ignore it:

Note that the .NET Core debugger only works if the target framework is netcoreapp1.x, so you will not be able to use it with this project. However, it is necessary to download and install it to perform builds. To run your project without debugging, press Ctrl + F5. The default browser will open on the site http://localhost:5000/.

Close the browser, then return to Visual Studio Code and click Shift + F5 to stop the web server process.

Add NuGet Packages

Since Visual Studio Code doesn’t provide an integrated NuGet package manager, you will need to edit the project file directly to specify external package references. Open CubeDemo.csproj and add the following lines to the end of the existing <ItemGroup> tag:

<PackageReference Include="C1.AspNetCore.Api" Version="1.0.20171.91" />
<PackageReference Include="C1.AspNetCore.Api.DataEngine" Version="1.0.20171.91" />

After you save the project file, you should see the following prompt:

Click Restore, or return to the integrated terminal and run the dotnet restore command again.

Generate GrapeCity Runtime Licenses

Before you can use the ComponentOne packages you added in the previous section, you will need to generate runtime licenses for your application. Visit http://www.componentone.com/MyAccount/MyASPNet.aspx and enter the email address and password for your GrapeCity account. On the next screen, select Evaluation from the Serial Number dropdown, then enter CubeDemo in the App Name box.

Click the Generate button to emit a class declaration that looks something like this:

public static class License
{
    public const string Key = 
        "ABgBGAIYB4BDAHUAYgBlAEQAZQBtAG8ApRLl1hi2W7vtfkFDXJboBWYdrfKOfg5G" +
        "3TsNjAbp0UoNuYaZBgAO57j/Q6f+0aIbGMn7q4V0pdIy2Nr0fy2Y9B7RU782QCbJ" +
        "xRtMaeu4PUQLj+VaOdMJ3M2IHRMZ4qGl1NG7YXAeUtmMBUZWK6/ySM5TSgruyqlN" +
        "<<lines deleted>>" +
        "EpNEiC0XFqTaOG2cIOQPeDuF57WNJ4kFeNx4HnEkQ6bWin5spSGIEg==";
}

Copy all code from the multiline text box to the clipboard. Go back to Visual Studio Code and switch to Explorer view. Add a new file named Licenses.cs to the project, and insert the following contents:

namespace CubeDemo
{
    // paste generated License class here
}

Replace the comment line with the contents of the clipboard. Next, open the file Startup.cs and locate the ConfigureServices method. Add these lines to the beginning of the method:

C1.Web.Api.LicenseManager.Key = License.Key;
C1.Web.Api.DataEngine.LicenseManager.Key = License.Key;

At runtime, this code will apply your license key to the appropriate ComponentOne artifacts.

Register a Cube Provider

After installing the required NuGet packages and generating runtime licenses, you’re ready to register a cube data provider. Open the file Startup.cs and insert the following code before the call to app.UseStaticFiles:

app.UseDataEngineProviders().AddCube(
    "cube",
    @"Data Source=http://ssrs.componentone.com/OLAP/msmdpump.dll;Provider=msolap;Initial Catalog=AdventureWorksDW2012Multidimensional",
    "Adventure Works"
);

The AddCube method takes three parameters:

  1. The name of the cube that you will use later when specifying data service URL strings.
  2. The connection string for the cube on the server. This example uses the standard Adventure Works cube hosted on the componentone.com domain, which is accessible via anonymous authentication.
  3. The name of the cube as defined on the server.

To test the connection to the cube, press Ctrl + F5 to run the application, then enter the following URL in a new tab:

http://localhost:5000/api/dataengine/cube/fields

In this URL, the api/dataengine fragment denotes a data engine provider implemented by the C1.AspNetCore.Api.DataEngine package. The cube fragment matches the first parameter in the call to AddCube. The fields fragment directs the data provider to return the cube’s schema in JSON format. If the connection is successful, the browser should display the following content:

Close the browser and stop debugging by pressing Shift + F5 in Visual Studio Code. At this point, you’re ready to create a client test page that interacts with the server to visually render the cube.

Add a Client Test Page

Now that you have a functional server application that can access cube data, you can write a client test page containing data visualization controls that communicate with the server. Note that the project contains a web root folder named wwwroot. Any assets that you place in this folder can be accessed with a relative path to that root. This means that you can add a standard HTML page to your project and bypass the MVC conventions for client pages.

Right-click the wwwroot folder, then select New Folder. Name the new folder cube. Add a new file named index.html to this folder, and insert the following content:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="/css/site.min.css" />
    <link rel="stylesheet" href="http://cdn.wijmo.com/5.20171.300/styles/wijmo.min.css" />
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.min.js"></script>
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.input.min.js"></script>
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.grid.min.js"></script>
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.grid.filter.min.js"></script>
    <script src="http://cdn.wijmo.com/5.20171.300/controls/wijmo.olap.min.js"></script>
</head>
<body>
    <div class="container body-content">
        <div class="row">
            <div class="col-sm-3">
                <div id="pivotPanel">
                </div>
            </div>
            <div class="col-sm-9">
                <div id="pivotGrid">
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript">
        var panel = new wijmo.olap.PivotPanel("#pivotPanel");
        var grid = new wijmo.olap.PivotGrid("#pivotGrid");
        var engine = new wijmo.olap.PivotEngine();
        engine.itemsSource = "http://localhost:5000/api/dataengine/cube";
        engine.rowFields.push(new wijmo.olap.CubePivotField(engine, "[Customer].[Country]", "Country"));
        engine.columnFields.push(new wijmo.olap.CubePivotField(engine, "[Customer].[Occupation]", "Occupation"));
        engine.valueFields.push(new wijmo.olap.CubePivotField(engine, "[Measures].[Customer Count]", "Customer Count"));
        panel.engine = engine;
        grid.itemsSource = panel;
    </script>
</body>
</html>

The <head> tag includes local references to Bootstrap stylesheets, followed by Wijmo CDN references to stylesheets and scripts. The <body> tag defines a two-column layout using Bootstrap styles, with <div> placeholders named pivotPanel and pivotGrid. The <script> block defines the behavior of the page. First, the PivotPanel and PivotGrid data visualization controls are instantiated:

var panel = new wijmo.olap.PivotPanel("#pivotPanel");
var grid = new wijmo.olap.PivotGrid("#pivotGrid");

Next, a PivotEngine is created and bound to the server instance by setting its itemsSource property to the appropriate URL:

var engine = new wijmo.olap.PivotEngine();
engine.itemsSource = "http://localhost:5000/api/dataengine/cube";

The initial settings of the row, column, and value fields are specified at the pivot engine level:

engine.rowFields.push(new wijmo.olap.CubePivotField(engine, "[Customer].[Country]", "Country"));
engine.columnFields.push(new wijmo.olap.CubePivotField(engine, "[Customer].[Occupation]", "Occupation"));
engine.valueFields.push(new wijmo.olap.CubePivotField(engine, "[Measures].[Customer Count]", "Customer Count"));

Finally, the pivot panel is bound to the pivot engine, and the pivot grid is bound to the pivot panel:

panel.engine = engine;
grid.itemsSource = panel;

One minor server tweak remains. Open the file Startup.cs and insert the following line of code before the call to app.UseStaticFiles:

app.UseDefaultFiles();

This lets you specify the path to your test page in the browser without explicitly appending the index.html filename.

Run the Project

Save all files, press Ctrl + F5 to run the project, and then enter the following URL in the browser:

http://localhost:5000/cube

You should see the pivot panel populated with the available fields, and the pivot grid should display rows of countries and columns of occupations.

View Results in Visual Studio Code

As an alternative to opening a separate browser window, you can configure your project to display the target web site within Visual Studio Code itself. First, add a new file named test.md to the project folder, and insert the following content:

<div style="border:1px solid #777;position:absolute;left:10px;top:10px;bottom:10px;right:10px;"> 
<iframe src="http://localhost:5000/cube" width="100%" height="100%" frameborder="0" /> 
</div>

Next, open the Debug pane and use the toolbar to change the debug target to .NET Core Launch (console). Press Ctrl + F5 to start the web server. With test.md open, press Ctrl + Shift + V to open markdown preview in an adjacent tab. If you see a button labeled Scripts have been disabled in this document, click it, then choose Enable script execution in markdown previews for this workspace. Close the preview tab, reopen test.md, then press Ctrl + Shift + V again.

Conclusion

In this blog post, you learned how to use ASP.NET Core MVC in conjunction with ComponentOne Studio to render SSAS cubes in Wijmo pivot controls. Moreover, you learned how to accomplish this with Microsoft’s free, lightweight editor, Visual Studio Code.

 

The post Using ASP.NET Core MVC and Web API to Render SSAS Cubes (Part 2) appeared first on Wijmo.

Pivoting data in Microsoft Dynamics 365 using Wijmo OLAP

$
0
0

The numerous entities and records in the back-end that generally compose Dynamics 365 applications can make it difficult for business analysts to analyze their data on a day-to-day basis, gather useful information, and then make recommendations to business stakeholders. Although reporting tools can help, they lack the business intelligence required to extrapolate meaningful information to empower the stakeholders to make timely decisions.

A developer could devise mechanisms to help business analysts or introduce built-in reports for the business stakeholders to view the right data at the right time, but creating such reports could be a daunting task. User requirements might change from time to time based on business trends. They might also need different views of the same data, leading to the creation of several reports, with some only being used once. By devising a generic system, like a dashboard, developers could make it possible for users to create views themselves and find the data analysts they need.

This is where the Wijmo 5 OLAP control comes in.

In our previous post, we learned with how we can easily extend Microsoft Dynamics 365 using Wijmo 5 Controls — FlexGrid and FlexChart — to create applications customized to your end user’s needs and how we can analyze data from CRM-specific entities. We also talked about how we could use Wijmo 5’s OLAP control to analyze and drilldown. Here, we’ll put our words into action and showcase the analytical capabilities of the Wijmo 5 OLAP control.

olap-with-dynamics-1

We’ll learn how to:

  1. Fetch Data from an invoice entity
  2. Pivot data on Invoice and create views
  3. Save and load views

Let’s Get Started

First, we’ll create an invoice dashboard using OLAP and FlexGrid.

Dashboard Features and Components:
There will be two tabs to display/analyze data and a side panel to list the defined views:

Tab 1:
For pivoting and analyzing data: This will be used to pivot data from the invoices. Users can pivot data however they need to. Here, we’ll use the PivotGrid control.
Tab 2:
For tabular display of raw data: Displays raw data from the invoices entity. Here, we’ll use the FlexGrid control.
Save Button:
For users to save their custom views.
Side Panel:
For listing all of the saved views.

Even though creating the dashboard involves the same steps we elaborated on in our previous post, but we’ll briefly summarize the steps here.

Step 1: Creating the HTML page

Our HTML page needs these Wijmo components: OLAP Grid, OLAP Panel, and FlexGrid. We’ll use the Bootstrap Angular UI framework to create our tabs.

Step 2: Add JavaScript code to perform the read operation on the invoice entity and plug data from Dynamics 365 to OLAP

Here, we’ll use Dynamics 365 Web API to fetch records from the invoice entity. We’ve selected specific fields from the invoice entity that we’ll use to pivot data on it.
Once the data is extracted from Invoices, we’ll bind it to FlexGrid to display the raw data. We’ll use this raw data to pivot data using the OLAP control.

We’ve created a single file which contains both HTML & the JavaScript controller. We used a different approach than in our last post to save some loading time.

gc_olapview.html – Code Snippets

The code snippets given below only highlight the core functionality needed to create the application. The entire code for gc_olapview.html can be found in the project link below.

Wijmo 5 & Angular Dependencies
    <!-- Angular/Bootstrap -->
    <script src="gc_angular.min.js"></script>
    <script src="gc_uibootstrap"></script>
    <link rel="stylesheet" type="text/css" href="gc_bootstrap.min.css" />
    <!-- Angular/Bootstrap -->
    <!-- Wijmo -->
    <link href="gc_wijmo.min.css" rel="stylesheet" />
    <script src="gc_wijmo.min.js"></script>
    <script src="gc_wijmo.input.min.js"></script>
    <script src="gc_wijmo.grid.min.js"></script>
    <script src="gc_wijmo.grid.filter.min.js"></script>
    <script src="gc_wijmo.olap.min.js"></script>
    <script src="gc_wijmo.grid.grouppanel.min.js"></script>
    <script src="gc_wijmo.angular.min.js"></script>
Angular App Reference & Wijmo Dependency Injection
var app = angular.module('app', ['wj', 'ui.bootstrap']);
Data Service to Fetch Data from Invoice Entity
        app.service('DataService', function ($q) {
            this.retrieveRecords = function () {
                return $q(function (resolve, reject) {
                    var clientURL = Xrm.Page.context.getClientUrl();
                    var req = new XMLHttpRequest()
                    var query = "/api/data/v8.0/invoices?$select=totalamount,billto_postalcode,billto_country,datedelivered,discountpercentage,totaltax,totaldiscountamount,name&$top=1000";
                    req.open("GET", encodeURI(clientURL + query), true);
                    req.setRequestHeader("Accept", "application/json");
                    req.setRequestHeader("Content-Type", "application/json;charset=utf-8");
                    req.setRequestHeader("OData-MaxVersion", "4.0");
                    req.setRequestHeader("OData-Version", "4.0");
                    req.onreadystatechange = function () {
                        if (this.readyState == 4) {
                            req.onreadystatechange = null;
                            if (this.status == 200) {
                                var data = JSON.parse(this.response);
                                if (data && data.value) {
                                    resolve(data.value);
                                }
                            }
                            else {
                                var error = JSON.parse(this.response).error;
                                reject(error);
                            }
                        }
                    };
                    req.send(null);
                });
            }
        });
Code for Controller
        app.controller('appCtrl', function ($scope, $timeout, $uibModal, $log, $q, DataService) {
            $scope.pivotgrid = [];
            $scope.flxgrid = [];
            $scope.viewdefs = [];
            $scope.thePanel = null;
            // Parse the Invoice Shipped date and get Month and Year
            $scope.AddTimeInformation = function (data) {
                data.forEach(function (item, index) {
                    var dateofinvoice = new Date(item.datedelivered);
                    data[index].datedelivered = dateofinvoice
                    data[index].Year = dateofinvoice.getFullYear().toString();
                    data[index].Month = monthNames[dateofinvoice.getMonth()];
                })
                return data;
            }
Load Invoices Data from Dynamics 365
            $scope.GetData = function () {
                var promise = DataService.retrieveRecords();
                promise.then(function (response) {
                    response = $scope.AddTimeInformation(response)
                    $scope.data = new wijmo.collections.CollectionView(response);
                    $scope.data.trackChanges = true;
                }, function (reason) {
                    alert(reason)
                });
            }
Save View to Local Storage
       $scope.SaveView = function () {
                $scope.viewCounter = $scope.viewCounter + 1
                var definiation =
                {
                    "def": $scope.thePanel.engine.viewDefinition,
                    "name": "CustomView" + $scope.viewCounter
                }
                if ($scope.viewdefs == null) {
                    $scope.viewdefs = [];
                }
                $scope.viewdefs.push(definiation);
                localStorage.setItem('viewdefs', JSON.stringify($scope.viewdefs));
            }
Load View from Local Storage
            $scope.LoadViews = function () {
                var retrievedObject = localStorage.getItem('viewdefs');

HTML Markup

Pivot Panel & Pivot Grid
<div class="col-md-3" style="">
                            <wj-pivot-panel items-source="data" control="thePanel" initialized="initPivotPanel(s,e)">
                            </wj-pivot-panel>
                        </div>
<div class="col-md-9">
                            <wj-pivot-grid control="pivotGrid"
                                           items-source="thePanel"
                                           show-selected-headers="All"
                                           initialized="initPivotDataGrid(s,e)">
                            </wj-pivot-grid>
                        </div>
</div>

Step 3: Deploying Code & Dependencies to CRM

Please refer to Step 3 of our previous post: we need to perform the exact steps that we outlined there, but with a few minor changes.

  1. Upload Wijmo.Olap.min.js as a web resource.
  2. Upload Angular UI Bootstrap JS(gc_uibootstrap.js) as a web resource. It’s required for rendering the Tab control in Angular applications.
  3. Upload gc_olapview.html as Web Resource. It contains Script and HTML for rendering OLAP and the FlexGrid control.

Step 4: Entry Point

Take note of the URL field for gc_OlapView.html: this serves as the entry point for the invoice pivot dashboard.
In our sample, the entry point is:
https://dtdustest.crm.dynamics.com//WebResources/gc_OlapView

Step 5: Open the Invoice Pivot Dashboard

Invoices Pivot Dashboard

Raw Data- The Raw Data Tab displays the data that’s been fetched from the invoices entity. This data is populated into FlexGrid.

raw-data

Additionally, we can perform grouping, filtering and sorting operations on the raw data.

raw-data-2

Pivoting Data on Invoices

This is where all the action takes place. The Pivot Grid Tab consists of a Pivot panel and a Pivot Grid.

Pivot Panel

This panel displays all fields from the invoices entity (the raw data). Users can drag and drop fields into columns, rows, and values sections to create views based on the data set.
Additionally, users can also use filters and sort on these fields. If you’re familiar with an OLAP Engine, you have a sense of how easy it is to pivot data.

Pivot Grid

The Pivot Grid displays the pivoted data based on the fields we added to the Pivot panel.
We can also perform sort operation on the columns and view the underlying data for a pivot point.

pivot-grid-1

pivot-grid-2

Adding Custom Views

You can preserve the pivoted data to view it again by saving these views. In our shared example, we used a bit of code to save views created by the users. This is because we’re storing them to local storage. But in real applications, we can save the views to custom CRM entities against specific users and load them when the Pivot Grid is loaded.

adding-custom-views

For more information about Wijmo 5 controls, check out our Sample Explorer and Docs. You can also download a free trial of Wijmo 5.


Project Files

Source Code Project: WijmoSampleSolution_1_0_0_0
This project contains all the source files for the invoice pivot dashboard.
Dynamics 365 Solution: OlapWithDynamics365
We also created a MSCRM solution which can be directly imported into your CRM Instance and you can see the pivot dashboard in action on your end.

The post Pivoting data in Microsoft Dynamics 365 using Wijmo OLAP appeared first on Wijmo.

Building Great Web Forms with Wijmo

$
0
0
Most web applications use forms to get data from users. Whether the form is made of text boxes, toggles, buttons, checkboxes, or custom controls, web developers need to be purposeful about forms to make users happy and increase conversions.

2016 Rio: Summer Games Olympic Medal Tracker

How to Filter Hierarchical Data in FlexGrid and Angular

$
0
0
Filtering is an important feature for large data sets and may vary depending on the structure of the data being filtered. In this post, we'll walk through filtering hierarchical data in the FlexGrid control in an Angular application.

Integrating Wijmo Controls with Module Loaders and Bundlers

$
0
0
We’ve recently received a barrage of questions regarding Wijmo integration with various module loaders and bundlers (like SystemJS, WebPack, Angular CLI, etc), which is possible, but requires a number of extra steps. We also received a question about Wijmo npm installation, which is tightly coupled with the former.

Data Validation in a JavaScript Data Grid

$
0
0
Data validation is an important task for any applications that accept user input. In build 212, we added data validation to the CollectionView class, which can be used by all Wijmo controls that support data collections, including the FlexGrid.

How to Create a Dynamic Dashboard in React

$
0
0
A while ago one of our customers asked for a sample showing how to create a “dynamic dashboard”—one like those you can create in the Google Analytics app. The image below shows an example:

How to add HTML5 Report Viewer to a Web App

$
0
0
C1 Web API Report Services enables you to build HTTP services, which can be consumed by a variety of clients for viewing, loading and caching reports, including the Wijmo ReportViewer. These are REST-based API services, which communicated with HTML 5 ReportViewer control to display the report content on the web.

Rethinking the Periodic Table with Wijmo's Sunburst


How to export selected rows of FlexGrid to a PDF file

$
0
0
Recently, I received an inquiry about exporting selected data from a FlexGrid to a PDF document. I found this interesting. While we often brag about the amount of data the FlexGrid can hold, in this case,the user needed only a portion of the data.

Extend Microsoft Dynamics 365 with Wijmo 5 Controls

$
0
0
Microsoft Dynamics 365, a popular enterprise business solution designed to manage business processes, has a plethora of features that fulfill most business needs. But because every business is different, each enterprise generally requires customization. Fortunately, the customizations are straightforward, and can be done through plugins, actions, and web resources.

Create a Quick-Start Application with Angular and Wijmo

$
0
0
This blog was originally published on February 14, 2017 for Wijmo Japan.

Angular Quick Start: Data Binding with Wijmo Controls

$
0
0
This blog was originally published on February 22, 2017 for Wijmo Japan.

Create a Web Application for Performing CRUD Operations Using JavaScript and Web API

$
0
0
This post was originally published on June 6, 2016 for Wijmo Japan
Viewing all 93 articles
Browse latest View live