Author Archives: admin

Angular Observables

[fuente: https://angular.io/guide/observables]

Observables

Observables provide support for passing messages between publishers and subscribers in your application. Observables offer significant benefits over other techniques for event handling, asynchronous programming, and handling multiple values.

Observables are declarative—that is, you define a function for publishing values, but it is not executed until a consumer subscribes to it. The subscribed consumer then receives notifications until the function completes, or until they unsubscribe.

An observable can deliver multiple values of any type—literals, messages, or events, depending on the context. The API for receiving values is the same whether the values are delivered synchronously or asynchronously. Because setup and teardown logic are both handled by the observable, your application code only needs to worry about subscribing to consume values, and when done, unsubscribing. Whether the stream was keystrokes, an HTTP response, or an interval timer, the interface for listening to values and stopping listening is the same.

Because of these advantages, observables are used extensively within Angular, and are recommended for app development as well.

Basic usage and terms

As a publisher, you create an Observable instance that defines a subscriber function. This is the function that is executed when a consumer calls the subscribe() method. The subscriber function defines how to obtain or generate values or messages to be published.

To execute the observable you have created and begin receiving notifications, you call its subscribe() method, passing an observer. This is a JavaScript object that defines the handlers for the notifications you receive. The subscribe() call returns a Subscription object that has an unsubscribe() method, which you call to stop receiving notifications.

Here’s an example that demonstrates the basic usage model by showing how an observable could be used to provide geolocation updates.

// Create an Observable that will start listening to geolocation updates
// when a consumer subscribes.
const locations = new Observable((observer) => {
  // Get the next and error callbacks. These will be passed in when
  // the consumer subscribes.
  const {next, error} = observer;
  let watchId;

  // Simple geolocation API check provides values to publish
  if ('geolocation' in navigator) {
    watchId = navigator.geolocation.watchPosition(next, error);
  } else {
    error('Geolocation not available');
  }

  // When the consumer unsubscribes, clean up data ready for next subscription.
  return {unsubscribe() { navigator.geolocation.clearWatch(watchId); }};
});

// Call subscribe() to start listening for updates.
const locationsSubscription = locations.subscribe({
  next(position) { console.log('Current Position: ', position); },
  error(msg) { console.log('Error Getting Location: ', msg); }
});

// Stop listening for location after 10 seconds
setTimeout(() => { locationsSubscription.unsubscribe(); }, 10000);

Defining observers

A handler for receiving observable notifications implements the Observer interface. It is an object that defines callback methods to handle the three types of notifications that an observable can send:

NOTIFICATION TYPE DESCRIPTION
next Required. A handler for each delivered value. Called zero or more times after execution starts.
error Optional. A handler for an error notification. An error halts execution of the observable instance.
complete Optional. A handler for the execution-complete notification. Delayed values can continue to be delivered to the next handler after execution is complete.

An observer object can define any combination of these handlers. If you don’t supply a handler for a notification type, the observer ignores notifications of that type.

Subscribing

An Observable instance begins publishing values only when someone subscribes to it. You subscribe by calling the subscribe() method of the instance, passing an observer object to receive the notifications.

In order to show how subscribing works, we need to create a new observable. There is a constructor that you use to create new instances, but for illustration, we can use some methods from the RxJS library that create simple observables of frequently used types:

  • of(...items)Returns an Observable instance that synchronously delivers the values provided as arguments.
  • from(iterable)Converts its argument to an Observable instance. This method is commonly used to convert an array to an observable.

Here’s an example of creating and subscribing to a simple observable, with an observer that logs the received message to the console:

// Create simple observable that emits three values
const myObservable = of(1, 2, 3);

// Create observer object
const myObserver = {
  next: x => console.log('Observer got a next value: ' + x),
  error: err => console.error('Observer got an error: ' + err),
  complete: () => console.log('Observer got a complete notification'),
};

// Execute with the observer object
myObservable.subscribe(myObserver);
// Logs:
// Observer got a next value: 1
// Observer got a next value: 2
// Observer got a next value: 3
// Observer got a complete notification

Alternatively, the subscribe() method can accept callback function definitions in line, for nexterror, and completehandlers. For example, the following subscribe() call is the same as the one that specifies the predefined observer:

myObservable.subscribe(
  x => console.log('Observer got a next value: ' + x),
  err => console.error('Observer got an error: ' + err),
  () => console.log('Observer got a complete notification')
);

In either case, a next handler is required. The error and complete handlers are optional.

Note that a next() function could receive, for instance, message strings, or event objects, numeric values, or structures, depending on context. As a general term, we refer to data published by an observable as a stream. Any type of value can be represented with an observable, and the values are published as a stream.

more in the fuente!!

 

Parent and children communicate via a service

A parent component and its children share a service whose interface enables bi-directional communication within the family.

The scope of the service instance is the parent component and its children. Components outside this component subtree have no access to the service or their communications.

This MissionService connects the MissionControlComponent to multiple AstronautComponent children.

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs';

@Injectable()
export class MissionService {

  // Observable string sources
  private missionAnnouncedSource = new Subject<string>();
  private missionConfirmedSource = new Subject<string>();

  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();

  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }

  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }
}

The MissionControlComponent both provides the instance of the service that it shares with its children (through the providers metadata array) and injects that instance into itself through its constructor:

import { Component }          from '@angular/core';

import { MissionService }     from './mission.service';

@Component({
  selector: 'app-mission-control',
  template: `
    <h2>Mission Control</h2>
    <button (click)="announce()">Announce mission</button>
    <app-astronaut *ngFor="let astronaut of astronauts"
      [astronaut]="astronaut">
    </app-astronaut>
    <h3>History</h3>
    <ul>
      <li *ngFor="let event of history">{{event}}</li>
    </ul>
  `,
  providers: [MissionService]
})
export class MissionControlComponent {
  astronauts = ['Lovell', 'Swigert', 'Haise'];
  history: string[] = [];
  missions = ['Fly to the moon!',
              'Fly to mars!',
              'Fly to Vegas!'];
  nextMission = 0;

  constructor(private missionService: MissionService) {
    missionService.missionConfirmed$.subscribe(
      astronaut => {
        this.history.push(`${astronaut} confirmed the mission`);
      });
  }

  announce() {
    let mission = this.missions[this.nextMission++];
    this.missionService.announceMission(mission);
    this.history.push(`Mission "${mission}" announced`);
    if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
  }
}

The AstronautComponent also injects the service in its constructor. Each AstronautComponent is a child of the MissionControlComponent and therefore receives its parent’s service instance:

import { Component, Input, OnDestroy } from '@angular/core';

import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs';

@Component({
  selector: 'app-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()"
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})
export class AstronautComponent implements OnDestroy {
  @Input() astronaut: string;
  mission = '<no mission announced>';
  confirmed = false;
  announced = false;
  subscription: Subscription;

  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
  }

  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }

  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }
}

Notice that this example captures the subscription and unsubscribe() when the AstronautComponent is destroyed. This is a memory-leak guard step. There is no actual risk in this app because the lifetime of a AstronautComponent is the same as the lifetime of the app itself. That would not always be true in a more complex application.

You don’t add this guard to the MissionControlComponent because, as the parent, it controls the lifetime of the MissionService.

The History log demonstrates that messages travel in both directions between the parent MissionControlComponent and the AstronautComponent children, facilitated by the service:

bidirectional-service

Test it

Tests click buttons of both the parent MissionControlComponent and the AstronautComponent children and verify that the history meets expectations:

// ...
it('should announce a mission', function () {
  let missionControl = element(by.tagName('app-mission-control'));
  let announceButton = missionControl.all(by.tagName('button')).get(0);
  announceButton.click().then(function () {
    let history = missionControl.all(by.tagName('li'));
    expect(history.count()).toBe(1);
    expect(history.get(0).getText()).toMatch(/Mission.* announced/);
  });
});

it('should confirm the mission by Lovell', function () {
  testConfirmMission(1, 2, 'Lovell');
});

it('should confirm the mission by Haise', function () {
  testConfirmMission(3, 3, 'Haise');
});

it('should confirm the mission by Swigert', function () {
  testConfirmMission(2, 4, 'Swigert');
});

function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
  let _confirmedLog = ' confirmed the mission';
  let missionControl = element(by.tagName('app-mission-control'));
  let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
  confirmButton.click().then(function () {
    let history = missionControl.all(by.tagName('li'));
    expect(history.count()).toBe(expectedLogCount);
    expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog);
  });
}
// ...

 

Create a responsive Angular D3 charts

[fuente: https://school.geekwall.in/p/SJ52XGRWX/create-a-responsive-angular-d3-charts]

While the landscape of frameworks available for structuring and building web applications is changing by the minute, D3 is still the recognized way to create visualizations using Javascript. In this tutorial, we will add a D3 chart to an Angular application and make the size of the graph dynamic.

Note: You can find the finished source code here.

Creating the Angular project

The first step is to create a new Angular project using the CLI, and to add the d3 library to it:

ng new angular-d3
npm install d3 --save
npm install @types/d3 --save-dev

Next, we will create the component that we will work with:

ng generate component bar-chart

Finally, we will replace the content of ‘src/app/app.component.html’ with the following:

<h1>Bar Chart</h1>
<app-bar-chart></app-bar-chart>

Loading and passing data

In this tutorial, we will use this bar chart from Mike Bostock as the D3 visualization. You can find the data in JSON format here, and we will put it in a new asset file at ‘src/assets/data.json’.

We can also create an interface for the data points, in a new file ‘src/app/data/data.model.ts’:

export interface DataModel {
  letter: string;
  frequency: number;
}

To load this data, we can modify the ‘src/app/app.component.ts’ file like this:
angular-d3-app.component.ts

import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { DataModel } from 'src/data/data.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  data: Observable<DataModel>;

  constructor(private http: HttpClient) {
    this.data = this.http.get<DataModel>('data/data.json');
  }
}

For HttpClient to work, we need to add HttpClientModule to our App NgModule imports in ‘src/app/app.module.ts’.

Finally, we can pass the data to our chart component by modifying ‘src/app/app.component.html’:

<h1>Bar Chart</h1>
<app-bar-chart [data]=”data | async”></app-bar-chart>

Angular 7 Lazy loading

[Fuente: https://angular.io/guide/lazy-loading-ngmodules]

Lazy Loading Feature Modules

——– PREVIOUS start——-

Feature Modules

Feature modules are NgModules for the purpose of organizing code.

As your app grows, you can organize code relevant for a specific feature. This helps apply clear boundaries for features. With feature modules, you can keep code related to a specific functionality or feature separate from other code. Delineating areas of your app helps with collaboration between developers and teams, separating directives, and managing the size of the root module.

Feature modules vs. root modules

A feature module is an organizational best practice, as opposed to a concept of the core Angular API. A feature module delivers a cohesive set of functionality focused on a specific application need such as a user workflow, routing, or forms. While you can do everything within the root module, feature modules help you partition the app into focused areas. A feature module collaborates with the root module and with other modules through the services it provides and the components, directives, and pipes that it shares.

Types of Feature Modules

There are five general categories of feature modules which tend to fall into the following groups:

  • Domain feature modules.
  • Routed feature modules.
  • Routing modules.
  • Service feature modules.
  • Widget feature modules.

While the following guidelines describe the use of each type and their typical characteristics, in real world apps, you may see hybrids.

Domain feature modules

  • Domain feature modules deliver a user experience dedicated to a particular application domain like editing a customer or placing an order.
  • They typically have a top component that acts as the feature root and private, supporting sub-components descend from it.
  • Domain feature modules consist mostly of declarations. Only the top component is exported.
  • Domain feature modules rarely have providers. When they do, the lifetime of the provided services should be the same as the lifetime of the module.
  • Domain feature modules are typically imported exactly once by a larger feature module.
  • They might be imported by the root AppModule of a small application that lacks routing.

Routed feature modules

  • Routed feature modules are domain feature modules whose top components are the targets of router navigation routes.
  • All lazy-loaded modules are routed feature modules by definition.
  • Routed feature modules don’t export anything because their components never appear in the template of an external component.
  • A lazy-loaded routed feature module should not be imported by any module. Doing so would trigger an eager load, defeating the purpose of lazy loading.That means you won’t see them mentioned among the AppModule imports. An eager loaded routed feature module must be imported by another module so that the compiler learns about its components.
  • Routed feature modules rarely have providers for reasons explained in Lazy Loading Feature Modules. When they do, the lifetime of the provided services should be the same as the lifetime of the module. Don’t provide application-wide singleton services in a routed feature module or in a module that the routed module imports.

Routing modules

A routing module provides routing configuration for another module and separates routing concerns from its companion module.

A routing module typically does the following:

  • Defines routes.
  • Adds router configuration to the module’s imports.
  • Adds guard and resolver service providers to the module’s providers.
  • The name of the routing module should parallel the name of its companion module, using the suffix “Routing”. For example, FooModule in foo.module.ts has a routing module named FooRoutingModule in foo-routing.module.ts. If the companion module is the root AppModule, the AppRoutingModule adds router configuration to its imports with RouterModule.forRoot(routes). All other routing modules are children that import RouterModule.forChild(routes).
  • A routing module re-exports the RouterModule as a convenience so that components of the companion module have access to router directives such as RouterLink and RouterOutlet.
  • A routing module does not have its own declarations. Components, directives, and pipes are the responsibility of the feature module, not the routing module.

A routing module should only be imported by its companion module.

Service feature modules

Service modules provide utility services such as data access and messaging. Ideally, they consist entirely of providers and have no declarations. Angular’s HttpClientModule is a good example of a service module.

The root AppModule is the only module that should import service modules.

Widget feature modules

  • A widget module makes components, directives, and pipes available to external modules. Many third-party UI component libraries are widget modules.
  • A widget module should consist entirely of declarations, most of them exported.
  • A widget module should rarely have providers.
  • Import widget modules in any module whose component templates need the widgets.

——– PREVIOUS end——-

Prerequisites

A basic understanding of the following:

For the final sample app with two lazy loaded modules that this page describes, see the live example / download example.


High level view

There are three main steps to setting up a lazy loaded feature module:

  1. Create the feature module.
  2. Create the feature module’s routing module.
  3. Configure the routes.

Set up an app

If you don’t already have an app, you can follow the steps below to create one with the CLI. If you do already have an app, skip to Configure the routes. Enter the following command where customer-app is the name of your app:

ng new customer-app --routing

This creates an app called customer-app and the --routing flag generates a file called app-routing.module.ts, which is one of the files you need for setting up lazy loading for your feature module. Navigate into the project by issuing the command cd customer-app.

Create a feature module with routing

Next, you’ll need a feature module to route to. To make one, enter the following command at the terminal window prompt where customers is the name of the module:

ng generate module customers --routing

This creates a customers folder with two files inside; CustomersModule and CustomersRoutingModuleCustomersModulewill act as the gatekeeper for anything that concerns customers. CustomersRoutingModule will handle any customer-related routing. This keeps the app’s structure organized as the app grows and allows you to reuse this module while easily keeping its routing intact.

The CLI imports the CustomersRoutingModule into the CustomersModule by adding a JavaScript import statement at the top of the file and adding CustomersRoutingModule to the @NgModule imports array.

Add a component to the feature module

In order to see the module being lazy loaded in the browser, create a component to render some HTML when the app loads CustomersModule. At the command line, enter the following:

ng generate component customers/customer-list

This creates a folder inside of customers called customer-list with the four files that make up the component.

Just like with the routing module, the CLI imports the CustomerListComponent into the CustomersModule.

Add another feature module

For another place to route to, create a second feature module with routing:

ng generate module orders --routing

This makes a new folder called orders containing an OrdersModule and an OrdersRoutingModule.

Now, just like with the CustomersModule, give it some content:

ng generate component orders/order-list

Set up the UI

Though you can type the URL into the address bar, a nav is easier for the user and more common. Replace the default placeholder markup in app.component.html with a custom nav so you can easily navigate to your modules in the browser:

<h1>
  {{title}}
</h1>

<button routerLink="/customers">Customers</button>
<button routerLink="/orders">Orders</button>
<button routerLink="">Home</button>

<router-outlet></router-outlet>

To make the buttons work, you need to configure the routing modules.

Configure the routes

The two feature modules, OrdersModule and CustomersModule, have to be wired up to the AppRoutingModule so the router knows about them. The structure is as follows:

lazy loaded modules diagram

Each feature module acts as a doorway via the router. In the AppRoutingModule, you configure the routes to the feature modules, in this case OrdersModule and CustomersModule. This way, the router knows to go to the feature module. The feature module then connects the AppRoutingModule to the CustomersRoutingModule or the OrdersRoutingModule. Those routing modules tell the router where to go to load relevant components.

Routes at the app level

In AppRoutingModule, update the routes array with the following:

const routes: Routes = [
  {
    path: 'customers',
    loadChildren: './customers/customers.module#CustomersModule'
  },
  {
    path: 'orders',
    loadChildren: './orders/orders.module#OrdersModule'
  },
  {
    path: '',
    redirectTo: '',
    pathMatch: 'full'
  }
];

The import statements stay the same. The first two paths are the routes to the CustomersModule and the OrdersModulerespectively. Notice that the lazy loading syntax uses loadChildren followed by a string that is the relative path to the module, a hash mark or #, and the module’s class name.

Inside the feature module

Next, take a look at customers.module.ts. If you’re using the CLI and following the steps outlined in this page, you don’t have to do anything here. The feature module is like a connector between the AppRoutingModule and the feature routing module. The AppRoutingModule imports the feature module, CustomersModule, and CustomersModule in turn imports the CustomersRoutingModule.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomersRoutingModule } from './customers-routing.module';
import { CustomerListComponent } from './customer-list/customer-list.component';

@NgModule({
  imports: [
    CommonModule,
    CustomersRoutingModule
  ],
  declarations: [CustomerListComponent]
})
export class CustomersModule { }

The customers.module.ts file imports the CustomersRoutingModule and CustomerListComponent so the CustomersModule class can have access to them. CustomersRoutingModule is then listed in the @NgModule imports array giving CustomersModule access to its own routing module, and CustomerListComponent is in the declarations array, which means CustomerListComponent belongs to the CustomersModule.

Configure the feature module’s routes

The next step is in customers-routing.module.ts. First, import the component at the top of the file with the other JavaScript import statements. Then, add the route to CustomerListComponent.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { CustomerListComponent } from './customer-list/customer-list.component';


const routes: Routes = [
  {
    path: '',
    component: CustomerListComponent
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class CustomersRoutingModule { }

Notice that the path is set to an empty string. This is because the path in AppRoutingModule is already set to customers, so this route in the CustomersRoutingModule, is already within the customers context. Every route in this routing module is a child route.

Repeat this last step of importing the OrdersListComponent and configuring the Routes array for the orders-routing.module.ts:

import { OrderListComponent } from './order-list/order-list.component';

const routes: Routes = [
  {
    path: '',
    component: OrderListComponent
  }
];

Now, if you view the app in the browser, the three buttons take you to each module.

Confirm it’s working

You can check to see that a module is indeed being lazy loaded with the Chrome developer tools. In Chrome, open the dev tools by pressing Cmd+Option+i on a Mac or Ctrl+Alt+i on a PC and go to the Network Tab.

lazy loaded modules diagram

Click on the Orders or Customers button. If you see a chunk appear, you’ve wired everything up properly and the feature module is being lazy loaded. A chunk should appear for Orders and for Customers but will only appear once for each.

lazy loaded modules diagram

forRoot() and forChild()

You might have noticed that the CLI adds RouterModule.forRoot(routes) to the app-routing.module.ts imports array. This lets Angular know that this module, AppRoutingModule, is a routing module and forRoot() specifies that this is the root routing module. It configures all the routes you pass to it, gives you access to the router directives, and registers the RouterService. Use forRoot() in the AppRoutingModule—that is, one time in the app at the root level.

The CLI also adds RouterModule.forChild(routes) to feature routing modules. This way, Angular knows that the route list is only responsible for providing additional routes and is intended for feature modules. You can use forChild() in multiple modules.

forRoot() contains injector configuration which is global; such as configuring the Router. forChild() has no injector configuration, only directives such as RouterOutlet and RouterLink.

Angular 7 Internationalization (i18n)

[Fuente: https://angular.io/guide/i18n]

Application internationalization is a many-faceted area of development, focused on making applications available and user-friendly to a worldwide audience. This page describes Angular’s internationalization (i18n) tools, which can help you make your app available in multiple languages.

See the i18n Example for a simple example of an AOT-compiled app, translated into French.

Angular and i18n

Internationalization is the process of designing and preparing your app to be usable in different languages.

Localization is the process of translating your internationalized app into specific languages for particular locales.

Angular simplifies the following aspects of internationalization:

  • Displaying dates, number, percentages, and currencies in a local format.
  • Preparing text in component templates for translation.
  • Handling plural forms of words.
  • Handling alternative text.

For localization, you can use the Angular CLI to generate most of the boilerplate necessary to create files for translators, and to publish your app in multiple languages. After you have set up your app to use i18n, the CLI can help you with the following steps:

  • Extracting localizable text into a file that you can send out to be translated.
  • Building and serving the app for a given locale, using the translated text.
  • Creating multiple language versions of your app.

Setting up the locale of your app

A locale is an identifier (id) that refers to a set of user preferences that tend to be shared within a region of the world, such as country. This document refers to a locale identifier as a “locale” or “locale id”.

A Unicode locale identifier is composed of a Unicode language identifier and (optionally) the character - followed by a locale extension. (For historical reasons the character _ is supported as an alternative to -.) For example, in the locale id fr-CA the fr refers to the French language identifier, and the CA refers to the locale extension Canada.

Angular follows the Unicode LDML convention that uses stable identifiers (Unicode locale identifiers) based on the norm BCP47. It is very important that you follow this convention when you define your locale, because the Angular i18n tools use this locale id to find the correct corresponding locale data.

By default, Angular uses the locale en-US, which is English as spoken in the United States of America.

To set your app’s locale to another value, use the CLI parameter --configuration with the value of the locale id that you want to use:

ng serve --configuration=fr

If you use JIT, you also need to define the LOCALE_ID provider in your main module:

src/app/app.module.ts
import { LOCALE_ID, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from '../src/app/app.component';

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ AppComponent ],
  providers: [ { provide: LOCALE_ID, useValue: 'fr' } ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

For more information about Unicode locale identifiers, see the CLDR core spec.

For a complete list of locales supported by Angular, see the Angular repository.

The locale identifiers used by CLDR and Angular are based on BCP47. These specifications change over time; the following table maps previous identifiers to current ones at time of writing: -REMOVED-

 

i18n pipes

Angular pipes can help you with internationalization: the DatePipeCurrencyPipeDecimalPipe and PercentPipe use locale data to format data based on the LOCALE_ID.

By default, Angular only contains locale data for en-US. If you set the value of LOCALE_ID to another locale, you must import locale data for that new locale. The CLI imports the locale data for you when you use the parameter --configuration with ng serve and ng build.

If you want to import locale data for other languages, you can do it manually:

import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';

// the second parameter 'fr' is optional
registerLocaleData(localeFr, 'fr');

The first parameter is an object containing the locale data imported from @angular/common/locales. By default, the imported locale data is registered with the locale id that is defined in the Angular locale data itself. If you want to register the imported locale data with another locale id, use the second parameter to specify a custom locale id. For example, Angular’s locale data defines the locale id for French as “fr”. You can use the second parameter to associate the imported French locale data with the custom locale id “fr-FR” instead of “fr”.

The files in @angular/common/locales contain most of the locale data that you need, but some advanced formatting options might only be available in the extra dataset that you can import from @angular/common/locales/extra. An error message informs you when this is the case.

import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import localeFrExtra from '@angular/common/locales/extra/fr';

registerLocaleData(localeFr, 'fr-FR', localeFrExtra);
All locale data used by Angular are extracted from the Unicode Consortium's Common Locale Data Repository (CLDR).

NOTA: Lo de arriba es solo relativo al multidioma en los pipes de fechas, números, etc.

Template translations

This document refers to a unit of translatable text as “text,” a “message”, or a “text message.”

The i18n template translation process has four phases:

  1. Mark static text messages in your component templates for translation.
  2. Create a translation file: Use the Angular CLI xi18n command to extract the marked text into an industry-standard translation source file.
  3. Edit the generated translation file: Translate the extracted text into the target language.
  4. Merge the completed translation file into the app. To do this, use the Angular CLI build command to compile the app, choosing a locale-specific configuration, or specifying the following command options.
    • --i18nFile=path to the translation file
    • --i18nFormat=format of the translation file
    • --i18nLocalelocale id

The command replaces the original messages with translated text, and generates a new version of the app in the target language.

You need to build and deploy a separate version of the app for each supported language.

Mark text with the i18n attribute

The Angular i18n attribute marks translatable content. Place it on every element tag whose fixed text is to be translated.

In the example below, an <h1> tag displays a simple English language greeting, “Hello i18n!”

<h1>Hello i18n!</h1>

To mark the greeting for translation, add the i18n attribute to the <h1> tag.

<h1 i18n>Hello i18n!</h1>

i18n is a custom attribute, recognized by Angular tools and compilers. After translation, the compiler removes it. It is not an Angular directive.

Help the translator with a description and meaning

To translate a text message accurately, the translator may need additional information or context.

You can add a description of the text message as the value of the i18n attribute, as shown in the example below:

<h1 i18n="An introduction header for this sample">Hello i18n!</h1>

The translator may also need to know the meaning or intent of the text message within this particular app context.

You add context by beginning the i18n attribute value with the meaning and separating it from the description with the |character: <meaning>|<description>

<h1 i18n="site header|An introduction header for this sample">Hello i18n!</h1>

All occurrences of a text message that have the same meaning will have the same translation.

A text message that is associated with different meanings can have different translations.

The Angular extraction tool preserves both the meaning and the description in the translation source file to facilitate contextually-specific translations, but only the combination of meaning and text message are used to generate the specific id of a translation.

If you have two similar text messages with different meanings, they are extracted separately.

If you have two similar text messages with different descriptions (not different meanings), then they are extracted only once.

Set a custom id for persistence and maintenance

The angular i18n extractor tool generates a file with a translation unit entry for each i18n attribute in a template. By default, it assigns each translation unit a unique id such as this one:

<trans-unit id="ba0cc104d3d69bf669f97b8d96a4c5d8d9559aa3" datatype="html">

When you change the translatable text, the extractor tool generates a new id for that translation unit. You must then update the translation file with the new id.

Alternatively, you can specify a custom id in the i18n attribute by using the prefix @@. The example below defines the custom id introductionHeader:

<h1 i18n="@@introductionHeader">Hello i18n!</h1>

When you specify a custom id, the extractor tool and compiler generate a translation unit with that custom id.

<trans-unit id="introductionHeader" datatype="html">

The custom id is persistent. The extractor tool does not change it when the translatable text changes. Therefore, you do not need to update the translation. This approach makes maintenance easier.

Use a custom id with a description

–removed–

Define unique custom ids

Be sure to define custom ids that are unique. If you use the same id for two different text messages, only the first one is extracted, and its translation is used in place of both original text messages.

In the example below the custom id myId is used for two different messages:

-removed-

Translate text without creating an element

If there is a section of text that you would like to translate, you can wrap it in a <span> tag. However, if you don’t want to create a new DOM element merely to facilitate translation, you can wrap the text in an <ng-container> element. The <ng-container> is transformed into an html comment:

<ng-container i18n>I don't output any element</ng-container>

Translate attributes

Displayed text is sometimes supplied as the value of an attribute, rather than the content of tag. For example, if your template has an image with a title attribute, the text value of the title attribute needs to be translated.

<img [src]="logo" title="Angular logo">

To mark an attribute for translation, add an attribute in the form of i18n-x, where x is the name of the attribute to translate. The following example shows how to mark the title attribute for translation by adding the i18n-title attribute on the imgtag:

<img [src]="logo" i18n-title title="Angular logo" />

This technique works for any attribute of any element.

You also can assign a meaning, description, and id with the i18n-x="<meaning>|<description>@@<id>" syntax.

Regular expressions for plurals and selections

Different languages have different pluralization rules and grammatical constructions that add complexity to the translation task. You can use regular expressions with the plural and select clauses to provide patterns that aid translation in these cases.

Pluralization

Suppose that you want to say that something was “updated x minutes ago”. In English, depending upon the number of minutes, you could display “just now”, “one minute ago”, or “x minutes ago” (with x being the actual number). Other languages might express the cardinality differently.

The example below shows how to use a plural ICU expression to display one of those three options based on when the update occurred:

<span i18n>Updated {minutes, plural, =0 {just now} =1 {one minute ago} other {{{minutes}} minutes ago}}</span>
  • The first parameter is the key. It is bound to the component property (minutes), which determines the number of minutes.
  • The second parameter identifies this as a plural translation type.
  • The third parameter defines a pluralization pattern consisting of pluralization categories and their matching values.

This syntax conforms to the ICU Message Format as specified in the CLDR pluralization rules.

Pluralization categories include (depending on the language):

  • =0 (or any other number)
  • zero
  • one
  • two
  • few
  • many
  • other

After the pluralization category, put the default English text in braces ({}).

In the example above, the three options are specified according to that pluralization pattern. For talking about zero minutes, you use =0 {just now}. For one minute, you use =1 {one minute}. Any unmatched cardinality uses other {{{minutes}} minutes ago}. You could choose to add patterns for two, three, or any other number if the pluralization rules were different. For the example of “minute”, only these three patterns are necessary in English.

You can use interpolations and html markup inside of your translations.

Select among alternative text messages

If your template needs to display different text messages depending on the value of a variable, you need to translate all of those alternative text messages.

You can handle this with a select ICU expression. It is similar to the plural expressions except that you choose among alternative translations based on a string value instead of a number, and you define those string values.

The following format message in the component template binds to the component’s gender property, which outputs one of the following string values: “m”, “f” or “o”. The message maps those values to the appropriate translations:

<span i18n>The author is {gender, select, male {male} female {female} other {other}}</span>

Nesting plural and select ICU expressions

You can also nest different ICU expressions together, as shown in this example:

<span i18n>Updated: {minutes, plural,
  =0 {just now}
  =1 {one minute ago}
  other {{{minutes}} minutes ago by {gender, select, male {male} female {female} other {other}}}}
</span>

Create a translation source file

When your app is ready, you can use the Angular CLI to extract the text messages marked with i18n and attributes marked with i18n-x into a translation source file. Open a terminal window at the root of the app project and run the CLI command xi18n.

ng xi18n

By default, the command creates a file named messages.xlf in your src/ folder.

If you don’t use the CLI, you have two options:

  • You can use the ng-xi18n tool directly from the @angular/compiler-cli package. For more information, see the ng xi18n command documentation.
  • You can use the CLI Webpack plugin AngularCompilerPlugin from the @ngtools/webpack package. Set the parameters i18nOutFile and i18nOutFormat to trigger the extraction. For more information, see the Angular Ahead-of-Time Webpack Plugin documentation.

Output options

You can supply command options to change the format, the name, the location, and the source locale of the extracted file. For example, to create a file in the src/locale folder, specify the output path:

ng xi18n --output-path src/locale

By default, the xi18n command generates a translation file named messages.xlf in the XML Localization Interchange File Format (XLIFF, version 1.2).

The command can read and write files in three translation formats:

You can specify the translation format explicitly with the --i18nFormat command option, as illustrated in these example commands:

ng xi18n  --i18n-format=xlf
ng xi18n  --i18n-format=xlf2
ng xi18n  --i18n-format=xmb

The sample in this guide uses the default XLIFF 1.2 format.

XLIFF files have the extension .xlf. The XMB format generates .xmb source files but uses .xtb (XML Translation Bundle: XTB) translation files.

You can change the name of the translation source file that is generated by the extraction tool with the --outFile command option:

ng xi18n --out-file source.xlf

You can specify the base locale of your app with the--i18n-locale command option:

ng xi18n --i18n-locale fr

The extraction tool uses the locale to add the app locale information into your translation source file. This information is not used by Angular, but external translation tools may need it.

Translate the source text

The ng xi18n command generates a translation source file named messages.xlf in the project src folder. The next step is to translate the display strings in this source file into language-specific translation files. The example in this guide creates a French translation file.

Create a localization folder

Most apps are translated into more than one other language. For this reason, it is standard practice for the project structure to reflect the entire internationalization effort.

One approach is to dedicate a folder to localization and store related assets, such as internationalization files, there.

Localization and internationalization are different but closely related terms.

This guide follows that approach. It has a locale folder under src/. Assets within that folder have a filename extension that matches their associated locale.

Create the translation files

For each translation source file, there must be at least one language translation file for the resulting translation.

For this example:

  1. Make a copy of the messages.xlf file.
  2. Put the copy in the locale folder.
  3. Rename the copy to messages.fr.xlf for the French language translation.

If you were translating to other languages, you would repeat these steps for each target language.

Translate text nodes

In a large translation project, you would send the messages.fr.xlf file to a French translator who would enter the translations using an XLIFF file editor.

This sample file is easy to translate without a special editor or knowledge of French.

  1. Open messages.fr.xlf and find the first <trans-unit> section:
<trans-unit id="introductionHeader" datatype="html">
  <source>Hello i18n!</source>
  <note priority="1" from="description">An introduction header for this sample</note>
  <note priority="1" from="meaning">User welcome</note>
</trans-unit>

This XML element represents the translation of the <h1> greeting tag that you marked with the i18n attribute earlier in this guide.

Note that the translation unit id=introductionHeader is derived from the custom id that you set earlier, but without the @@ prefix required in the source HTML.

  1. Duplicate the <source/> tag, rename it target, and then replace its content with the French greeting. If you were working with a more complex translation, you could use the information and context provided by the source, description, and meaning elements to guide your selection of the appropriate French translation.
<trans-unit id="introductionHeader" datatype="html">
  <source>Hello i18n!</source>
  <target>Bonjour i18n !</target>
  <note priority="1" from="description">An introduction header for this sample</note>
  <note priority="1" from="meaning">User welcome</note>
</trans-unit>
  1. Translate the other text nodes the same way:
<trans-unit id="ba0cc104d3d69bf669f97b8d96a4c5d8d9559aa3" datatype="html">
  <source>I don&apos;t output any element</source>
  <target>Je n'affiche aucun élément</target>
</trans-unit>
<trans-unit id="701174153757adf13e7c24a248c8a873ac9f5193" datatype="html">
  <source>Angular logo</source>
  <target>Logo d'Angular</target>
</trans-unit>

The Angular i18n tools generated the ids for these translation units. Don’t change them. Each id depends upon the content of the template text and its assigned meaning. If you change either the text or the meaning, then the idchanges. For more information, see the translation file maintenance discussion.

Translating plural and select expressions

The plural and select ICU expressions are extracted separately, so they require special attention when preparing for translation.

Look for these expressions in relation to other translation units that you recognize from elsewhere in the source template. In this example, you know the translation unit for the select must be just below the translation unit for the logo.

Translate plural

To translate a plural, translate its ICU format match values:

<trans-unit id="5a134dee893586d02bffc9611056b9cadf9abfad" datatype="html">
  <source>{VAR_PLURAL, plural, =0 {just now} =1 {one minute ago} other {<x id="INTERPOLATION" equiv-text="{{minutes}}"/> minutes ago} }</source>
  <target>{VAR_PLURAL, plural, =0 {à l'instant} =1 {il y a une minute} other {il y a <x id="INTERPOLATION" equiv-text="{{minutes}}"/> minutes} }</target>
</trans-unit>

You can add or remove plural cases, with each language having its own cardinality. (See CLDR plural rules.)

Translate select

Below is the content of our example select ICU expression in the component template:

<span i18n>The author is {gender, select, male {male} female {female} other {other}}</span>

The extraction tool broke that into two translation units because ICU expressions are extracted separately.

The first unit contains the text that was outside of the select. In place of the select is a placeholder, <x id="ICU">, that represents the select message. Translate the text and move around the placeholder if necessary, but don’t remove it. If you remove the placeholder, the ICU expression will not be present in your translated app.

<trans-unit id="f99f34ac9bd4606345071bd813858dec29f3b7d1" datatype="html">
  <source>The author is <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></source>
  <target>L'auteur est <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></target>
</trans-unit>

The second translation unit, immediately below the first one, contains the select message. Translate that as well.

<trans-unit id="eff74b75ab7364b6fa888f1cbfae901aaaf02295" datatype="html">
  <source>{VAR_SELECT, select, male {male} female {female} other {other} }</source>
  <target>{VAR_SELECT, select, male {un homme} female {une femme} other {autre} }</target>
</trans-unit>

Here they are together, after translation:

</trans-unit>
<trans-unit id="f99f34ac9bd4606345071bd813858dec29f3b7d1" datatype="html">
  <source>The author is <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></source>
  <target>L'auteur est <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></target>
</trans-unit>
<trans-unit id="eff74b75ab7364b6fa888f1cbfae901aaaf02295" datatype="html">
  <source>{VAR_SELECT, select, male {male} female {female} other {other} }</source>
  <target>{VAR_SELECT, select, male {un homme} female {une femme} other {autre} }</target>
</trans-unit>

Translate a nested expression

A nested expression is similar to the previous examples. As in the previous example, there are two translation units. The first one contains the text outside of the nested expression:

<trans-unit id="972cb0cf3e442f7b1c00d7dab168ac08d6bdf20c" datatype="html">
  <source>Updated: <x id="ICU" equiv-text="{minutes, plural, =0 {...} =1 {...} other {...}}"/></source>
  <target>Mis à jour: <x id="ICU" equiv-text="{minutes, plural, =0 {...} =1 {...} other {...}}"/></target>
</trans-unit>

The second unit contains the complete nested expression:

<trans-unit id="7151c2e67748b726f0864fc443861d45df21d706" datatype="html">
  <source>{VAR_PLURAL, plural, =0 {just now} =1 {one minute ago} other {<x id="INTERPOLATION" equiv-text="{{minutes}}"/> minutes ago by {VAR_SELECT, select, male {male} female {female} other {other} }} }</source>
  <target>{VAR_PLURAL, plural, =0 {à l'instant} =1 {il y a une minute} other {il y a <x id="INTERPOLATION" equiv-text="{{minutes}}"/> minutes par {VAR_SELECT, select, male {un homme} female {une femme} other {autre} }} }</target>
</trans-unit>

And both together:

<trans-unit id="972cb0cf3e442f7b1c00d7dab168ac08d6bdf20c" datatype="html">
  <source>Updated: <x id="ICU" equiv-text="{minutes, plural, =0 {...} =1 {...} other {...}}"/></source>
  <target>Mis à jour: <x id="ICU" equiv-text="{minutes, plural, =0 {...} =1 {...} other {...}}"/></target>
</trans-unit>
<trans-unit id="7151c2e67748b726f0864fc443861d45df21d706" datatype="html">
  <source>{VAR_PLURAL, plural, =0 {just now} =1 {one minute ago} other {<x id="INTERPOLATION" equiv-text="{{minutes}}"/> minutes ago by {VAR_SELECT, select, male {male} female {female} other {other} }} }</source>
  <target>{VAR_PLURAL, plural, =0 {à l'instant} =1 {il y a une minute} other {il y a <x id="INTERPOLATION" equiv-text="{{minutes}}"/> minutes par {VAR_SELECT, select, male {un homme} female {une femme} other {autre} }} }</target>
</trans-unit>

The entire template translation is complete. The next section describes how to load that translation into the app.

The app and its translation file

The sample app and its translation file are now as follows: –removed–

Merge the completed translation file into the app

To merge the translated text into component templates, compile the app with the completed translation file.

Provide the Angular compiler with three translation-specific pieces of information:

  • The translation file.
  • The translation file format.
  • The locale (fr or en-US for instance).

The compilation process is the same whether the translation file is in .xlf format or in another format that Angular understands, such as .xtb.

How you provide this information depends upon whether you compile with the JIT compiler or the AOT compiler.

  • With AOT, you pass the information as configuration settings.
  • With JIT, you provide the information at bootstrap time.

Merge with the AOT compiler

The AOT compiler is part of a build process that produces a small, fast, ready-to-run application package, typically for production.

When you internationalize with the AOT compiler, you must pre-build a separate application package for each language and serve the appropriate package based on either server-side language detection or url parameters.

To instruct the AOT compiler to use your translation configuration, set the three “i18n” build configuration options in your angular.json file.

  • i18nFile: the path to the translation file.
  • i18nFormat: the format of the translation file.
  • i18nLocale: the locale id.

You should also direct the output to a locale-specific folder to keep it separate from other locale versions of your app, by setting the outputPath configuration option.

"build": {
  ...
  "configurations": {
    ...
    "fr": {
      "aot": true,
      "outputPath": "dist/my-project-fr/",
      "i18nFile": "src/locale/messages.fr.xlf",
      "i18nFormat": "xlf",
      "i18nLocale": "fr",
      ...
    }
  }
},
"serve": {
  ...
  "configurations": {
    ...
    "fr": {
      "browserTarget": "*project-name*:build:fr"
    }
  }
}

You can then pass this configuration to the ng serve or ng build commands. The example below shows how to serve the French language file created in previous sections of this guide:

ng serve --configuration=fr

For production builds, you define a separate production-fr build configuration in the CLI configuration file, angular.json.

...
"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": { ... },
    "configurations": {
      "fr": {
        "aot": true,
        "outputPath": "dist/my-project-fr/",
        "i18nFile": "src/locale/messages.fr.xlf",
        "i18nFormat": "xlf",
        "i18nLocale": "fr",
        "i18nMissingTranslation": "error",
      }
// ...
"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "browserTarget": "my-project:build"
  },
  "configurations": {
    "production": {
      "browserTarget": "my-project:build:production"
    },
    "fr": {
      "browserTarget": "my-project:build:fr"
    }
  }
},

The same configuration options can also be provided through the CLI with your existing production configuration.

ng build --prod --i18n-file src/locale/messages.fr.xlf --i18n-format xlf --i18n-locale fr

Report missing translations

By default, when a translation is missing, the build succeeds but generates a warning such as Missing translation formessage "foo". You can configure the level of warning that is generated by the Angular compiler:

  • Error: throw an error. If you are using AOT compilation, the build will fail. If you are using JIT compilation, the app will fail to load.
  • Warning (default): show a ‘Missing translation’ warning in the console or shell.
  • Ignore: do nothing.

You specify the warning level in the configurations section your Angular CLI build configuration. The example below shows how to set the warning level to error:

"configurations": {
  ...
  "fr": {
    ...
    "i18nMissingTranslation": "error"
  }
}

Build for multiple locales

When you use the CLI build or serve command to build your application for different locales, change the output path using the --outputPath command option (along with the i18n-specific command options), so that the translation files are saved to different locations. When you are serving a locale-specific version from a subdirectory, you can also change the base URL used by your app by specifying the --baseHref option.

For example, if the French version of your application is served from https://myapp.com/fr/, configure the build for the French version as follows.

"configurations": {
  "fr": {
    "aot": true,
    "outputPath": "dist/my-project-fr/",
    "baseHref": "/fr/",
    "i18nFile": "src/locale/messages.fr.xlf",
    "i18nFormat": "xlf",
    "i18nLocale": "fr",
    "i18nMissingTranslation": "error",
  }

For more details about how to create scripts to generate an app in multiple languages and how to set up Apache 2 to serve them from different subdirectories, read this tutorial by Philippe Martin.

ewe
w
ew
ew
ew
ewe
w

Ventajas de desarrolla web apps modernas con ReactJS

[Fuente: https://medium.com/@hamzamahmood/advantages-of-developing-modern-web-apps-with-react-js-8504c571db71]

Cómo los negocios online pueden sacar el máximo partido de utilizar React

If we talk about the 2010’s, we have observed a massive growth in modern web and mobile applications, powered by lightweight and sophisticated JavaScript libraries. Better user experience is now derived from the speed of the application and as more customers start using a particular technology, companies have to strive towards scalability. Considering there are a plethora of frameworks to choose from, software teams fall victim to the bothersome “paradox of choice”; that is, the dilemma to decide which one is better than the other before pitching it to upper management. Choosing a framework is a challenging task. It could be viewed as more of a business decision than a technological one if one thinks about it.

React.js is one such Javascript library that has achieved massive popularity within the domain of online, web-based businesses and that too for a perfectly good reason. The one thing that it does exceptionally well — carve out great looking user interfaces (UI). With the principle that HTML and JavaScript are bound to collaborate side-by-side, React was created with a business-forward mindset by leveraging faster web-page load speed, SEO friendliness and code reusability through combining the two technologies.

A Very Concise Introduction

React was developed by Facebook in 2011 and given open-source status in 2013 under the controversial BSD3 license. Since its first release, React’s Github repository has generated 96,000 stars from developers and has amassed a community of almost 1200 active contributors, regularly pushing updates to enrich the library. With over 7M downloads in the month of May alone, this library has definitely proven its preference within technology companies.

Which Companies Use React

Businesses were quick to grasp this technology because of the simplicity it offered to developers; that is, the ability to learn React in bare minimum time. Code reusability with hassle-free addition/modification of functionalities in the existing system meant allocating significantly less time and budget on development and building larger teams, respectively.

Top companies are truly enamored by React’s capabilities on a business stand point. The list of companies utilizing React in production is there to prove it, just to name a few: Facebook, Instagram, Netflix, Whatsapp, Salesforce, Uber, The New York Times, CNN, Dropbox, DailyMotion, IMDB, Venmo, and Reddit are the major league ones among the 100+ other medium to large scale companies.

To elaborate on why businesses are converging towards this technology, we need to explore React’s primary features that facilitate the creation of high performance, agile applications, then proceed on to the pros and cons associated with using React in online businesses before finally finishing it off with a conclusion.

1.0 ReactJS has Power-Packed Features

To give a high-level overview of what React is capable of, we need to understand the core concepts and features associated with the library. There are 3 primary ones, which are summarized as follows:

1.1 Component Creation

React enables the creation of module-like pieces of code called “Components”. These code snippets reflect a particular part of the user-interface, which can be repeated across several web pages. This is what we meant by reusability, which is a great way to save valuable time on development. The declarative nature of React also makes designing UI seamless and takes a major load off from product developers so they could focus on more important functions and business logic.

1.2 Virtual DOM

Considered the next biggest leap in web development since AJAX, the virtual DOM (short for Document Object Model) is the core reason why React enables the creation of fast, scalable web apps. Through React’s memory reconciliation algorithm, the library constructs a representation of the page in a virtual memory, where it performs the necessary updates before rendering the final web-page into the browser.

1.3 Easy to learn

We need to clarify that React is NOT a framework; unlike Angular or Vue.js, but a library that is consistently used in association with other Javascript libraries. Hence, there is a shorter learning curve involved in understanding React compared to other comprehensive libraries. Businesses; in turn, are able to streamline development without spending much capital on the existing system.

2.0 Analyzing the Pros and Cons from a business standpoint

React is a miniature library but serves multiple possible advantages to boost traction of online services and apps. The illustration below provides a glimpse of what React does well and where it falls short. We shall elaborate further on each particular element through the perspective of a business.

The advantages easily outweigh the disadvantages. Source: Cleveroad

2.1) Make Your Business “React”-ive

Notable Boost In Performance

The Virtual DOM mentioned earlier emphatically increases speed of modern web applications because it eliminates the usage of code heavy frameworks such as Jquery and other bootstrapping libraries. React itself is sufficient in creating awesome looking front-end designs and combined with its super fast rendering capabilities is a natural fit for companies to utilize it in their services.

Seamless SEO Integration

For any online business, content is king. Search Engine Optimization is the gateway to boost user traffic onto their platform. React significantly reduces page load time through faster rendering speed, adapts it performance in real-time based on current user traffic, features that are otherwise not perfectly handled by most frameworks.

This particular aspect is essential for the success of businesses because faster speed is directly proportional to more users which converts to main goal of companies: Revenue. This is also emphasized by Moz in their article that effective use of SEO will improve the app’s ranking on Google search and reaching the number one spot is the holy grail of every web-based platform.

Ease of Migration

When engineers and managers collaboratively decide upon migrating from an older technological infrastructure to a new one, certain questions arise on the level of effort and time required in performing the task. Google’s Angular framework; for instance, faced a very alarming compatibility issue between its first generation Angular 1.x and the futuristic Angular 2. Companies intent on shifting to the newer version, had to invest time to train developers in the latest technology.

React; on the other hand, is lightweight and practically wrapped around the same JavaScript standards that made developers and manager alike, to fall in love with this language in the first place. React code can be added anywhere onto existing infrastructure without worry of shutting down the system for maintenance and also less dependency to reinvent the wheel.

2.2 Upgrade Your Technological Assets

Fuse technologies for bigger Impact

React makes use of the ever popular HTML and Javascript frameworks by integrating them together. Extend the two technologies onto CSS facilitates the designing of advanced looking web-interfaces. React is a very API friendly library and is extensible across a multitude of frameworks. It works seamlessly with Google’s iconic Material Design and SemanticUI frameworks to leverage user interface development.

Quickly Debug Faults

Facebook has created a dedicated debugging mechanism to isolate UI errors and bugs to the exact component causing it. The browser itself spits out relevant data about the erroneous line of code, web page and/or section to make the required correction, making lives of developers a whole lot easier.

Improve Code Stability With Tests

Businesses looking to create fault tolerant user interface will find ReactJS as the go-to choice. The component creation aspect of this library allows developers to efficiently perform unit testing, making sure no system crashes occur. Code reuse enables for curtailing time performing redundant tests. Adding such tests improves standards in code quality; hence, platform stability.

2.3 Some Drawbacks

Counterproductive Documentation

The roadblock that developers and product managers are known to face is the limitation of documentation within React. When trying to ‘hook’ React with other libraries, two scenarios occur: Either the information provided on their forum is 1) inadequate or, 2) is not useful. Angular being a complete solution, and considering it has been around for a longer period of time, has better maintained resources and tutorials. Vue.js also, despite being new, is reliable with regard to documentation and code readability.

Very UI-centric

Some experts have reservations about React’s over-dependence on external libraries to add functionality to its base. Despite being lightweight, React quintessentially is a UI library. There is only so much it can do to enhance performance and robustness of a web app through its core functionalities. Angular and Vue would be considered more extensive with regard to creating full-scale projects unlike React.

GitLab cogently claims that Vue “…does not make large assumptions about much of anything either. It really only assumes that your data will change.” This is a subtle, yet critical remark on React’s superfluousness of depending on multiple libraries that may complicate matters for businesses.

3.0 Conclusion

If we look at the whole discussion holistically, React’s advantages far exceed its disadvantages making it a robust and adaptable programming library. Companies realize React’s ability to remain relevant in the market and that is why they are investing wholeheartedly in this technology.

The point; nevertheless, was to showcase the usefulness of the React library in creating modern, scalable applications. With users more quick to choose alternatives in the case of poor application performance, product developers have to make an important judgment call when deciding the ‘right’ framework for creating the best looking and performing application to drive user leads and then monetary sales, eventually. React is that library assisting companies achieve to achieve their goals, fortifying its relevance in the market for a longer time to come.



Spread and share knowledge. If this article piqued your interest and if you are kind fellow, give a few claps to this article. Follow my profile for more tech related articles. — Hamza

React native — OTA updates

[Fuente: https://docs.expo.io/versions/v31.0.0/guides/configuring-ota-updates]

Configuring OTA Updates

Expo provides various settings to configure how your app receives over-the-air (OTA) JavaScript updates. OTA updates allow you to publish a new version of your app JavaScript and assets without building a new version of your standalone app and re-submitting to app stores (read more about the limitations).

[ —- Limitations ——

If you make any of the following changes in app.json, you will need to re-build the binaries for your app for the change to take effect:

  • Increment the Expo SDK Version

  • Change anything under the ios or android keys

  • Change your app splash

  • Change your app icon

  • Change your app name

  • Change your app scheme

  • Change your facebookScheme

  • Change your bundled assets under assetBundlePatterns

—–]

To perform an over-the-air update of your app, you simply run expo publish. If you’re using release channels, specify one with --release-channel <channel-name> option. Please note that if you wish to update the SDK version which your app is using, you need to rebuild your app with expo build:* command and upload the binary file to the appropriate app store (see the docs here).

OTA updates are controlled by the updates settings in app.json, which handle the initial app load, and the Updates SDK module, which allows you to fetch updates asynchronously from your JS.

By default, Expo will check for updates automatically when your app is launched and will try to fetch the latest published version. If a new bundle is available, Expo will attempt to download it before launching the experience. If there is no network connection available, or it has not finished downloading in 30 seconds, Expo will fall back to loading a cached version of your app, and continue trying to fetch the update in the background (at which point it will be saved into the cache for the next app load).

With this automatic configuration, calling Expo.Updates.reload() will also result in Expo attempting to fetch the most up-to-date version of your app, so there is no need to use any of the other methods in the Updates module.

The timeout length is configurable by setting updates.fallbackToCacheTimeout (ms) in app.json. For example, a common pattern is to set updates.fallbackToCacheTimeout to 0. This will allow your app to start immediately with a cached bundle while downloading a newer one in the background for future use. Expo.Updates.addListener provides a hook to let you respond when the new bundle is finished downloading.

In standalone apps, it is also possible to turn off automatic updates, and to instead control updates entirely within your JS code. This is desirable if you want some custom logic around fetching updates (e.g. only over Wi-Fi).

Setting updates.checkAutomatically to "ON_ERROR_RECOVERY" in app.json will prevent Expo from automatically fetching the latest update every time your app is launched. Only the most recent cached version of your bundle will be loaded. It will only automatically fetch an update if the last run of the cached bundle produced a fatal JS error.

You can then use the Expo.Updates module to download new updates and, if appropriate, notify the user and reload the experience.

try {
  const update = await Expo.Updates.checkForUpdateAsync();
  if (update.isAvailable) {
    await Expo.Updates.fetchUpdateAsync();
    // ... notify user of update ...
    Expo.Updates.reloadFromCache();
  }
} catch (e) {
  // handle or log error
}

Note that checkAutomatically: "ON_ERROR_RECOVERY" will be ignored in the Expo client, although the imperative Updates methods will still function normally.

It is possible to entirely disable OTA JavaScript updates in a standalone app, by setting updates.enabled to false in app.json. This will ignore all code paths that fetch app bundles from Expo’s servers. In this case, all updates to your app will need to be routed through the iOS App Store and/or Google Play Store.

This setting is ignored in the Expo client.

React native – Facebook integration with expo

Facebook

Provides Facebook integration for Expo apps. Expo exposes a minimal native API since you can access Facebook’s Graph API directly through HTTP (using fetch, for example).

NOTA: React native has a built-in “Share” service that display device’s native dialog.

Follow Facebook’s developer documentation to register an application with Facebook’s API and get an application ID.

[—————–

[Fuente: https://developers.facebook.com/docs/apps#register]

Registro

Para que tu aplicación pueda acceder a nuestros productos o API, primero debes convertir tu cuenta de Facebook a una cuenta de desarrollador y registrar la aplicación mediante el panel de aplicaciones. Puedes hacerlo desde developers.facebook.com. El proceso de registro crea un identificador de la aplicación que nos permite conocer quién eres, nos ayuda a distinguir tu aplicación de otras aplicaciones y es una forma de que puedas proporcionar otros posibles materiales necesarios a la hora de configurar productos específicos o solicitar acceso a API confidenciales.

Una vez hecho el registro, haz clic en el enlace Configuración a la izquierda, y proporciona toda información adicional que tengas acerca de tu aplicación. La mayoría de los campos son sencillos de entender, pero para algunos hay más información disponible.

Identificador de la aplicación

Cuando te registres, generaremos un identificador de la aplicación único para tu aplicación. Vamos a usar mucho este identificador, ya que debe incluirse al hacer llamadas a las API. Esto puede configurarse fácilmente desde nuestros SDK, de modo que el identificador se incluya automáticamente en cualquier llamada a la API.

Plataforma

Nos permite saber cómo acceden los usuarios a tu aplicación; por ejemplo, desde un dispositivo móvil o desde un sitio web. De esta forma, más adelante podemos mostrarte productos y funciones específicos de la plataforma.

Política de privacidad y condiciones del servicio

Deben ser URL a la política de privacidad y a las condiciones del servicio que correspondan a los usuarios de tu aplicación. Supongamos que eres usuario de esta aplicación y quieres saber de qué modo el desarrollador de aplicaciones protegerá tu privacidad y qué condiciones debes aceptar. Debes buscar una política de privacidad y las condiciones del servicio. Introduce las URL aquí. Si estás usando algún producto, permisos de inicio de sesión o funciones que requieren la revisión de aplicaciones, revisaremos el contenido en estas URL.

Situaciones y productos

Después de registrar tu aplicación, tienes la opción de seleccionar una o más situaciones. Seleccionar una situación agrega los productos relacionados con la situación. Después de confirmar tus situaciones, los productos agregados aparecen a la izquierda, y la documentación aparece a la derecha.

También puedes saltear la selección de situaciones y agregar productos adicionales.

——————–]

Take note of this application ID because it will be used as the appId option in your Expo.Facebook.logInWithReadPermissionsAsync call.

NOTA IMPORTANTE:

En la consola de developers de facebook hay que añadir que el app tenga inicio de sesión con facebook, Entonces eliges aplicación nativa android y ya puedes meter información de la app, como el package name y el android hashes (que se saca con el comando ‘expo fetch:android:hashes’).

Ejemplo de hacer login dentro del API de Facebook:

async function logIn() {
  const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync('<APP_ID>', {
      permissions: ['public_profile'],
    });
  if (type === 'success') {
    // Get the user's name using Facebook's Graph API
    const response = await fetch(
      `https://graph.facebook.com/me?access_token=${token}`);
    Alert.alert(
      'Logged in!',
      `Hi ${(await response.json()).name}!`,
    );
  }
}

Given a valid Facebook application ID in place of <APP_ID>, the code above will prompt the user to log into Facebook then display the user’s name. This uses React Native’s fetch to query Facebook’s Graph API.

 

React native – Push notifications with Expo

[Fuente: https://docs.expo.io/versions/latest/guides/push-notifications]

 

BIG INCONVENIENT: At the moment this post writting: with EXpo there is no way of getting an unique device identifier (react-native-device-info package need detaching from Expo)

Push Notifications

Push Notifications are an important feature to, as “growth hackers” would say, retain and re-engage users and monetize on their attention, or something. From my point of view it’s just super handy to know when a relevant event happens in an app so I can jump back into it and read more. Let’s look at how to do this with Expo. Spoiler alert: it’s almost too easy.

Note: iOS and Android simulators cannot receive push notifications. To test them out you will need to use a real-life device. Additionally, when calling Permissions.askAsync on the simulator, it will resolve immediately with “undetermined” as the status, regardless of whether you choose to allow or not.

There are three main steps to wiring up push notifications:

  • sending a user’s Expo Push Token to your server
  • calling Expo’s Push API with the token when you want to send a notification
  • and responding to receiving and/or selecting the notification in your app (for example to jump to a particular screen that the notification refers to).

In order to send a push notification to somebody, we need to know about their device. Sure, we know our user’s account information, but Apple, Google, and Expo do not understand what devices correspond to “Brent” in your proprietary user account system. Expo takes care of identifying your device with Apple and Google through the Expo push token, which is unique each time an app is installed on a device. All we need to do is send this token to your server so you can associate it with the user account and use it in the future for sending push notifications.

import { Permissions, Notifications } from 'expo';

const PUSH_ENDPOINT = 'https://your-server.com/users/push-token';

async function registerForPushNotificationsAsync() {
  const { status: existingStatus } = await Permissions.getAsync(
    Permissions.NOTIFICATIONS
  );
  let finalStatus = existingStatus;

  // only ask if permissions have not already been determined, because
  // iOS won't necessarily prompt the user a second time.
  if (existingStatus !== 'granted') {
    // Android remote notification permissions are granted during the app
    // install, so this will only ask on iOS
    const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
    finalStatus = status;
  }

  // Stop here if the user did not grant permissions
  if (finalStatus !== 'granted') {
    return;
  }

  // Get the token that uniquely identifies this device
  let token = await Notifications.getExpoPushTokenAsync();

  // POST the token to your backend server from where you can retrieve it to send push notifications.
  return fetch(PUSH_ENDPOINT, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      token: {
        value: token,
      },
      user: {
        username: 'Brent',
      },
    }),
  });
}

Push notifications have to come from somewhere, and that somewhere is your server, probably (you could write a command line tool to send them if you wanted, it’s all the same). When you’re ready to send a push notification, grab the Expo push token off of the user record and send it over to the Expo API using a plain old HTTPS POST request. We’ve taken care of wrapping that for you in a few languages:

[Fuente: https://docs.expo.io/versions/latest/guides/using-fcm]

Using FCM for Push Notifications

Firebase Cloud Messaging is a popular option for delivering push notifications reliably and is required for all new standalone Android apps made with Expo. To set up your Expo Android app to get push notifications using your own FCM credentials, follow this guide closely.

Note that FCM is not currently available for Expo iOS apps.

  1. If you have not already created a Firebase project for your app, do so now by clicking on Add project in the Firebase Console.
  2. In your new project console, click Add Firebase to your Android app and follow the setup steps. Make sure that the Android package name you enter is the same as the value of android.package in your app.json.
  3. Download the google-services.json file and place it in your Expo app’s root directory.
  4. In your app.json, add an android.googleServicesFile field with the relative path to the google-services.json file you just downloaded. If you placed it in the root directory, this will probably look like
{
  ...
  "android": {
    "googleServicesFile": "./google-services.json",
    ...
  }
}
Finally, make a new build of your app by running expo build:android.

In order for Expo to send notifications from our servers using your credentials, you’ll need to upload your secret server key. You can find this key in the Firebase Console for your project:

  1. At the top of the sidebar, click the gear icon to the right of Project Overview to go to your project settings.
  2. Click on the Cloud Messaging tab in the Settings pane.
  3. Copy the token listed next to Server key.
  4. Run expo push:android:upload --api-key <your-token-here>, replacing <your-token-here> with the string you just copied. We’ll store your token securely on our servers, where it will only be accessed when you send a push notification.

That’s it — users who run this new version of the app will now receive notifications through FCM using your project’s credentials. You just send the push notifications as you normally would (see guide). We’ll take care of choosing the correct service to send the notification.

React Native Animations – Info compilation

[Fuente: https://facebook.github.io/react-native/docs/animations]

[Fuente: https://scotch.io/@rakshitkrsoral/the-beginners-guide-to-using-react-native-animated-api]

[Fuente: https://www.raizlabs.com/dev/2018/03/react-native-animations-part1/]

Animations

Animations are very important to create a great user experience. Stationary objects must overcome inertia as they start moving. Objects in motion have momentum and rarely come to a stop immediately. Animations allow you to convey physically believable motion in your interface.

React Native provides two complementary animation systems:

  • Animated for granular and interactive control of specific values,
  • LayoutAnimation for animated global layout transactions.

Animated API

The Animated API is designed to make it very easy to concisely express a wide variety of interesting animation and interaction patterns in a very performant way. Animated focuses on declarative relationships between inputs and outputs, with configurable transforms in between, and simple start/stop methods to control time-based animation execution.

Animated exports four animatable component types: ViewTextImage, and ScrollView, but you can also create your own using Animated.createAnimatedComponent().

For example, a container view that fades in when it is mounted may look like this:

import React from 'react';
import { Animated, Text, View } from 'react-native';

class FadeInView extends React.Component {
  state = {
    fadeAnim: new Animated.Value(0),  // Initial value for opacity: 0
  }

  componentDidMount() {
    Animated.timing(                  // Animate over time
      this.state.fadeAnim,            // The animated value to drive
      {
        toValue: 1,                   // Animate to opacity: 1 (opaque)
        duration: 10000,              // Make it take a while
      }
    ).start();                        // Starts the animation
  }

  render() {
    let { fadeAnim } = this.state;

    return (
      <Animated.View                 // Special animatable View
        style={{
          ...this.props.style,
          opacity: fadeAnim,         // Bind opacity to animated value
        }}
      >
        {this.props.children}
      </Animated.View>
    );
  }
}

// You can then use your `FadeInView` in place of a `View` in your components:
export default class App extends React.Component {
  render() {
    return (
      <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
        <FadeInView style={{width: 250, height: 50, backgroundColor: 'powderblue'}}>
          <Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>Fading in</Text>
        </FadeInView>
      </View>
    )
  }
}

Let’s break down what’s happening here. In the FadeInView constructor, a new Animated.Value called fadeAnim is initialized as part of state. The opacity property on the View is mapped to this animated value. Behind the scenes, the numeric value is extracted and used to set opacity.

When the component mounts, the opacity is set to 0. Then, an easing animation is started on the fadeAnimanimated value, which will update all of its dependent mappings (in this case, just the opacity) on each frame as the value animates to the final value of 1.

This is done in an optimized way that is faster than calling setState and re-rendering.
Because the entire configuration is declarative, we will be able to implement further optimizations that serialize the configuration and runs the animation on a high-priority thread.

Configuring animations

Animations are heavily configurable. Custom and predefined easing functions, delays, durations, decay factors, spring constants, and more can all be tweaked depending on the type of animation.

Animated provides several animation types, the most commonly used one being Animated.timing(). It supports animating a value over time using one of various predefined easing functions, or you can use your own. Easing functions are typically used in animation to convey gradual acceleration and deceleration of objects.

By default, timing will use a easeInOut curve that conveys gradual acceleration to full speed and concludes by gradually decelerating to a stop. You can specify a different easing function by passing a easing parameter. Custom duration or even a delay before the animation starts is also supported.

For example, if we want to create a 2-second long animation of an object that slightly backs up before moving to its final position:

Animated.timing(this.state.xPosition, {
  toValue: 100,
  easing: Easing.back(),
  duration: 2000,
}).start();

Take a look at the Configuring animations section of the Animated API reference to learn more about all the config parameters supported by the built-in animations.

Composing animations

Animations can be combined and played in sequence or in parallel. Sequential animations can play immediately after the previous animation has finished, or they can start after a specified delay. The Animated API provides several methods, such as sequence() and delay(), each of which simply take an array of animations to execute and automatically calls start()/stop() as needed.

For example, the following animation coasts to a stop, then it springs back while twirling in parallel:

Animated.sequence([
  // decay, then spring to start and twirl
  Animated.decay(position, {
    // coast to a stop
    velocity: {x: gestureState.vx, y: gestureState.vy}, // velocity from gesture release
    deceleration: 0.997,
  }),
  Animated.parallel([
    // after decay, in parallel:
    Animated.spring(position, {
      toValue: {x: 0, y: 0}, // return to start
    }),
    Animated.timing(twirl, {
      // and twirl
      toValue: 360,
    }),
  ]),
]).start(); // start the sequence group

If one animation is stopped or interrupted, then all other animations in the group are also stopped. Animated.parallel has a stopTogether option that can be set to false to disable this.

You can find a full list of composition methods in the Composing animations section of the Animated API reference.

Combining animated values

You can combine two animated values via addition, multiplication, division, or modulo to make a new animated value.

There are some cases where an animated value needs to invert another animated value for calculation. An example is inverting a scale (2x –> 0.5x):

const a = new Animated.Value(1);
const b = Animated.divide(1, a);

Animated.spring(a, {
  toValue: 2,
}).start();

Animation Drivers

Drivers are nodes in the graph that change Animated Values every frame. These are things like Animated.Timing, which drives a value over time, or Animated.Decay, which reduces a value every frame. We can connect an Animated.Event to an onScroll event: this drives a value as the user scrolls. We can start or stop a Driver, and connect it to other Animated Values or Drivers. When the driver updates each frame, its new value propagates down through the graph, updating its child Animation Drivers or Animated Values and eventually updating view properties.

Animated.decay(this.valueToAnimate, {
   velocity: 2.0,
   deceleration: 0.9
}).start();

In the decay animation above, our Animated Value starts at a velocity, and slows over time.

Define how the animated value will change over time

All animated values can be defined w.r.t change in time using Animated.timingmethod. Animated.timing has 3 common parameters that you can set:

  • Decay – starts with an initial velocity and gradually slows to a stop
  • Duration – starts the animation after a certain duration
  • Easing – To understand how easing functions work, you can prefer reading docs. On the surface level, these are the functions that allows developers to implement physically believable motion in React Native animations. By default, a typical easing function is linear, but if you want to override it to make something like Easing.sin(t) or Easing.bezier(x1, y1, x2, y2), you can add the line “import { ‘Easing’ } from ‘react-native'”

In the following example, I am fading animation over 200 ms.

Animated.timing(this.state.opacity, {toValue: 0, duration: 200})

Using Native Animations in Animated API

Changing an animation driver (like Animated.value, Animated.Timing or Animated.Spring) to use native animations is as easy as adding the property useNativeDriver: true. The same goes for an Animated.Event:

Animated.event( [{ ... }], { useNativeDriver: true } )

Note: – Always use Native Animations wherever possible in your React Native app.

Transform Operations

We convert Animated Values to new Animated Values through Transform operations.

Animated.add() , Animated.multiply() or Animated.interpolate() are examples of Transforms. We can run a Transform on any other node in the Animated graph: an animated value (new Animated.Value(42)).interpolate(...), or another transform: animatedPosition.interpolate(...).interpolate(...).

In one of our apps, we show a large illustration in the navbar, and minimize it as the screen scrolls. This gives the user more space to be productive. To do this, we pass the scroll distance (an Animated Value) as a prop on the NavBar component. The NavBar interpolates the scroll distance into a position or opacity, fading itself out and moving it up and off the screen.

ewewe
we
we
we
we
we
we

Publishing a NativeScript Android App in Google Play

[Fuente: https://developer.android.com/studio/publish/app-signing#signing-manually]

Compilar y firmar tu aplicación desde la línea de comandos

No necesitas Android Studio para firmar tu aplicación. Puedes hacerlo desde la línea de comandos con la herramienta apksigner, o bien configurar Gradle para que se encargue de ello durante la compilación. En cualquier caso, debes generar primero una clave privada usando keytool. Por ejemplo:

keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias

Nota: keytool se encuentra en el directorio bin/ de tu JDK. Para encontrar tu JDK desde Android Studio, selecciona File > Project Structure y luego haz clic en SDK Location. Con esto, verás la ubicación del JDK.

En este ejemplo se te solicitan contraseñas para el keystore y la clave, y para otorgar los campos de nombre distinguido de tu clave. Luego, se genera el keystore como un archivo llamado my-release-key.jks, que se guarda en el directorio actual (puedes moverlo cuando lo desees). El keystore contiene una clave única válida por 10 000 días.

Ahora puedes compilar un APK sin firma y firmarlo manualmente o configurar Gradle para que firme tu APK.

Compilar un APK sin firma y firmarlo manualmente

  1. Abre una línea de comandos, dirígete a la raíz del directorio de tu proyecto, desde Android Studio, y selecciona View > Tool Windows > Terminal. Luego invoca la tarea assembleRelease:
    gradlew assembleRelease

    De esta forma, se creará un APK con el nombre module_name-unsigned.apk en project_name/module_name/build/outputs/apk/. En este punto, el APK no está firmado ni alineado; no puede instalarse hasta que se firme con tu clave privada.

  2. Alinea el APK sin firma usando zipalign:
    zipalign -v -p 4 my-app-unsigned.apk my-app-unsigned-aligned.apk
    

    zipalign garantiza que todos los datos descomprimidos comiencen con una alineación de bytes en particular relacionada con el comienzo del archivo, lo que reduce el consumo de memoria RAM de una aplicación.

  3. Firma tu APK con tu clave privada usando apksigner:
    apksigner sign --ks my-release-key.jks --out my-app-release.apk my-app-unsigned-aligned.apk
    

    En este ejemplo se genera el APK firmado en my-app-release.apk una vez que se firma con una clave privada y un certificado, que se almacenan en un único archivo de keystore: my-release-key.jks.

    La herramienta apksigner admite otras opciones de firmas, incluidas la firma de un archivo APK con archivos de clave y certificado privados separados y la firma de un APK con varios firmantes. Para obtener información detallada, consulta la referencia de apksigner.

    Nota: Para usar la herramienta apksigner, debes tener instalada la revisión 24.0.3 o revisiones posteriores de Android SDK Build Tools. Puedes actualizar este paquete con SDK Manager.

  4. Verifica que tu APK esté firmado.
    apksigner verify my-app-release.apk
    

Configurar Gradle para que firme tu APK

  1. Abre el archivo build.gradle de nivel de módulo y agrega el bloque signingConfigs {} con entradas para storeFilestorePasswordkeyAlias y keyPassword, y luego pasa ese objeto a la propiedad signingConfig en tu tipo de compilación. Por ejemplo:
    android {
        ...
        defaultConfig { ... }
        signingConfigs {
            release {
                storeFile file("my-release-key.jks")
                storePassword "password"
                keyAlias "my-alias"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                signingConfig signingConfigs.release
                ...
            }
        }
    }

    Debido a que Gradle lee las rutas de acceso relativas a build.gradle, el ejemplo anterior funciona únicamente si my-release-key.jks está en el mismo directorio que el archivo build.gradle.

    Nota: En este caso, el keystore y la contraseña de clave pueden verse directamente en el archivo build.gradle. Para una mayor seguridad, debes quitar la información de firmas de tu archivo de compilación.

  2. Abre una línea de comandos en el directorio raíz de tu proyecto e invoca la tarea assembleRelease:
    gradlew assembleRelease

De esta forma, se creará un APK con el nombre module_name-release.apk en project_name/module_name/build/outputs/apk/. Este archivo APK se firma con la clave privada especificada en tu archivo build.gradle y se alinea con zipalign.

Ahora que ya configuraste la compilación de lanzamiento con tu clave de firma, la tarea “install” estará disponible para ese tipo de compilación. Por lo tanto, podrás compilar, alinear e instalar el APK de lanzamiento en un emulador o dispositivo con la tarea installRelease.

Un APK firmado con tu clave privada está listo para la distribución, pero antes debes leer más información sobre cómo publicar tu aplicación y revisar la lista de comprobación de lanzamiento para Google Play.

[Fuente: https://docs.nativescript.org/tooling/publishing/publishing-android-apps]

Overview

Tip: Instead of the CLI, use NativeScript Sidekick for easy app store publishing to both the iOS App Store and Google Play. Sidekick also offers starter kits, plugin management, and cloud-based builds for iOS and Android.

You can publish a NativeScript app in Google Play the same way you would release a purely native Android app.

  1. Make sure that you have a .keystore file to sign your app with. For more information, see How to create a .keystore file?
  2. Build your project in release mode by running the following command:
    tns build android --release --key-store-path <path-to-your-keystore> --key-store-password <your-key-store-password> --key-store-alias <your-alias-name> --key-store-alias-password <your-alias-password>
    

    Note: At the end of <path-to-your-keystore> you should also add the exact name of your keystore.

Example(Windows): tns build android --release --key-store-path C:\keystore\Telerik.keystore --key-store-password sample_password --key-store-alias Telerik --key-store-alias-password sample_password .

Example(Mac): tns build android --release --key-store-path ~/Desktop/keystore/Telerik.keystore --key-store-password sample_password --key-store-alias Telerik --key-store-alias-password sample_password .

  1. Obtain the release .apk located at <app_name>/platforms/android/app/build/outputs/apk/<app_name>-release.apk.
  2. Publish your Android app by uploading the .apk file to the Google Developer Console. For more information, see How to publish an Android app?

Application Id and Package Name

Both Package Name, and Application Id, are unique identifiers, provided by you for your app.

  • Package Name is used to identify resources such as the R.
  • Application Id is used to identify your app on devices and at the Google Play.

In the NativeScript framework, both are set to the applicationId in app.gradle. The NativeScript CLI build system will set them as the package attribute in the generated project in platforms/android/src/main/AndroidManifest.xml. In the app/App_Resources/Android/AndroidManifest.xml it will use a placeholder: package="__PACKAGE__". Do not modify the package attribute there.

NOTE: To edit the Package Name and the Application Id, modify the package.jsonof your app and set the nativescript.id key. You may need to delete platforms/android and rebuild using the CLI command tns prepare android.

Read more about “ApplicationId versus PackageName”.

App name

This is the display name for your app. It is purely cosmetic but highly important. For example, it appears under the app icon. The value can be stored in the app/App_Resources/Android/values/strings.xml file

<resources>
    <string name="app_name">MyAppName</string>
    <string name="title_activity_kimera">MyAppName</string>
</resources>

By default (or if the project lacks the values above) your application name is generated with the create command (e.g. tns create testApp will have app name testApp)

You can check out more information about the elements you can define in the AndroidManifest.xml here.

App icons

App icons are defined similar to the app name. The icon name is defined in the app/App_Resources/Android/AndroidManifest.xml file, as an android:icon="@drawable/icon"attribute, on the <application> element.

The actual .PNG icons stay at the Android resources in app/App_Resource/Android/<DPI>/icon.png, DPIs:

DIRECTORY DPI SCREEN SIZE
drawable-ldpi 120 Low density screen 36px x 36px
drawable-mdpi 160 Medium density screen 48px x 48px
drawable-hdpi 240 High density screen 72px x 72px
drawable-xhdpi 320 Extra-high density screen 96px x 96px
drawable-xxhdpi 480 Extra-extra-high density screen 144px x 144px
drawable-xxxhdpi 640 Extra-extra-extra-high density screen 192px x 192px

Note: NativeScript supports adaptive icons on Android 8 and above (API 26+). No code changes are required – follow the Android guidelines for creating adaptive icons for your application.

Launch screen

Android has no built-in mechanism to provide launch screen image. Here is a documentation article that describes how to implement a launch screen in the NativeScript framework.

Certificates

Debug certificate

These are automatically generated by the Android SDK tools for you.

In debug mode, you sign your app with a debug certificate. This certificate has a private key with a known password. The process is handled by the Android tooling.

You can read more at “Signing in Debug Mode”.

Release certificate

The release certificate for Android is created by you; it does not have to be signed by a certificate authority. It is easier to create a release certificate for Android than it is for iOS. You should, however, be more careful with your certificate.

A few pitfalls are:

  • You create the certificate only once. If you lose it, you will not be able to publish any updates to your app, because you must always sign all versions of your app with the same key.
  • If your certificate expires, you will not be able to renew it. Ensure long validity when creating a new certificate (for 20+ years).
  • If a third party obtains your private key, that party could sign and distribute apps that maliciously replace your authentic apps or corrupt them.

You can generate a private key for a release certificate using the keytool.

keytool -genkey -v -keystore <my-release-key>.keystore -alias <alias_name> -keyalg RSA -keysize 2048 -validity 10000

This will run an interactive session collecting information about your name, organization and most importantly — keystore and alias passwords.

Google Play Developer Console

You will need a developer account and you will need to log into the Google Play Developer Console.

Go to the All applications section and click the + Add new application button.

  • You will get prompted to provide the app title
  • You can then proceed with the store listings.
  • You can fill in app description, screenshots and so on.
  • You can also submit an APK. Read about how to obtain an APK from a NativeScript app.

Builds

Build versioning

We have already explained how the Application Id is set in your project, how icons are added to your app and how you can set the display name.

Before the build, you need to set two important things: the versionCode and the android:versionName.

When a build is uploaded, its versionCode should be larger than previous builds. A new build with a higher versionCode is considered an upgrade to builds that have a lower versionCode. The versionCode is an integer so you should carefully consider a strategy for versioning.

Both values are stored in app/App_Resources/Android/AndroidManifest.xml.

NOTE: android:versionName is a string value, which is used to represent the application version to the user whereas android:versionCode, which is integer value showing version of the application code relative to the other versions. You can read more about “Versioning Your Applications”.

In the app/App_Resources/Android/AndroidManifest.xml, the versionCode and versionName appear as:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="org.nativescript.name"
      android:versionCode="2"
      android:versionName="1.1">
      ...

Build signed release APK

You can perform a full build and produce a signed APK using the NativeScript CLI:

tns build android --release --key-store-path <path-to-your-keystore> --key-store-password <your-key-store-password> --key-store-alias <your-alias-name> --key-store-alias-password <your-alias-password> --copy-to <apk-location>.apk

You can then use the produced <apk-location>.apk for upload to Google Play.

APKs with ABI splits

If you want to reduce the apk sizes you can check how to achieve this in Android ABI Split article.

Submit with the Google Play Developer Console

To submit your app to the Google Play Developer Console:

  1. Log into the Google Play Developer Console.
  2. Select your application and go to the APK section.
  3. Choose ProductionBeta or Alpha stage and click the Upload new APK.
  4. Select the APK produced by the CLI.

You can read more about these stages at “Set up alpha/beta tests”.

Once you upload your APK, it will go through a review. When approved, you can move it to production to make it available on Google Play.

Submission automation

Some tools allow the submission process to be automated – MIT Licensed one: fastlane. You can also hack your own scripts around the Google Play Developer API.

Publish

Once you successfully upload your APK, and it passes Google review, you will be able to move your APK to production, and it will go live on Google Play.