Not able to load icons in a client side component - node.js

I have started a new NextJs app (with the beta app feature) and it doesn't seem to be able to load icons when in a client side component.
It is throwing errors like Could not find icon { prefix: 'far', iconName: 'home' }
I am using the #fortawesome/react-fontawesome to render the icons as recomened in the FA setup. And they successfully work when in server side components, but I think it fails in client side as it doesn't see the config.
FA is configured like this in the new layout.tsx file.
import { config, library } from '#fortawesome/fontawesome-svg-core'
import { fas } from '#fortawesome/pro-solid-svg-icons';
import { far } from '#fortawesome/pro-regular-svg-icons';
import { fal } from '#fortawesome/pro-light-svg-icons';
config.autoAddCss = false;
library.add(fas, far, fal);
The using a icon like so <FontAwesomeIcon icon={['far', 'home]} />
What sort of config do I need an be loaded at the client components?
I have tried dumping all the imports into a client side component - that fixes it, but then lots of bloats the app by bundling FA everywhere!

Related

Having Issues using Esri ArcGIS API for JavaScript in Lightning Web Component (require is not defined at eval)

I'm trying to implement Esri ArcGIS JS in Lightning Web Component. While using ArcGIS JS, the sample code uses require function to load modules. In order to do that I'm trying to use require.js. I downloaded it from here (Require.js). And then uploaded to my sandbox as static resource. I'm trying to use that static resource in my Lightning Web Component. I also added the script tag for the ArcGIS Javascript API in my Experience Cloud site's header as
<script src="https://js.arcgis.com/4.24"></script>
Lightning Web Component:
import { LightningElement, track } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import requireJS from '#salesforce/resourceUrl/requireJS';
export default class TestMap extends LightningElement {
renderedCallback() {
loadScript(this, requireJS).then(() => {
console.log('requireJS loaded');
require([
"esri/geometry/Extent"
], (
Extent
) => {
var initExtent = new Extent({
xmin: -15884312,
ymin: 1634835,
xmax: -6278767,
ymax: 7505198,
spatialReference: 102100
});
});
}).catch(exception => {
console.log(exception);
});
}
}
My problem right now, eventhough I can see in the Network tab that the require.js is loaded from static resource, require function cannot be found.
Exception message catched
I'm not sure where is the issue since this is how I loaded my all javascript files before.
I was expecting to see the the require function is working after the require.js script loaded from Static Resource.
This one is a bit tricky, I will try to guide you as much as I can.
First, don't put the script tag in your website header. This is a last chance solution, we'll keep it if nothing else work.
Second, requireJS is not compatible with LWC (or Locker Service to be precise). So you can forget it. loadScript is in someways similar.
Now the solution, usually I download the whole from a CDN and host it as a static resource. Then you can load it via loadScript and use it as per the documentation.
In case the library is really small, it could be created as a LWC and then be imported but usually libraries are too heavy regarding Salesforce limit.
Looking at the library, it seems that they do not provide any compiled full version (which is probably huge). In this case I would recommend to make a custom build locally containing only the necessary pieces of code and then uploading as a static resource. Unfortunately I can't help on this part as I still didn't do it myself yet.
Feel free to comment and I will improve my answer is it's unclear.

Using analytics in Angular app only on a specific route using Firebase

I have an Angular website which I would like to use Google Analytics with to track the page views. Im using Firebase through #angular/fire to host my website. Therefore, I added the #angular/fire library by ng add #angular/fire and enabled the services I needed, plus analytics.
Now according to the docs I can see how one can inject AngularFireAnalytics in their component to log page views:
import { AngularFireAnalytics } from '#angular/fire/compat/analytics';
constructor(analytics: AngularFireAnalytics) {
analytics.logEvent('custom_event', { ... });
}
But Im not able to find a way to only enable the whole analytics only on a specific route, and no where else in the website.
I want to set up different paths for the same LandingPageComponent, so that I can visualize the page views from different advertisement funnels. In general I do not want any means of analytics on any other part of the website except for the page view events related to these URLs that end up in the same component after all.
I managed to solve my question by defining a custom event and adding a condition to check for the specific route that I wanted in the component by injecting Location service from Angular:
import {Component, OnInit} from '#angular/core';
import {Location} from "#angular/common";
import {AngularFireAnalytics} from "#angular/fire/compat/analytics";
#Component({
selector: 'app-xxx',
templateUrl: './xxx.component.html',
styleUrls: ['./xxx.component.scss'],
})
export class XxxComponent implements OnInit {
constructor(readonly location: Location,
readonly analytics: AngularFireAnalytics) { }
ngOnInit(): void {
if (this.location.path() === '/myPath') {
this.analytics.logEvent('custom_event', { ... });
}
}
}
According to what I read, it is not possible to disable the default analytics data collection and instead to just send your custom event. Therefore, you either have to have default data collection enabled and send your custom event, or completely turn analytics off. Although, you can turn on and off the analytics from the component, which I'm not sure if its a good practice.
Please let me know if there is an alternative and better solution.

How do I add the explorer to GraphiQL using 'flask-graphql'?

I am serving GraphiQL using flask-graphql but do not see any way to enable GraphiQL's "explorer" without using Node.JS. Is there a way to enable the explorer for a Python (flask-graphql) backend?
Node.js is not required, but React is since both graphiql and graphiql-explorer are React components. flask-graphql just renders an HTML page with a script that renders the GraphiQL interface using React as shown here.
You should be able to provide your own template for rendering this page when creating your GraphQLView via the graphiql_template option as shown here.
Copy and paste the existing template, then add the script tag for the library:
<script src="//cdn.jsdelivr.net/npm/graphiql-explorer#0.4.6/graphiqlExplorer.min.js"></script>
And implement the component as shown here. Since you're not transpiling this code, you won't be able to use JSX, so you'll need to do something like...
React.createElement(
"div",
{ className: "graphiql-container" },
React.createElement(
GraphiQLExplorer,
{ /* props here */ },
),
React.createElement(
GraphiQL,
{ /* props here */ },
)
)
Someone asked on the flask-graphql repo how to do it and I provided the initial template setup to get the explorer with some minor styles over that: Enabling "explorer" at Flask-graphql.

Vuejs organization for a website

First time using Vuejs and have a question about organization. Building a website pages vary in functionality, one page may have many areas of functionality [modals, forms, pagination, content filtering via ajax]. Would it be best to have a new Vue() for each page stacking functionality by components or each section of functionality should have it's own new Vue(). Some of these sections may need to talk to one another. My concern is having a main app.js file that is bloated.
No, you can just use one new Vue() instance for all pages. It does not make sense to have multiple vue instances per page.
As on a page reload the instance is recreated. I guess you don't have a SPA. So you rather have some templates where you have your app container and the vue components there, right?
You can for example create a components/index.js where you import / export all your components. So you have an entry for your components. (Cleaner structure)
And in your main.js you simply import them.
import Vue from 'vue'
import Components from './components/'
const app = new Vue({
components: {...Components }
}).$mount('#app')
And your components.js will look like this
import Pagination from './components/pagination.vue'
import Modal from '.components/modal.vue'
const Components = {
Pagination,
Modal
}
export {...Components }
export default Components
You can then import your components as es6 modules
import {Modal, Pagination} from './components'
Which is a nice way of organizing them if you use them in other components.
However, as you don't use vue-router you can't (at least I don't know any way) use webpacks code splitting to create smaller bundles, depending on their route and components used on that route.

How to modularize AngularJS applications / plugins

I have a couple of (software-)architecture questions regarding a migration from Grails (REST-API, parts of AngularJS, MongoDB, Tomcat, Spock, several plugins) to Node.js + Angular.js.
I probably have to explain the structure of the Grails project fist, so here we go:
There is a main Grails application (beside a couple of other applications), which is built on several plugins. Each of these plugins is able to get executed by itself - that means it has its own UI, individual templates, services, controllers, routes, tests etc. It is also hosted on different repositories.
This is done by the Grails plugin mechanisms. The benefits are less testing-efforts, less compiling time, modularization, single responsibilities and so on.
But still, the time to compile and test are too expensive. Also I don't like the fact that the API delivers parts of the templates/views. I would like to have the backend APIs "just to be backend APIs" and the frontend "just to be the frontend".
So each AngularJS application/plugin will provide its own view, routes, service etc. But they might also depend on other plugins.
So what I would like to achieve is as follow:
One main AngularJS application, which includes several plugins (a plugin can be something like a report-generator, a guestbook or whatsoever, speaking of a single independent part of an application, either with a specific route, or just a small part of the page).
Each plugin must be a stand-alone AngularJS application (probably executable during development via grunt or so). So that the UI developer does not need to start the whole backend application, further that we may run functional tests only with JavaScript
Communication only via REST, The frontend must retrieve all it's data from the APIs
Each plugin must be testable on its own
A Plugin might require other plugins to work
The main index.html (and app.js?) might be provided by a Nginx server, which is decoupled from the rest of the backend (API)
Though I have a specific picture in my head, I am struggling in how to setup this architecture.
In Grails the plugin mechanisms are somehow merging the plugin dependant settings (like URL mappings, dependencies, etc) to the main application in which they get included/injected - this is what I want to achieve with AngularJS as well. So:
Are there some kind of same mechanisms for AngularJS?
How may I provide/merge the routes of each plugin into the main application?
How can I declare application- and plugin-dependencies?
What tools might be usefull for the build process?
How to establish lazy-retrievments of the plugin-resources (css/less files, views, services etc)?
Prevent the application to provide all resources of the plugins on startup (I guess the routes are required on startup though)
Since this is not just a how-to-do-this-or-that question I excuse myself if I am missing important parts or if some parts are not clear enough. Just ask me and I will answer each question in depths.
** This answer is incomplete **
I want to make sure I understand you before I dig into this.
Here's a quick implementation of a Loader module to manage lazy loading (plugins, vendor assets, admin stuff etc).
Does this help?
angular.module('Bizcoin.loader')
.service('Loader', Loader);
function Loader($injector, $ocLazyLoad, User) {
var Loader = {
load: load,
get: get,
plugins: {},
flags: {}
};
init();
return Loader;
function init() {
load('vendors');
if (userIsAdmin)
load('admin');
}
function load(plugin) {
Loader.plugins[plugin] = Loader[plugin] || $ocLazyLoad.load('path/to/'+plugin+'.js');
return Loader.plugins[plugin].then(setFlag);
function setFlag() {
return Loader.flags[plugin] = true;
}
}
function get(plugin) {
return load(plugin).then(function() {
return $injector.get(plugin);
});
}
}
I work on an large .Net/AngularJS application that is composed of 20+, independent, Areas (or modules) and some core functionality common and reused across all Areas.
Let me go into detail on how I do this for my particular case, and it might give some ideas. The fact that I use .Net is irrelevant here, since this can achieve with any framework.
Each Area acts as an independent application that depends only on the core functionality, always present. Each Area has its own ASP.Net MVC route.
Each Area registers with the core application the menu links it wants to provide.
When the customer goes to the application dashboard, only the core part of of the application. When the user clicks on link in the menu, it will navigate to the content provided by one of the Areas, and only the core plus the assets of that Area are loaded.
Lets see how this is done.
In the main page of the application I load the scripts like this:
<script type="text/javascript">
// a JS object with all the necessary app data from the server.
// e.g.: menu data, etc
window.appContext = #Html.Action("ApplicationContext", "Portal"));
</script>
#Scripts.Render("~/bundles/angular-main")
#RenderSection("AngularAreas", required: false)
I make use of dot .Net bundles and sections.
The main (core) AngularJS part of the application consists of angular configuration, internationalization services, global notifications service, reusable UI components, etc. This is loaded is #Scripts.Render("~/bundles/angular-main").
The #RenderSection("AngularAreas", required: false) section will be filled in by each area when the user navigates to that Area.
Lets see some AngularJS code.
Here is part of the main app.ts.
// If user is visiting an Area, the NgModules array will be augmented.
// with the modules the Area wants to provide (to be bootstrapped)
export var LoadNgModules = [
NgModules.Config,
NgModules.Core
];
angular.module(NgModules.Bootstraper, LoadNgModules);
angular.element(document).ready(function () {
angular.bootstrap(document, [NgModules.Bootstraper]);
});
Lets look at an example Area now.
And here is how an Area would supply its assets, to be outputted in #RenderSection("AngularAreas", required: false):
#section AngularAreas {
#Scripts.Render("~/bundles/areas/staff-management")
}
Its a simple bundle containing all the scripts for that Area.
Now, let's see the important part of the AngularJS code for this Area.
var StaffManagementNgModule = 'areas.staff-management';
// Push our self into the modules to be bootstrapped
LoadNgModules.push(StaffManagementNgModule );
// Define the module
angular
.module(StaffManagementNgModule , ['ngRoute', NgModules.Core])
.config([
'$routeProvider', '$locationProvider', ($routeProvider: ng.route.IRouteProvider, $locationProvider) => {
$routeProvider
.when(staff', { template: '<staff></staff>' })
.when('staff/details/:id', { template: '<staff-details></staff-details>' });
}
]);;
That is all, from here the Area is a normal Angular application.
To summarize, we load the main (core) AngularJS functionality and provide the LoadNgModules array that an area can fill with its own modules.
we load the Area scripts and and "our self" to the LoadNgModules array.
finally the run angular.bootstrap.
For completeness, here is a fragment of C# showing how an area would indicate to the main application that it is available
public class ItemManagementModuleRegistration : IModuleRegistration
{
public void Register(ModuleContext moduleContext)
{
string thisAreaName = "Staff";
moduleContext.RegisterMenu(menuContext =>
{
var ItemsMenu = menuContext.Items(thisAreaName);
// add urls and stuff...
});
// register more stuff with the moduleContext
}
}
Using reflection one can easily find what areas are "installed".
These are the main moving parts of the setup.
Each Area can have its own API and tests. It is quite flexible.

Resources