How to Make GraphQL and DynamoDB Play Nicely Together
Strategy

How to Make GraphQL and DynamoDB Play Nicely Together


Serverless, GraphQL, and DynamoDB are a powerful combination for building websites. The first two are well-loved, but DynamoDB is often misunderstood or actively avoided. It’s often dismissed by folks who consider it only worth the effort “at scale.”

That was my assumption, too, and I tried to stick with a SQL database for my serverless apps. But after learning and using DynamoDB, I see the benefits of it for projects of any scale.

To show you what I mean, let’s build an API from start to finish — without any heavy Object Relational Mapper (ORM) or GraphQL framework to hide what is really going on. Maybe when we’re done you might consider giving DynamoDB a second look. I think it is worth the effort.

The main objections to DynamoDB and GraphQL

The main objection to DynamoDB is that it is hard to learn, but few people argue about its power. I agree the learning curve feels very steep. But SQL databases are not the best fit with serverless applications. Where do you stand up that SQL database? How do you manage connections to it? These things just don’t mesh with the serverless model very well. DynamoDB is serverless-friendly by design. You are trading the up-front pain of learning something hard to save yourself from future pain. Future pain that only grows if your application grows.

The case against using GraphQL with DynamoDB is a little more nuanced. GraphQL seems to fit well with relational databases partly because it is assumed by a lot of the documentation, tutorials, and examples. Alex Debrie is a DynamoDB expert who wrote The DynamoDB Book which is a great resource to deeply learn it. Even he recommends against using the two together, mostly because of the way that GraphQL resolvers are often written as sequential independent database calls that can result in excessive database reads.

Another potential problem is that DynamoDB works best when you know your access patterns beforehand. One of the strengths of GraphQL is that it can handle arbitrary queries more easily by design than REST. This is more of a problem with a public API where users can write arbitrary queries. In reality, GraphQL is often used for private APIs where you control both the client and the server. In this case, you know and can control the queries you run. With a GraphQL API it is possible to write queries that clobber any database without taking steps to avoid them.

A basic data model

For this example API, we will model an organization with teams, users, and certifications. The entity relational diagram is shown below. Each team has many users and each user can have many certifications.

Relational database model

Our end goal is to model this data in a DynamoDB table, but if we did model it in a SQL database, it would look like the following diagram:

To represent the many-to-many relationship of users to certifications, we add an intermediate table called “Credential.” The only unique attribute on this table is the expiration date. There would be other attributes for each of the tables, but we reduce it to just a name for each for simplicity.

Access patterns

The key to designing a data model for DynamoDB is to know your access patterns up front. In a relational database you start with normalized data and perform joins across the data to access it. DynamoDB does not have joins, so we build a data model that matches how we intend to access it. This is an iterative process. The goal is to identify the most frequent patterns to start. Most of these will directly map to a GraphQL query, but some may be only used internally to the back end to authenticate or check permissions, etc. An access pattern that is rarely used, like a check run once a week by an administrator, does not need to be designed. Something very inefficient (like a table scan) can handle these queries.

Most frequently accessed:

  • User by ID or name
  • Team by ID or name
  • Certification by ID or name

Frequently accessed:

  • All Users on a Team by Team ID
  • All Certifications for a given User
  • All Teams
  • All Certifications

Rarely accessed

  • All Certifications of users on a Team
  • All Users who have a Certification
  • All Users who have a Certification on a Team

DynamoDB single table design

DynamoDB does not have joins and you can only query based on the primary key or predefined indexes. There is no set schema for items imposed by the database, so many different types of items can be stored in a single table. In fact, the recommended best practice for your data schema is to store all items in a single table so that you can access related items together with a single query. Below is a single table model representing our data. To design this schema, you take the access patterns above and choose attributes for the keys and indexes that match.

The primary key here is a composite of the partition/hash key (pk) and the sort key (sk). To retrieve an item in DynamoDB, you must specify the partition key exactly and either a single value or a range of values for the sort key. This allows you to retrieve more than one item if they share a partition key. The indexes here are shown as gsi1pk, gsi1sk, etc. These generic attribute names are used for the indexes (i.e. gsi1pk) so that the same index can be used to access different types of items with different access pattern. With a composite key, the sort key cannot be empty, so we use “#” as a placeholder when the sort key is not needed.

Access pattern Query conditions
Team, User, or Certification by ID   Primary Key, pk=”T#”+ID, sk=”#”  
Team, User, or Certification by name Index GSI 1, gsi1pk=type, gsi1sk=name
All Teams, Users, or Certifications   Index GSI 1, gsi1pk=type    
All Users on a Team by ID Index GSI 2, gsi2pk=”T#”+teamID
All Certifications for a User by ID Primary Key, pk=”U#”+userID, sk=”C#”+certID
All Users with a Certification by ID Index GSI 1, gsi1pk=”C#”+certID, gsi1sk=”U#”+userID

Database schema

We enforce the “database schema” in the application. The DynamoDB API is powerful, but also verbose and complicated. Many people jump directly to using an ORM to simplify it. Here, we will directly access the database using the helper functions below to create the schema for the Team item.

const DB_MAP = {
  TEAM: {
    get: ({ teamId }) => ({
      pk: 'T#'+teamId,
      sk: '#',
    }),
    put: ({ teamId, teamName }) => ({
      pk: 'T#'+teamId,
      sk: '#',
      gsi1pk: 'Team',
      gsi1sk: teamName,
      _tp: 'Team',
      tn: teamName,
    }),
    parse: ({ pk, tn, _tp }) => {
      if (_tp === 'Team') {
        return {
          id: pk.slice(2),
          name: tn,
          };
        } else return null;
        },
    queryByName: ({ teamName }) => ({
      IndexName: 'gsi1pk-gsi1sk-index',
      ExpressionAttributeNames: { '#p': 'gsi1pk', '#s': 'gsi1sk' },
      KeyConditionExpression: '#p = :p AND #s = :s',
      ExpressionAttributeValues: { ':p': 'Team', ':s': teamName },
      ScanIndexForward: true,
    }),
    queryAll: {
      IndexName: 'gsi1pk-gsi1sk-index',
      ExpressionAttributeNames: { '#p': 'gsi1pk' },
      KeyConditionExpression: '#p = :p ',
      ExpressionAttributeValues: { ':p': 'Team' },
      ScanIndexForward: true,
    },
  },
  parseList: (list, type) => {
    if (Array.isArray(list)) {
      return list.map(i => DB_MAP[type].parse(i));
    }
    if (Array.isArray(list.Items)) {
      return list.Items.map(i => DB_MAP[type].parse(i));
    }
  },
};

To put a new team item in the database you call:

DB_MAP.TEAM.put({teamId:"t_01",teamName:"North Team"})

This forms the index and key values that are passed to the database API. The parse method takes an item from the database and translates it back to the application model.

GraphQL schema

type Team {
  id: ID!
  name: String
  members: [User]
}
type User {
  id: ID!
  name: String
  team: Team
  credentials: [Credential]
}
type Certification {
  id: ID!
  name: String
}
type Credential {
  id: ID!
  user: User
  certification: Certification
  expiration: String
}
type Query {
  team(id: ID!): Team
  teamByName(name: String!): [Team]
  user(id: ID!): User
  userByName(name: String!): [User]
  certification(id: ID!): Certification
  certificationByName(name: String!): [Certification]
  allTeams: [Team]
  allCertifications: [Certification]
  allUsers: [User]
}

Bridging the gap between GraphQL and DynamoDB with resolvers

Resolvers are where a GraphQL query is executed. You can get a long way in GraphQL without ever writing a resolver. But to build our API, we’ll need to write some. For each query in the GraphQL schema above there is a root resolver below (only the team resolvers are shown here). This root resolver returns either a promise or an object with part of the query results.

If the query returns a Team type as the result, then execution is passed down to the Team type resolver. That resolver has a function for each of the values in a Team. If there is no resolver for a given value (i.e. id), it will look to see if the root resolver already passed it down.

A query takes four arguments. The first, called root or parent, is an object passed down from the resolver above with any partial results. The second, called args, contains the arguments passed to the query. The third, called context, can contain anything the application needs to resolve the query. In this case, we add a reference for the database to the context. The final argument, called info, is not used here. It contains more details about the query (like an abstract syntax tree).

In the resolvers below, ctx.db.singletable is the reference to the DynamoDB table that contains all the data. The get and query methods directly execute against the database and the DB_MAP.TEAM.... translates the schema to the database using the helper functions we wrote earlier. The parse method translates the data back to the from needed for the GraphQL schema.

const resolverMap = {
  Query: {
    team: (root, args, ctx, info) => {
      return ctx.db.singletable.get(DB_MAP.TEAM.get({ teamId: args.id }))
        .then(data => DB_MAP.TEAM.parse(data));
    },
    teamByName: (root, args, ctx, info) =>; {
      return ctx.db.singletable
        .query(DB_MAP.TEAM.queryByName({ teamName: args.name }))
        .then(data => DB_MAP.parseList(data, 'TEAM'));
    },
    allTeams: (root, args, ctx, info) => {
      return ctx.db.singletable.query(DB_MAP.TEAM.queryAll)
        .then(data => DB_MAP.parseList(data, 'TEAM'));
    },
  },
  Team: {
    name: (root, _, ctx) => {
      if (root.name) {
        return root.name;
      } else {
        return ctx.db.singletable.get(DB_MAP.TEAM.get({ teamId: root.id }))
          .then(data => DB_MAP.TEAM.parse(data).name);
      }
    },
    members: (root, _, ctx) => {
      return ctx.db.singletable
        .query(DB_MAP.USER.queryByTeamId({ teamId: root.id }))
        .then(data => DB_MAP.parseList(data, 'USER'));
    },
  },
  User: {
    name: (root, _, ctx) => {
      if (root.name) {
        return root.name;
      } else {
        return ctx.db.singletable.get(DB_MAP.USER.get({ userId: root.id }))
          .then(data => DB_MAP.USER.parse(data).name);
      }
    },
    credentials: (root, _, ctx) => {
      return ctx.db.singletable
        .query(DB_MAP.CREDENTIAL.queryByUserId({ userId: root.id }))
        .then(data =>DB_MAP.parseList(data, 'CREDENTIAL'));
    },
  },
};

Now let’s follow the execution of the query below. First, the team root resolver reads the team by id and returns id and name. Then the Team type resolver reads all the members of that team. Then the User type resolver is called for each user to get all of their credentials and certifications. If there are five members on the team and each member has five credentials, that results in a total of seven reads for the database. You could argue that is too many. In a SQL database this might be reduced to four database calls. I would argue that the seven DynamoDB reads will be cheaper and faster than the four SQL reads in many cases. But this comes with a big dose of “it depends” on a lot of factors.

query { team( id:"t_01" ){
  id
  name
  members{
    id
    name
    credentials{
      id
      certification{
        id
        name
      }
    }
  }
}}

Over-fetching and the N+1 problem

Optimizing a GraphQL API involves balancing a whole lot of tradeoffs that we won’t get into here. But two that weigh heavily in the decision of DynamoDB versus SQL are over-fetching and the N+1 problem. In many ways, these are opposite sides of the same coin. Over-fetching is when a resolver requests more data from the database than it needs to respond to the query. This often happens when you try to make one call to the database in the root resolver or a type resolver (e.g., members in the Team type resolver above) to get as much of the data as you can. If the query did not request the name attribute, it can be seen as wasted effort.

The N+1 problem is almost the opposite. If all the reads are pushed down to the lowest level resolver, then the team root resolver and the members resolver (for Team type) would make only a minimal or no request to the database. They would just pass the IDs down to the Team type and User type resolver. In this case, instead of members making one call to get all five members, it would push down to User to make five separate reads. This would result in potentially 36 or more separate reads for the query above. In practice, this does not happen because an optimized server would use something like the DataLoader library that acts as a middleware to intercept those 36 calls and batch them into probably only four calls to the database. These smaller atomic read requests are needed so that the DataLoader (or similar tool) can efficiently batch them into fewer reads.

So, to optimize a GraphQL API with SQL, it is usually best to have small resolvers at the lowest levels and use something like DataLoader to optimize them. But for a DynamoDB API it is better to have “smarter” resolvers higher up that better match the access patterns your single table database it written for. The over-fetching that results in this case is usually the lesser of the two evils.

Deploy this example in 60 seconds

This is where you realize the full payoff of using DynamoDB together with serverless GraphQL. I built this example with Architect. It is an open-source tool to build serverless apps on AWS without most of the headaches of directly using AWS. Once you clone the repo and run npm install, you can launch the app for local development (including a built-in local version of the database) with a single command. Not only that, you can also deploy it straight to production infrastructure (including DynamoDB) on AWS with a single command when you are ready.



Source link

r/graphic_design - Looking to hire a graphic designer to rebrand a funfair
Strategy

Looking to hire a graphic designer to rebrand a funfair : gr…


I have a sizeable client that is in need of a rebrand as well as digital assets for the website etc. They’re needing to appeal to families and teenagers and would like their event to appear somewhat otherworldly and utopian. For reference, they like the style of the attached graphic (not the logo).

I’d appreciate some links to relevant work in the comments and if you’d like more details/to see their current branding, please give me a message.

r/graphic_design - Looking to hire a graphic designer to rebrand a funfair



Source link

Orgchart Raw
Strategy

Orgchart With CSS Flex and ZK


I invested a little time to “play” with CSS Flex to see if/how a simple organization chart can be styled using CSS Flex, mostly without CSS.

In order to create the hierarchy dynamically, I’ll show how ZK Features can be leveraged – even though the example HTML/CSS are not ZK Framework specific and can be generated by any framework of your choice.

Flexible Orgchart Markup

The starting point is an ordinary, unordered html-lists (<ul>/<li>as they conveniently allow nesting arbitrary hierarchies.

orgchart-raw.zul

<div xmlns="xhtml">
  <ul>
    <li>
      Company
      <ul>
        <li>
          Sales Department
          <ul>
            <li>Sales Unit I</li>
            <li>Sales Unit II</li>
          </ul>
        </li>
        <li>Purchase Department</li>
        <li>
          Controlling
          <ul>
            <li>Controlling Unit I</li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</div>

The browser will render something like the image below, already conveying the information needed – like in the good old ’90s.

Orgchart Raw

An orgchart is structurally the same: a Tree. We “just” need to align the items horizontally and connect the nodes somehow. There are definitely several libraries available using JavaScript/Canvas/SVG that will do the job.

The challenge here is: how far can we get with pure HTML/CSS for the layout – to ease markup generation with any templating engines or UI frameworks.

With the previous case, we can do this: (still coder’s “art”, nicer examples will follow)

Image title

As you can see the nodes consume the available space. The text is allowed to line-break when necessary to avoid a horizontal scrollbar as long as possible. The connecting edges adjust without JS (using responsive CSS media queries we could adjust even further but that’s a bit too much for this article).

Let’s see how this is done:

In order to add the styles, orgchart-simple.zul makes some minor adjustments to the markup, besides adding the CSS stylesheets.

<?style src="https://dzone.com/css/orgchart.css"?>
<?style src="css/simple.css"?>
<!--<?style src="css/debug.css"?>-->
<zk>
  <div xmlns="xhtml" class="centered">
    <ul class="orgchart">
      <li class="root">
        <div class="nodecontent">Company</div>
        <ul>
          <li>
            <div class="nodecontent">Sales Department</div>
            <ul>
              <li class="leaf">
                <div class="nodecontent">Sales Unit I</div>
              </li>
              <li class="leaf">
                <div class="nodecontent">Sales Unit II</div>
              </li>
              ...

(Don’t be distracted by ZK’s syntax to add stylesheets and the XHTML namespace declaration – the HTML/CSS is what’s relevant right now).

The main differences (besides the added stylesheets) are the CSS classes for the root and leaf nodes, and the divs with class nodecontent

simple.css just defines the nodecontent classes – here a typical flat box with round corners.

orgchart.css is arranging the  ul/li elements as a tree – taking into account any size the.nodecontentdivs might have.

The relevant CSS rules to arrange the nodes for the tree structure are:

/*align children horizontally using CSS flex*/
.orgchart ul {
    display: flex;
}

/*align nodecontent and children list vertically*/
.orgchart li {
    display: flex;
    flex-direction: column;
    position: relative;
}

/*arrange the nodecontent centered above the children list*/
.orgchart .nodecontent {
    align-self: center;
    position: relative;
    margin: 20px 5px;
}

After adding some debugging styles (debug.css) the layout nesting becomes visible.

Image title

The ul elements are surrounded by a dark blue border (showing the horizontal arrangement).
The li elements are green highlighting their adaptive widths.
The pink borders surround the .nodecontentdivs which are centered horizontally while line breaks increase the height if needed.

Furthermore, the boxes also expose how the connectors work. Relative to the size of containing  li-elements, the connecting lines are mostly located at very basic positions (center, left or right) and have a fixed height covering the 20px margin of the .nodecontent-divs. Their width is either 100% or adaptive calc(50%+1px) (1px because of the border width of 2px). These relative widths allow an arbitrary combination of node sizes and a flexible number of children without JS calculations.

The connecting lines are rendered using partial borders of CSS pseudo elements ::before and  ::after. Below an excerpt of orgchart.css with the rules for the vertical top and bottom connectors.

.orgchart .nodecontent::after,
.orgchart .nodecontent::before {
    box-sizing: border-box;
    content: '';
    position: absolute;
    z-index: -1;
    border: 0px solid #CCCCCC;
}

...

.orgchart .nodecontent::after,
.orgchart .nodecontent::before {
    border-left-width: 2px;
    width: 2px;
    height: 20px;
    left: calc(50% - 1px);
}

.orgchart .nodecontent::before {
    top: -20px;
}
.orgchart .nodecontent::after {
    bottom: -20px;
}

The full stylesheet (orgchart.css) contains additional rules to hide connectors for root and leaf nodes, as well as the curved connectors (using border-radius) for the :first-child and :last-child li-elements.

.orgchart li:first-child::before {
    width: calc(50% + 1px);
    right: 0;
    border-left-width: 2px;
    border-top-left-radius: 7px;
}

.orgchart li:last-child::before {
    width: calc(50% + 1px);
    border-right-width: 2px;
    border-top-right-radius: 7px;
}

These styles are quite fine-tuned. They would definitely benefit from a CSS preprocessor such as LESS or SASS to replace recurring numbers such as the 20px height or the 2px line width. Also, they cause some sub-pixel misalignments at certain odd zoom levels (e.g. 190% varying between browsers) – however the picture never gets destroyed by zooming in/out and the connections remain stable.

Scaling Up a Bit

Obviously, the previous example was only suitable to demonstrate the basics and you’d rarely need an orgchart for such a trivial company hierarchy anyway. So how about this instead:

orgchart-bigger.zul

Image title

(source Wikipedia)

Using the same styles as above you can see the limitations quite clearly: it may be suitable for ultra-wide screens but it doesn’t really take advantage of the screen height – and with orgcharts we often have those vertical structures in mind anyway – e.g. team members below a supervisor.

In addition, to save space, it would be nice to have something like this (a fully squeezed version of orgchart-vertical.zul)

Image title

Now the same information is represented in a more compact way. The only difference is an additional optional stylesheet (orgchart-vertical.css) which contains slightly more complicated rules for the connectors of vertical nodes.

The HTML simply requires a CSS class to apply to the vertical layout for a specific node and children list.

<ul class="orgchart">
  <li class="root">
    <div class="nodecontent">President</div>
    <ul>
      <li class="node">
        <div class="nodecontent">Vice President <br/> Account Services</div>
        <ul>
          <li class="vertical">
            <div class="nodecontent">Account Supervisor</div>
            <ul>
              <li class="leaf">
                <div class="nodecontent">Account Executive</div>
              </li>
              <li class="leaf">
                <div class="nodecontent">Account Executive</div>
              </li>
            </ul>
          </li>
          ...

In line 8, the class="vertical" is the only thing that needs to be added in order to arrange the children of this node vertically. The same style class is added for the remaining 3 supervisor nodes in this chart.

The current styles only address leaf nodes below a vertical node – any further nesting will break the layout. I am sure this would be technically possible – for the sake of brevity and readability I stopped there.

Dynamic Rendering

This section is about rendering the chart using a Java object model and ZK’s shadow elements with templates. Similar solutions will exist for other templating engines as long as they generate the same markup. If you’re just interested in the final colorful result skip to the end and just inspect the generated HTML in the browser.

Model/Template-Based Rendering

The model consists of OrgNode and OrgNodeData objects. The former contains the children collection, the isLeaf and isVertical properties as well as a generic the data object.

public class OrgNode<T> {
    private T data;
    private List<OrgNode<T>> children;
    /*constructors omitted*/
    public boolean isLeaf() { ... }
    public boolean isVertical() { ... }
    public T getData() { return data; }
    public List<OrgNode<T>> getChildren() { return children; }
}

The latter (OrgNodeData) holds the actual information to render as the node content: title, name, icon, type (used for node-specific styling). 

public class OrgNodeData {
    public enum Type {PRESIDENT, VICE_PRESIDENT, SUPERVISOR, EMPLOYEE}

    private Type type;
    private String title;
    private String name;
    private String icon;
    ...

Using this simple structure we can represent the organization hierarchy:

OrgChartViewModel.java

public class OrgChartViewModel {
    private static final String NO_IMAGE = null;
    private OrgNode<OrgNodeData> orgChartRoot;

    @Init
    public void init() {
        orgChartRoot = createOrgNode(PRESIDENT, "President", null, "icon/icon1.svg",
            createOrgNode(VICE_PRESIDENT, "Vice President", "Account Services", "icon/icon2.svg",
                createOrgNode(SUPERVISOR, "Account Supervisor", null, NO_IMAGE,
                    createOrgNode(EMPLOYEE, "Account Executive", null, NO_IMAGE),
                    createOrgNode(EMPLOYEE, "Account Executive", null, NO_IMAGE)
                ),
                createOrgNode(SUPERVISOR, "Account Supervisor", null, NO_IMAGE)
            ),
            createOrgNode(VICE_PRESIDENT, "Vice President", "Creative Services", "icon/icon3.svg",
            ...
    }

    public OrgNode<OrgNodeData> getOrgChartRoot() { return orgChartRoot; }

    public static OrgNode<OrgNodeData> createOrgNode(
            OrgNodeData.Type type, String title, String name, String icon, 
            OrgNode<OrgNodeData>... children) { ... }
}

The helper function createOrgNode(...) simplifies composing in the whole hierarchy in a single statement. No one is stopping you from building the hierarchy in a different way. All we then need is to expose the root node via getOrgChartRoot() and let UI templates consume the hierarchy recursively.

orgchart-template.zul

<x:ul class="orgchart" xmlns:x="xhtml">
  <apply template="node" node="${root}" root="${true}"/>
  <template name="node">
    <x:li class="${root ? ' root' : ''}${node.leaf ? ' leaf' : ''}${node.vertical ? ' vertical' : ''}">
      <x:div class="nodecontent">
        <if test="${collapsible}">
          <apply template="collapsible" node="${node}"/>
        </if>
        <apply template="nodecontent" data="${node.data}"/>
      </x:div>
      <if test="${not empty node.children}">
        <x:ul>
          <forEach items="${node.children}">
            <apply template="node" node="${each}" root="${false}"/>
          </forEach>
        </x:ul>
      </if>
    </x:li>
  </template>
</x:ul>

This template is ZK specific and uses several features like shadow/xhtml elements and EL expressions to add contextual CSS classes: root, leaf, vertical (the collapsible part is explained in the next paragraph).

You’ll find all previous elements (ul/li/div.nodecontent) controlled by ZK’s shadow elements  (if/forEach/apply). The recursion happens in line 14 only if the current node has children. This “node” template only cares about laying out the hierarchy and doesn’t contain any nodecontent specific information making it reusable for user-defined nodecontent markup – also no property of the OrgNodeData object is accessed allowing a different class representing the node content.

In orgchart-model.zul, the nodecontent-template is defined and injected into <orgchart>.

<?style src="https://dzone.com/css/orgchart.css"?>
<?style src="css/orgchart-vertical.css"?>
<?style src="css/simple.css"?>
<!--<?style src="css/debug.css"?>-->
<?component name="orgchart" templateURI="template/orgchart-template.zul"?>
<zk>
  <div sclass="centered" 
       viewModel="@id('vm') @init('zk.example.template.orgchart.OrgChartViewModel')">
    <orgchart root="@init(vm.orgChartRoot)">
      <template name="nodecontent">
        <div>
          <label value="${data.title}"/>
          <if test="${!empty data.name}">
            <separator/>
            <label value="${data.name}"/>
          </if>
        </div>
      </template>
    </orgchart>
  </div>
</zk>

Line 9 calls the orgchart-template, providing the markup (to be rendered inside the  .nodecontent-div) and the root note root="@init(vm.orgChartRoot)" from the viewModel.

For different node styling, a different template can be provided as can be seen in orgchart-full.zul.

<orgchart root="@init(vm.orgChartRoot)" collapsible="true">
  <template name="nodecontent">
    <div sclass="orgnode ${data.type}">
      <if test="${!empty data.icon}">
        <div sclass="icon" style="${('background-image: url('' += data.icon += '')')}"/>
      </if>
      <label value="${data.title}"/>
      <if test="${!empty data.name}">
        <separator/>
        <label value="${data.name}"/>
      </if>
    </div>
  </template>
...
</orgchart>

Above, the node type is used as a styleclass for individual node styles, and, if present, an icon is prepended as well.

Collapsible Nodes

A simple way to collapse and expand nodes is by just hiding/showing the children using JS (finally some JavaScript).

orgchart-model-collapsible.zul

<zk xmlns:w="client">
  <script>
    function toggleNode(wgt) {
      jq(wgt).closest('li').toggleClass('collapsed');
      jq(wgt).find('i').toggleClass('z-icon-minus z-icon-plus');
    }
  </script>
  <div sclass="centered" viewModel="@id('vm') @init('zk.example.template.orgchart.OrgChartViewModel')">
    <orgchart root="@init(vm.orgChartRoot)" collapsible="true">
      <template name="nodecontent">...</template>

      <template name="collapsible">
        <if test="${not node.leaf}">
          <a sclass="collapse" iconSclass="z-icon-minus" w:onClick="toggleNode(this);"/>
        </if>
      </template>
    </orgchart>
  </div>
</zk>

Setting the flag collapsible="true" on <orgchart> (Line 9) will invoke the collapsible-template for each node. In Line 13, I specified to only render the collapse toggle for non-leaf nodes.

All it renders is an <a>-element with a client-side listener w:onClickwhich calls a trivial JS function to toggle style classes (jq is ZK’s alias for jQuery’s  $-function). The styles for the collapse icon are defined in both simple.css or full.css to allow custom styling/positioning of the toggle in case this is needed.

.nodecontent .collapse {
    position: absolute;
    bottom: -15px;
    left: calc(50% - 6px);
    z-index: 0;
    color: white;
    background-color: #CCCCCC;
    border-radius: 6px;
    height: 12px;
    width: 12px;
    font-size: 12px;
    text-align: center;
}

Also, here, the positioning is adaptive to the actual nodecontent dimensions: centered below the nodecontent.

If we wanted we could control the open/close state in the OrgNode object and bind a server-side command-handler to load additional nodes on demand (here I didn’t, but good to know we have the option).

Fully Styled Example

Sometimes you’re lucky and you get some nice design templates from a dedicated designer (unfortunately this is an often forgotten role).

By styling (see: full.css) just the inner .orgnodedivs we get the below screenshot, which makes a big difference in appearance, even though the overal implementation remains the same:

orgchart-full.zul

Image title

Our previous efforts – ensuring the connections are independent of the actual node size – support these irregular shapes. Keep in mind that the nodes are still just treated like ordinary rectangular divs (again notice the automatic line breaks fitting into the available space where possible).

Image title

Wrapping Up

I hope this article was insightful for you. For me, it was definitely an enjoyable little distraction to explore the possibilities of a (finally) widely adopted CSS feature.

Example and Source

The source code (among other templating examples) is available on github/zkoss-demo/zk-template-examples project and contains instructions on how to run the project from command line.

Once the example project is running you can access the example pages for this article below:

http://localhost:8080/zk-template-examples/orgchart/

e.g.: http://localhost:8080/zk-template-examples/orgchart/orgchart-full.zul

Browser Compatibility

Obviously, the example uses CSS flexbox so it should support all modern browsers. On IE11 I noticed that the automatic line breaks don’t happen automatically so that a horizontal scrollbar is added earlier – still, the whole chart is accessible. If anyone knows how that can be enabled for IE11 too, I’d be happy for a helpful comment.



Source link

Fading in a Page on Load with CSS & JavaScript
Strategy

Fading in a Page on Load with CSS & JavaScript


Fading in a Page on Load with CSS & JavaScript

When I visit a page, I get annoyed when I try to interact with elements while the website is still loading. Often stuff is moving around, fonts aren’t quite loaded, and it feels broken.

I know nowadays we’re obsessed in this industry with gaining every millisecond in page performance. But in a couple of projects that I recently overhauled, I added a subtle and clean loading mechanism that I think makes the experience nicer, even if it does ultimately slightly delay the time that the user is able to start interacting with my page.

The two websites I’m talking about are Web Tools Weekly and CSS Values. Visit each of those home pages and you’ll notice in each case the content fades in rather than loading up in a clunky fashion. It’s more noticeable on CSS Values because there’s a lot more content on there and some JavaScript that has to load. Also, it happens on every “page” for CSS Values, but only on the home page for Web Tools Weekly (though technically the CSS Values pages are more like “states”).

Steps I Want to Accomplish

I’m sure there are a few ways to accomplish this but I think my method is clean and easy to understand. I’m basically doing two things:

  1. As soon as the <body> element is ready, set it to opacity: 0 in CSS.
  2. When the DOM is ready and any scripts finish running, set the <body> element back to opacity: 1, animating it so it comes in gracefully.

I’m also ensuring that the page is fully visible and accessible to anyone viewing with JavaScript turned off. Those users shouldn’t notice anything wrong. This means I have to set the opacity using JavaScript.

The concept here is basically the same as if I was adding a loading spinner to the page, but because the page shouldn’t be loading for long, I don’t care about indicating what’s happening to the user. In other cases, a loading spinner might be more appropriate, but here I’m happy with a simple fade-in.

The Code

I could have used classes in my HTML that are pulled in from my stylesheet. But the stylesheet is external and I’m not inlining it. So if I’m trying to interact with the page before the stylesheet finishes loading, this might not work as well. Because of this, I opted for adding some CSS in the <head> of the document.

Here’s what my HTML looks like:

  <style>
    .hidden {
      opacity: 0;
    }

    .visible {
      opacity: 1;
      transition: opacity 1s ease-out;
    }
  </style>
</head>
<body>
  <script>
    document.body.className = 'hidden';
  </script>

Notice what’s happening above:

  • I’ve added two declaration blocks in a <style> element, just above the opening <body> tag
  • I’m using a CSS transition for the subtle animation that causes the fade-in effect
  • I’ve put a <script> element right after the opening <body> tag
  • The JavaScript adds the hidden class, which removes the body’s content

I’m doing this right at the top of the body element because I want the content to disappear as soon as possible. If I wait for this to happen in my external JavaScript, then it’s not going to happen quickly enough.

In my external JavaScript, I have something like the following:

function doStuff (callback) {
  // do all app scripts here...
  callback();
}

doStuff(function () {
  document.body.className = 'visible';
});

In this instance I’m using a simple callback structure. I suppose you could also use Promises for this, but I didn’t really look into that much since I’m not yet that familiar with Promises. I believe the concept would be the same.

I’m basically ensuring all the app or website code that initializes any functionality is completely loaded before I set the <body> element back to visible.

To enhance it a little more, I could also wrap the whole thing in a jQuery-like document.ready handler:

window.addEventListener('DOMContentLoaded', function () {
  // do everything here...
});

This means I’d be waiting for the DOM to be ready before running my scripts. Then the scripts run and after they finish I make the page visible. I can alternatively use the load event, which means I’d be waiting for the full page resources to load, rather than just the DOM to be ready.

Conclusion

If you want a stripped down demo version of this, you can check out this page. Note that it’s intentionally a very large page, so maybe avoid it on mobile. I’m also running some useless JavaScript (adding a bunch of classes to the HTML) just to pretend something’s happening to give the appearance of a pause before the fade-in happens.

As mentioned, you can also see how this works on Web Tools Weekly and CSS Values.

You could easily switch this up and put a loading spinner in place instead while the page is “getting ready”. I chose to do the fade-in because I think it looks good and I’m not concerned that either website is going to take a long time to load.

If you know of a way to improve on this, I’m glad to hear your feedback.



Source link