Exciting new features in Vue 3

As you could expect, Vue 3 brings a lot of new exciting features. Thankfully Vue team mostly introduced additions and improvements over current APIs rather than major changes so people that already know Vue 2 should quickly feel comfortable with new syntaxes.

Let’s start with the API that most of you probably heard about…

Composition API

Composition API is most commonly discussed and featured syntax of the next major version of Vue. It’s a completely new approach to logic reuse and code organization.

Currently we build components with what we call the Options API. To add logic into Vue component we fill (option) properties such as data, methods, computed etc. The biggest downside of this approach is the fact that this is not a working JavaScript code per se. You needed to know exactly which properties are accessible in the template as well as the behavior of this keyword. Under the hood, the Vue compiler needs to transform this properties to working code. Because of that we can’t benefit from things like autosuggestions or type checking.

Composition API aims to solve this issue by exposing mechanisms currently available through component properties as JavaScript functions. Vue core team describes Composition API as “a set of additive, function-based APIs that allow flexible composition of component logic“. Code written with Composition API is more readable and there is no magic behind the scenes which makes it easier to read and learn.

Let’s see a very simple example of a component that uses new Composition API to understand how it works.

<template>
  <button @click="increment">
    Count is: {{ count }}, double is {{ double }}, click to increment.
  </button>
</template>

<script>
import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)

    function increment() {
      count.value++
    }

    onMounted(() => console.log('component mounted!'))

    return {
      count,
      double,
      increment
    }
  }
}
</script>

Now let’s break this code down into pieces to understand what happened

import { ref, computed, onMounted } from 'vue'

As I mentioned before Composition API is exposing component properties as functions so the first step is to import the functions that we need. In our case we need to create reactive reference with ref, computed property with computed and access mounted lifecycle hook with onMounted.

Now you probably wonder what is this mysterious setup method?

export default {
  setup() {

In short words it’s just a function that returns properties and functions to the template. That’s it. We are declaring all reactive properties, computed properties, watchers and lifecycle hooks here and then return them so they can be used in a template.

What we don’t return from the setup function will not be available in the template.

const count = ref(0)

According to above we are declaring reactive property called count with ref function. It can wrap any primitive or object and return it’s reactive reference. The value of passed element will be kept in a value property of the created reference. For example if you want to access value of count reference you need to explicitly ask for count.value.

const double = computed(() => count.value * 2)

function increment() {
  count.value++
}

…and this is exactly what we did when declaring computed property double as well as in increment function.

onMounted(() => console.log('component mounted!'))

With onMounted hook we are logging some message when component is mounted just to show you that you can 😉

return {
  count,
  double,
  increment
}

At the end we are returning count and double properties with increment method to make them available in a template.

<template>
  <button @click="increment">
    Count is: {{ count }}, double is {{ double }}. Click to increment.
  </button>
</template>

And voila! Now we can access the properties and functions returned by setup method in the template the same way as they were declared through the old Options API.

This is a simple example, that would be easy to achieve with the Options API as well. The true benefit of the new Composition API is not just to code in a different way, the benefits reveal themselves when it comes to reusing our code/logic.

Code reuse with Composition API

There are more advantages of the new Composition API. Think about code reuse. Currently if we want to share some code across other components there are two options available - mixins and scoped slots. Both have their drawbacks.

Let’s say that we want to extract counter functionality and reuse it in other components. Below you can see how it could be used with available APIs and new Composition API:

Let’s start with mixins:

import CounterMixin from './mixins/counter'

export default {
  mixins: [CounterMixin]
}

The biggest downside of mixins is the fact that we know nothing about what it’s actually adding to our component. It makes it not only hard to reason about but can also lead to name collisions with existing properties and functions.

It’s time for scoped slots.

<template>
  <Counter v-slot="{ count, increment }">
     {{ count }}
    <button @click="increment">Increment</button> 
  </Counter> 
</template>

With scoped slots we know exactly what properties we can access through v-slot property so it’s much easier to understand the code. The downside of this approach is that we can access it only in a template and it’s available only in Counter component scope.

Now it’s time for Composition API:

function useCounter() {
  const count = ref(0)
  function increment () { count.value++ }

  return {
    count,
    incrememt
  }
}

export default {
  setup () {
    const { count, increment } = useCounter()
    return {
      count,
      increment
    }
  }
}

Much more elegant isn’t it? We are not limited by neither template nor components scope and know exactly what properties from counter we can access. In addition we can benefit from code completion available in our editor because useCounter is just a function that returns some properties. There is no magic behind the scenes so editor can help us with type checking and suggestions.

It’s a more elegant way of using third party libraries as well. For example if we want to use Vuex we can explicitly use useStore function instead of polluting Vue prototype (this.$store). This approach erases behind the scenes magic from Vue plugins as well.

const { commit, dispatch } = useStore()

If you want to learn more about Composition API and it’s use cases I highly recommend reading this document from Vue team that explains reasoning behind new API and suggests it’s best use cases. There is also great repository with examples of Composition API usage by Thorsten Lünborg from Vue core team.

Global mounting/configuration API change

We can find another major change in the way we are instantiating and configuring our application. Let’s see how it works right now:

import Vue from 'vue'
import App from './App.vue'

Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)

new Vue({
  render: h => h(App)
}).$mount('#app')

Currently we are using global Vue object to provide any configuration and create new Vue instances. Any change made to Vue object will affect every Vue instance and component.

Now let’s see how it will work in Vue 3:

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.config.ignoredElements = [/^app-/]
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)

app.mount('#app')

As you probably noticed now every configuration is scoped to a certain Vue application defined with createApp.

It could make your code easier to understand and less prone to unexpected issues caused by third party addons. Currently if some third party solution is modifying Vue object it can affect your application in unexpected ways (especially with global mixins) which would not be possible with Vue 3.

This API change is currently discussed in this RFC which means it could potentially change in the future.

Fragments

Another exciting addition that we can expect in Vue 3 are Fragments.

What are fragments you may ask? Well if you create a Vue component it can only have one root node.

It means that component like this cannot be created:

<template>
  <div>Hello</div>
  <div>World</div>
</template>

The reason of that is Vue instance that represents any Vue component needs to be binded into a single DOM element. The only way you can create a component with multiple DOM nodes is by creating a functional component that doesn’t have underlying Vue instance.

It turns out React community had the same problem. The solution they came up with was a virtual element called Fragment. It looks more or less like this;

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

Even though Fragment looks like a normal DOM element it’s virtual and won’t be rendered in a DOM tree at all. This way we can bind component functionality into a single element without creating a redundant DOM node.

Currently you can use Fragments in Vue 2 with vue-fragments library and in Vue 3 you will have it out of the box!

Suspense

Another great idea from React ecosystem that will be adopted in Vue 3 is Suspense component.

Suspense suspends your component rendering and renders a fallback component until a condition is met. During Vue London Evan You briefly touched this topic and showed the API we can more or less expect. As it turns out, Suspense will just be a component with slots:

<Suspense>
  <template >
    <Suspended-component />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>

The fallback content will be shown until Suspended-component will fully render. Suspense can either wait till component will be downloaded if that’s an async component or perform some async actions in setup function.

Multiple v-models

V-model is a directive we can use to achieve two-way binding on a given component. We can pass a reactive property and modify it from the inside of a component.

We know v-model well from the form elements:

<input v-bind="property />

But did you know you can use v-model with every component? Under the hood v-model is just a shortcut for passing value property and listening to input event. Rewriting above example to below syntax will have exactly same effect:

<input 
  v-bind:value="property"
  v-on:input="property = $event.target.value"
/>

We can even change names of default property and event with components model property:

model: {
  prop: 'checked',
  event: 'change'
}

As you can see v-model directive can be a really helpful syntactic suger if we want to have a two-way binding in our components. Unfortunately you can only have one v-model per component.

Fortunately this won’t be a problem in Vue 3! You will be able to give v-model properties names and have have as many of them as you want. Below you can find example of two v-model’s in a form component:

<InviteeForm
  v-model:name="inviteeName"
  v-model:email="inviteeEmail"
/>

This API change is currently discussed in this RFC which means it could potentially change in the future.

Portals

Portals are special components meant to render certain content outside the current component. It’s also one of the features natively implemented in React. This is what React documentation says about portals:

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component."

It’s a very nice way of dealing with modals, popups and generally components that are meant to appear on top of the page. By using portals you can be sure that none of the host component CSS rules will affect the component that you want to display and reliefs you from doing nasty hacks with z-index.

For every portal we need to specify it’s target destination where portal content will be rendered. Below you can see the implementation from portal-vue library which adds this functionality to Vue 2:

<portal to="destination">
  <p>This slot content will be rendered wherever thportal-target with name 'destination'
    is  located.</p>
</portal>

<portal-target name="destination">
  <!--
  This component can be located anywhere in your App.
  The slot content of the above portal component wilbe rendered here.
  -->
</portal-target>

Vue 3 will ship with out of the box support for portals!

New custom directives API

Custom directives API will slightly change in Vue 3 just to better align with components lifecycle. This change should make the API easier to understand and learn for newcomers as it’s now more intuitive.

This is a current custom directives API:

const MyDirective = {
  bind(el, binding, vnode, prevVnode) {},
  inserted() {},
  update() {},
  componentUpdated() {},
  unbind() {}
}

…and this is how it will look like in Vue 3.

const MyDirective = {
  beforeMount(el, binding, vnode, prevVnode) {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {}, // new
  unmounted() {}
}

Even though it’s a breaking change it should be easily covered with a Vue compatibility build.

This API change is currently discussed in this RFC which means it could potentially change in the future.

Psst! You can learn how to master custom directives in our course.

Summary

Apart from Composition API that is the biggest major new API in Vue 3 we can find a lot of smaller improvements as well. We can see that Vue is moving towards better developer experience and simpler, more intuitive APIs. It’s also great to see that Vue team decided to adopt many ideas that are currently available only through third party libraries to the core of the framework.

The list above conatins only major API changes and improvements. If you’re curious about other ones be sure to check Vue RFCs repository..

#vue-js #vue #vuejs #javascript #web-development

What is GEEK

Buddha Community

Exciting new features in Vue 3
Luna  Mosciski

Luna Mosciski

1600583123

8 Popular Websites That Use The Vue.JS Framework

In this article, we are going to list out the most popular websites using Vue JS as their frontend framework.

Vue JS is one of those elite progressive JavaScript frameworks that has huge demand in the web development industry. Many popular websites are developed using Vue in their frontend development because of its imperative features.

This framework was created by Evan You and still it is maintained by his private team members. Vue is of course an open-source framework which is based on MVVM concept (Model-view view-Model) and used extensively in building sublime user-interfaces and also considered a prime choice for developing single-page heavy applications.

Released in February 2014, Vue JS has gained 64,828 stars on Github, making it very popular in recent times.

Evan used Angular JS on many operations while working for Google and integrated many features in Vue to cover the flaws of Angular.

“I figured, what if I could just extract the part that I really liked about Angular and build something really lightweight." - Evan You

#vuejs #vue #vue-with-laravel #vue-top-story #vue-3 #build-vue-frontend #vue-in-laravel #vue.js

A Wrapper for Sembast and SQFlite to Enable Easy

FHIR_DB

This is really just a wrapper around Sembast_SQFLite - so all of the heavy lifting was done by Alex Tekartik. I highly recommend that if you have any questions about working with this package that you take a look at Sembast. He's also just a super nice guy, and even answered a question for me when I was deciding which sembast version to use. As usual, ResoCoder also has a good tutorial.

I have an interest in low-resource settings and thus a specific reason to be able to store data offline. To encourage this use, there are a number of other packages I have created based around the data format FHIR. FHIR® is the registered trademark of HL7 and is used with the permission of HL7. Use of the FHIR trademark does not constitute endorsement of this product by HL7.

Using the Db

So, while not absolutely necessary, I highly recommend that you use some sort of interface class. This adds the benefit of more easily handling errors, plus if you change to a different database in the future, you don't have to change the rest of your app, just the interface.

I've used something like this in my projects:

class IFhirDb {
  IFhirDb();
  final ResourceDao resourceDao = ResourceDao();

  Future<Either<DbFailure, Resource>> save(Resource resource) async {
    Resource resultResource;
    try {
      resultResource = await resourceDao.save(resource);
    } catch (error) {
      return left(DbFailure.unableToSave(error: error.toString()));
    }
    return right(resultResource);
  }

  Future<Either<DbFailure, List<Resource>>> returnListOfSingleResourceType(
      String resourceType) async {
    List<Resource> resultList;
    try {
      resultList =
          await resourceDao.getAllSortedById(resourceType: resourceType);
    } catch (error) {
      return left(DbFailure.unableToObtainList(error: error.toString()));
    }
    return right(resultList);
  }

  Future<Either<DbFailure, List<Resource>>> searchFunction(
      String resourceType, String searchString, String reference) async {
    List<Resource> resultList;
    try {
      resultList =
          await resourceDao.searchFor(resourceType, searchString, reference);
    } catch (error) {
      return left(DbFailure.unableToObtainList(error: error.toString()));
    }
    return right(resultList);
  }
}

I like this because in case there's an i/o error or something, it won't crash your app. Then, you can call this interface in your app like the following:

final patient = Patient(
    resourceType: 'Patient',
    name: [HumanName(text: 'New Patient Name')],
    birthDate: Date(DateTime.now()),
);

final saveResult = await IFhirDb().save(patient);

This will save your newly created patient to the locally embedded database.

IMPORTANT: this database will expect that all previously created resources have an id. When you save a resource, it will check to see if that resource type has already been stored. (Each resource type is saved in it's own store in the database). It will then check if there is an ID. If there's no ID, it will create a new one for that resource (along with metadata on version number and creation time). It will save it, and return the resource. If it already has an ID, it will copy the the old version of the resource into a _history store. It will then update the metadata of the new resource and save that version into the appropriate store for that resource. If, for instance, we have a previously created patient:

{
    "resourceType": "Patient",
    "id": "fhirfli-294057507-6811107",
    "meta": {
        "versionId": "1",
        "lastUpdated": "2020-10-16T19:41:28.054369Z"
    },
    "name": [
        {
            "given": ["New"],
            "family": "Patient"
        }
    ],
    "birthDate": "2020-10-16"
}

And we update the last name to 'Provider'. The above version of the patient will be kept in _history, while in the 'Patient' store in the db, we will have the updated version:

{
    "resourceType": "Patient",
    "id": "fhirfli-294057507-6811107",
    "meta": {
        "versionId": "2",
        "lastUpdated": "2020-10-16T19:45:07.316698Z"
    },
    "name": [
        {
            "given": ["New"],
            "family": "Provider"
        }
    ],
    "birthDate": "2020-10-16"
}

This way we can keep track of all previous version of all resources (which is obviously important in medicine).

For most of the interactions (saving, deleting, etc), they work the way you'd expect. The only difference is search. Because Sembast is NoSQL, we can search on any of the fields in a resource. If in our interface class, we have the following function:

  Future<Either<DbFailure, List<Resource>>> searchFunction(
      String resourceType, String searchString, String reference) async {
    List<Resource> resultList;
    try {
      resultList =
          await resourceDao.searchFor(resourceType, searchString, reference);
    } catch (error) {
      return left(DbFailure.unableToObtainList(error: error.toString()));
    }
    return right(resultList);
  }

You can search for all immunizations of a certain patient:

searchFunction(
        'Immunization', 'patient.reference', 'Patient/$patientId');

This function will search through all entries in the 'Immunization' store. It will look at all 'patient.reference' fields, and return any that match 'Patient/$patientId'.

The last thing I'll mention is that this is a password protected db, using AES-256 encryption (although it can also use Salsa20). Anytime you use the db, you have the option of using a password for encryption/decryption. Remember, if you setup the database using encryption, you will only be able to access it using that same password. When you're ready to change the password, you will need to call the update password function. If we again assume we created a change password method in our interface, it might look something like this:

class IFhirDb {
  IFhirDb();
  final ResourceDao resourceDao = ResourceDao();
  ...
    Future<Either<DbFailure, Unit>> updatePassword(String oldPassword, String newPassword) async {
    try {
      await resourceDao.updatePw(oldPassword, newPassword);
    } catch (error) {
      return left(DbFailure.unableToUpdatePassword(error: error.toString()));
    }
    return right(Unit);
  }

You don't have to use a password, and in that case, it will save the db file as plain text. If you want to add a password later, it will encrypt it at that time.

General Store

After using this for a while in an app, I've realized that it needs to be able to store data apart from just FHIR resources, at least on occasion. For this, I've added a second class for all versions of the database called GeneralDao. This is similar to the ResourceDao, but fewer options. So, in order to save something, it would look like this:

await GeneralDao().save('password', {'new':'map'});
await GeneralDao().save('password', {'new':'map'}, 'key');

The difference between these two options is that the first one will generate a key for the map being stored, while the second will store the map using the key provided. Both will return the key after successfully storing the map.

Other functions available include:

// deletes everything in the general store
await GeneralDao().deleteAllGeneral('password'); 

// delete specific entry
await GeneralDao().delete('password','key'); 

// returns map with that key
await GeneralDao().find('password', 'key'); 

FHIR® is a registered trademark of Health Level Seven International (HL7) and its use does not constitute an endorsement of products by HL7®

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add fhir_db

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  fhir_db: ^0.4.3

Alternatively, your editor might support or flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:fhir_db/dstu2.dart';
import 'package:fhir_db/dstu2/fhir_db.dart';
import 'package:fhir_db/dstu2/general_dao.dart';
import 'package:fhir_db/dstu2/resource_dao.dart';
import 'package:fhir_db/encrypt/aes.dart';
import 'package:fhir_db/encrypt/salsa.dart';
import 'package:fhir_db/r4.dart';
import 'package:fhir_db/r4/fhir_db.dart';
import 'package:fhir_db/r4/general_dao.dart';
import 'package:fhir_db/r4/resource_dao.dart';
import 'package:fhir_db/r5.dart';
import 'package:fhir_db/r5/fhir_db.dart';
import 'package:fhir_db/r5/general_dao.dart';
import 'package:fhir_db/r5/resource_dao.dart';
import 'package:fhir_db/stu3.dart';
import 'package:fhir_db/stu3/fhir_db.dart';
import 'package:fhir_db/stu3/general_dao.dart';
import 'package:fhir_db/stu3/resource_dao.dart'; 

example/lib/main.dart

import 'package:fhir/r4.dart';
import 'package:fhir_db/r4.dart';
import 'package:flutter/material.dart';
import 'package:test/test.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final resourceDao = ResourceDao();

  // await resourceDao.updatePw('newPw', null);
  await resourceDao.deleteAllResources(null);

  group('Playing with passwords', () {
    test('Playing with Passwords', () async {
      final patient = Patient(id: Id('1'));

      final saved = await resourceDao.save(null, patient);

      await resourceDao.updatePw(null, 'newPw');
      final search1 = await resourceDao.find('newPw',
          resourceType: R4ResourceType.Patient, id: Id('1'));
      expect(saved, search1[0]);

      await resourceDao.updatePw('newPw', 'newerPw');
      final search2 = await resourceDao.find('newerPw',
          resourceType: R4ResourceType.Patient, id: Id('1'));
      expect(saved, search2[0]);

      await resourceDao.updatePw('newerPw', null);
      final search3 = await resourceDao.find(null,
          resourceType: R4ResourceType.Patient, id: Id('1'));
      expect(saved, search3[0]);

      await resourceDao.deleteAllResources(null);
    });
  });

  final id = Id('12345');
  group('Saving Things:', () {
    test('Save Patient', () async {
      final humanName = HumanName(family: 'Atreides', given: ['Duke']);
      final patient = Patient(id: id, name: [humanName]);
      final saved = await resourceDao.save(null, patient);

      expect(saved.id, id);

      expect((saved as Patient).name?[0], humanName);
    });

    test('Save Organization', () async {
      final organization = Organization(id: id, name: 'FhirFli');
      final saved = await resourceDao.save(null, organization);

      expect(saved.id, id);

      expect((saved as Organization).name, 'FhirFli');
    });

    test('Save Observation1', () async {
      final observation1 = Observation(
        id: Id('obs1'),
        code: CodeableConcept(text: 'Observation #1'),
        effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
      );
      final saved = await resourceDao.save(null, observation1);

      expect(saved.id, Id('obs1'));

      expect((saved as Observation).code.text, 'Observation #1');
    });

    test('Save Observation1 Again', () async {
      final observation1 = Observation(
          id: Id('obs1'),
          code: CodeableConcept(text: 'Observation #1 - Updated'));
      final saved = await resourceDao.save(null, observation1);

      expect(saved.id, Id('obs1'));

      expect((saved as Observation).code.text, 'Observation #1 - Updated');

      expect(saved.meta?.versionId, Id('2'));
    });

    test('Save Observation2', () async {
      final observation2 = Observation(
        id: Id('obs2'),
        code: CodeableConcept(text: 'Observation #2'),
        effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
      );
      final saved = await resourceDao.save(null, observation2);

      expect(saved.id, Id('obs2'));

      expect((saved as Observation).code.text, 'Observation #2');
    });

    test('Save Observation3', () async {
      final observation3 = Observation(
        id: Id('obs3'),
        code: CodeableConcept(text: 'Observation #3'),
        effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
      );
      final saved = await resourceDao.save(null, observation3);

      expect(saved.id, Id('obs3'));

      expect((saved as Observation).code.text, 'Observation #3');
    });
  });

  group('Finding Things:', () {
    test('Find 1st Patient', () async {
      final search = await resourceDao.find(null,
          resourceType: R4ResourceType.Patient, id: id);
      final humanName = HumanName(family: 'Atreides', given: ['Duke']);

      expect(search.length, 1);

      expect((search[0] as Patient).name?[0], humanName);
    });

    test('Find 3rd Observation', () async {
      final search = await resourceDao.find(null,
          resourceType: R4ResourceType.Observation, id: Id('obs3'));

      expect(search.length, 1);

      expect(search[0].id, Id('obs3'));

      expect((search[0] as Observation).code.text, 'Observation #3');
    });

    test('Find All Observations', () async {
      final search = await resourceDao.getResourceType(
        null,
        resourceTypes: [R4ResourceType.Observation],
      );

      expect(search.length, 3);

      final idList = [];
      for (final obs in search) {
        idList.add(obs.id.toString());
      }

      expect(idList.contains('obs1'), true);

      expect(idList.contains('obs2'), true);

      expect(idList.contains('obs3'), true);
    });

    test('Find All (non-historical) Resources', () async {
      final search = await resourceDao.getAll(null);

      expect(search.length, 5);
      final patList = search.toList();
      final orgList = search.toList();
      final obsList = search.toList();
      patList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Patient);
      orgList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Organization);
      obsList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Observation);

      expect(patList.length, 1);

      expect(orgList.length, 1);

      expect(obsList.length, 3);
    });
  });

  group('Deleting Things:', () {
    test('Delete 2nd Observation', () async {
      await resourceDao.delete(
          null, null, R4ResourceType.Observation, Id('obs2'), null, null);

      final search = await resourceDao.getResourceType(
        null,
        resourceTypes: [R4ResourceType.Observation],
      );

      expect(search.length, 2);

      final idList = [];
      for (final obs in search) {
        idList.add(obs.id.toString());
      }

      expect(idList.contains('obs1'), true);

      expect(idList.contains('obs2'), false);

      expect(idList.contains('obs3'), true);
    });

    test('Delete All Observations', () async {
      await resourceDao.deleteSingleType(null,
          resourceType: R4ResourceType.Observation);

      final search = await resourceDao.getAll(null);

      expect(search.length, 2);

      final patList = search.toList();
      final orgList = search.toList();
      patList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Patient);
      orgList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Organization);

      expect(patList.length, 1);

      expect(patList.length, 1);
    });

    test('Delete All Resources', () async {
      await resourceDao.deleteAllResources(null);

      final search = await resourceDao.getAll(null);

      expect(search.length, 0);
    });
  });

  group('Password - Saving Things:', () {
    test('Save Patient', () async {
      await resourceDao.updatePw(null, 'newPw');
      final humanName = HumanName(family: 'Atreides', given: ['Duke']);
      final patient = Patient(id: id, name: [humanName]);
      final saved = await resourceDao.save('newPw', patient);

      expect(saved.id, id);

      expect((saved as Patient).name?[0], humanName);
    });

    test('Save Organization', () async {
      final organization = Organization(id: id, name: 'FhirFli');
      final saved = await resourceDao.save('newPw', organization);

      expect(saved.id, id);

      expect((saved as Organization).name, 'FhirFli');
    });

    test('Save Observation1', () async {
      final observation1 = Observation(
        id: Id('obs1'),
        code: CodeableConcept(text: 'Observation #1'),
        effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
      );
      final saved = await resourceDao.save('newPw', observation1);

      expect(saved.id, Id('obs1'));

      expect((saved as Observation).code.text, 'Observation #1');
    });

    test('Save Observation1 Again', () async {
      final observation1 = Observation(
          id: Id('obs1'),
          code: CodeableConcept(text: 'Observation #1 - Updated'));
      final saved = await resourceDao.save('newPw', observation1);

      expect(saved.id, Id('obs1'));

      expect((saved as Observation).code.text, 'Observation #1 - Updated');

      expect(saved.meta?.versionId, Id('2'));
    });

    test('Save Observation2', () async {
      final observation2 = Observation(
        id: Id('obs2'),
        code: CodeableConcept(text: 'Observation #2'),
        effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
      );
      final saved = await resourceDao.save('newPw', observation2);

      expect(saved.id, Id('obs2'));

      expect((saved as Observation).code.text, 'Observation #2');
    });

    test('Save Observation3', () async {
      final observation3 = Observation(
        id: Id('obs3'),
        code: CodeableConcept(text: 'Observation #3'),
        effectiveDateTime: FhirDateTime(DateTime(1981, 09, 18)),
      );
      final saved = await resourceDao.save('newPw', observation3);

      expect(saved.id, Id('obs3'));

      expect((saved as Observation).code.text, 'Observation #3');
    });
  });

  group('Password - Finding Things:', () {
    test('Find 1st Patient', () async {
      final search = await resourceDao.find('newPw',
          resourceType: R4ResourceType.Patient, id: id);
      final humanName = HumanName(family: 'Atreides', given: ['Duke']);

      expect(search.length, 1);

      expect((search[0] as Patient).name?[0], humanName);
    });

    test('Find 3rd Observation', () async {
      final search = await resourceDao.find('newPw',
          resourceType: R4ResourceType.Observation, id: Id('obs3'));

      expect(search.length, 1);

      expect(search[0].id, Id('obs3'));

      expect((search[0] as Observation).code.text, 'Observation #3');
    });

    test('Find All Observations', () async {
      final search = await resourceDao.getResourceType(
        'newPw',
        resourceTypes: [R4ResourceType.Observation],
      );

      expect(search.length, 3);

      final idList = [];
      for (final obs in search) {
        idList.add(obs.id.toString());
      }

      expect(idList.contains('obs1'), true);

      expect(idList.contains('obs2'), true);

      expect(idList.contains('obs3'), true);
    });

    test('Find All (non-historical) Resources', () async {
      final search = await resourceDao.getAll('newPw');

      expect(search.length, 5);
      final patList = search.toList();
      final orgList = search.toList();
      final obsList = search.toList();
      patList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Patient);
      orgList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Organization);
      obsList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Observation);

      expect(patList.length, 1);

      expect(orgList.length, 1);

      expect(obsList.length, 3);
    });
  });

  group('Password - Deleting Things:', () {
    test('Delete 2nd Observation', () async {
      await resourceDao.delete(
          'newPw', null, R4ResourceType.Observation, Id('obs2'), null, null);

      final search = await resourceDao.getResourceType(
        'newPw',
        resourceTypes: [R4ResourceType.Observation],
      );

      expect(search.length, 2);

      final idList = [];
      for (final obs in search) {
        idList.add(obs.id.toString());
      }

      expect(idList.contains('obs1'), true);

      expect(idList.contains('obs2'), false);

      expect(idList.contains('obs3'), true);
    });

    test('Delete All Observations', () async {
      await resourceDao.deleteSingleType('newPw',
          resourceType: R4ResourceType.Observation);

      final search = await resourceDao.getAll('newPw');

      expect(search.length, 2);

      final patList = search.toList();
      final orgList = search.toList();
      patList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Patient);
      orgList.retainWhere(
          (resource) => resource.resourceType == R4ResourceType.Organization);

      expect(patList.length, 1);

      expect(patList.length, 1);
    });

    test('Delete All Resources', () async {
      await resourceDao.deleteAllResources('newPw');

      final search = await resourceDao.getAll('newPw');

      expect(search.length, 0);

      await resourceDao.updatePw('newPw', null);
    });
  });
} 

Download Details:

Author: MayJuun

Source Code: https://github.com/MayJuun/fhir/tree/main/fhir_db

#sqflite  #dart  #flutter 

Oleta  Orn

Oleta Orn

1589943900

Portal - a new feature in Vue 3 - Vue.js Tutorials

In today’s article, I want to talk a little bit more about one of the most useful ones - Portal
This article is based on vue-next repository content. There is no guarantee that the features mentioned in this article will land in Vue 3 exactly in the described form (but most likely they will).

#tutorials #vue #vue 3 #vuejs

Oleta  Orn

Oleta Orn

1591110059

Vue 3 Beta - Installation and New Features

As one of four official Vue.js partners, the Modus team was excited to dive into the newly released Vue 3.0.0-beta.1 and explore the new features that the upgrade brings to the table. Vue 3 not only brings new features, but a smaller framework footprint. This video shows how to install Vue 3 beta, upgrade an application from Vue 2 to Vue 3, boot strap an application in only a few lines of code, and breaks down more of the exciting new changes. This video also show off the Fragments API and explain how to implement Fragments into your Vue 3 application - removing the requirement for a single root node. Use the approach in this video to upgrade your Vue 2 application to Vue 3.

#vue #vuejs #vue 3

Oleta  Orn

Oleta Orn

1597416091

Getting Started with the Vue Router in Vue 3

Vue Router helps developers easily build single page applications with Vue.js. This video uses a simple e-Commerce application as an example to show how routing works in Vue 3 with vue-router. This video also shows how to initialize vue-router, dynamically load routes, breaks down the benefits and differences between useRoute and useRouter, and more.

Vue Router is a powerful tool, enabling nested route/view mapping, modular component-based router configuration, router params, query, and wildcards, view transition effects, fine-grained navigation control, integration with automatic active CSS classes, HTML5 history mode or hash mode with auto-fallback in Internet Explorer 9, and customizable scroll behavior. Learn more about vue-router and take a look at the source code on Github: https://github.com/vuejs/vue-router

#vue #vue 3 #vue router #programming