How to make Flask Heroku app a Progressive Web Application (pwa) - flask

I've deployed a simple web application named "ogagnage" with Flask and Heroku (gunicorn). It works perfectly in production and I try now to run it as a Progressive Web Application. Following Heroku instructions, I've created manifest.json and service workerfile (sw.js). Manifest file is recognized by my browsers but not service worker and i unfortunately don't understand why...
Manifest.json OK
sw not recognized
sw error message
Here is the structure of my project:
Directory tree
And here are the code added:
In views.py :
#app.route('/sw.js')
def sw():
return app.send_static_file('sw.js')
#app.route('/manifest.json')
def manifest():
return app.send_static_file('manifest.json')
#app.route('/app/static/app.js')
def app_js():
return app.send_static_file('app.js')
In sw.js :
console.log('Hello from sw.js');
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.2.0/workbox-sw.js');
if (workbox) {
console.log("Yay! Workbox is loaded 🎉");
workbox.precaching.precacheAndRoute([
{
"url": "/",
"revision": "1"
}
]);
workbox.routing.registerRoute(
/\.(?:js|css)$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'static-resources',
}),
);
workbox.routing.registerRoute(
/\.(?:png|gif|jpg|jpeg|svg)$/,
workbox.strategies.cacheFirst({
cacheName: 'images',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
],
}),
);
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
cacheName: 'googleapis',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 30,
}),
],
}),
);
} else {
console.log("Boo! Workbox didn't load 😬");
}
In app.js:
(function() {
if('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('sw.js')
.then(function(registration) {
console.log('Service Worker Registered');
return registration;
})
.catch(function(err) {
console.error('Unable to register service worker.', err);
});
navigator.serviceWorker.ready.then(function(registration) {
console.log('Service Worker Ready');
});
});
}
})();
In my base template:
<script type="text/javascript" src="../static/js/app.js"></script>
And my git:
Github project
Hope someone could help me, it would be a beautiful Christmas present.
Tx

You also need to send the manifest.json file
#app.route('/manifest.json')
def manifest():
return app.send_from_directory('static', 'manifest.json')
If you'd like a complete example, I have created a Flask PWA before. Here is the repository: https://github.com/MurphyAdam/Flask-chatbot

I've solved the problem after changing #app routes in views.py and relative path to go to sw.js in app.js (cf code updated)
Now , my service worker is working:
Service Worker Ok

Related

Loading remote Angular MFE into React shell using Webpack's Module federation

Can some one help me with an example to load remote Angular micro frontend application into React Shell using webpack's Module federation concept?
I have checked https://www.angulararchitects.io/en/aktuelles/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly/ where React is loaded in angular. But I am looking for other way.
With webpack, you can put the bootstrapping of angular app in a mount method and export this method. This must be done in another file to avoid conflicts for angular to run independently.
(Make sure to have the bootstrap.js file imported in main.ts, as used by module federation)
remote/src/load.ts
const mount = ()=>{
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
export{mount}
expose this new file with webpack from angular
remote/webpack.config.ts
output: {
scriptType: 'text/javascript',
},
plugins: [
new ModuleFederationPlugin({
name: "remoteMfe",
filename: "remoteEntry.js",
exposes: {
'./webcomponent':'./src/loadApp.ts',
},
})
load the exposed module in react app using webpack
host/webpack.config.js
plugins: [
new ModuleFederationPlugin(
{
name: 'host',
filename:
'remoteEntry.js',
remotes: {
RemoteMFE:
'remoteMfe#http://localhost:3000/remoteEntry.js',
},
}
),
import the mount method that was exposed from angular remote and get the root element(i.e. ) from angular mfe. This can be used as a regular DOM element
host/src/Example.js
const ExampleComponent = () => {
useEffect(() => {
mount();
}, []);
return <div className="left-sidebar-module"><app-root></app-root></div>;
};

How can i load changes to my code in a Vue app?

I deployed a Django+VueJS app that uses django webpack loader in order to render Vue apps in my Django templates. I used Nginx and Gunicorn to deploy the app to a DigitalOcean VPS, everything works without any problem but i have some doubts on how to edit my components in production, since i'm fairly new to Vue
Here is my vue.config:
const BundleTracker = require("webpack-bundle-tracker");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const pages = {
'main': {
entry: './src/main.js',
chunks: ['chunk-vendors']
},
}
module.exports = {
pages: pages,
runtimeCompiler: true,
filenameHashing: false,
productionSourceMap: false,
publicPath: process.env.NODE_ENV === 'production'
? 'static/vue'
: 'http://localhost:8080/',
outputDir: '../django_vue_mpa/static/vue/',
chainWebpack: config => {
config.optimization
.splitChunks({
cacheGroups: {
moment: {
test: /[\\/]node_modules[\\/]moment/,
name: "chunk-moment",
chunks: "all",
priority: 5
},
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "chunk-vendors",
chunks: "all",
priority: 1
},
},
});
Object.keys(pages).forEach(page => {
config.plugins.delete(`html-${page}`);
config.plugins.delete(`preload-${page}`);
config.plugins.delete(`prefetch-${page}`);
})
config
.plugin('BundleTracker')
.use(BundleTracker, [{filename: '../vue_frontend/webpack-stats.json'}]);
// Uncomment below to analyze bundle sizes
// config.plugin("BundleAnalyzerPlugin").use(BundleAnalyzerPlugin);
config.resolve.alias
.set('__STATIC__', 'static')
config.devServer
.public('http://localhost:8080')
.host('localhost')
.port(8080)
.hotOnly(true)
.watchOptions({poll: 1000})
.https(false)
.headers({"Access-Control-Allow-Origin": ["*"]})
}
};
So in order to deploy the Vue part i did npm run build and npm created a bunch of files in my static directory. Now, every time i edit a component, in order to see the changes on the web, i do npm run build every time, which takes some time. Is this how am i supposed to do it? Or is there a shorter way?
I don't know about django, But I know about vue..
is this how am I supposed to do it?
For me, I don't suggest it, you can use your django as a backend for your frontend
that should mean you would have 2 servers running. 1 for your django and 1 for your vue app. use XHR request to access your django App, remember to handle CORS. IMHO I don't want vue to be used as a component based framework.
is there a shorter way.
YES, and this is how you do it.
add to package.json
{
...,
scripts: {
...,
'watch' : 'vue-cli-service build --watch --inline-vue',
...,
}
}
while using the following settings in vue.config.js
module.exports = {
'publicPath': '/django/path/to/public/folder',
'outputDir': '../dist',
'filenameHashing': false,
runtimeCompiler: true,
'css': {
extract: true,
},
}
i forgot about how publicPath and outputDir works..
but you can check it out here https://cli.vuejs.org/config/#publicpath
regarding the code on the package.json file..
you can check it here
https://github.com/vuejs/vue-cli/issues/1120#issuecomment-380902334

How overcome with '#' , coming in URL through DeepLinking in Ionic 2?

I am trying to implement navigation in Ionic 2. I have tried with DeepLinking and i got the result, but '#' sign is comming in URL.
When '#' sign will come in URL then Google Analytic will not recognize the website, that's why i have tried to implement navigation in different ways like Angular 2 Routing, that supports both (HTML5 or hash URL style), but unable to implement in Ionic 2.
Ex- http://localhost:8100/#/registration - This one working fine but i want without '#'.
Like http://localhost:8100/registration
Thanks for help
I put in a PR for #ionic/app-scripts 3.2.5 to remedy this:
https://github.com/ionic-team/ionic-app-scripts/pull/1545
In the meantime you can edit some project and dependency files to enable it:
src/app/app.module.ts:
IonicModule.forRoot(MyApp,
{
locationStrategy: 'path'
},
{
links: [
{ component: RegistrationPage, name: 'registration', segment: 'registration' },
{ component: LoginPage, name: 'login', segment: 'login' },
{ component: HomePage, name: 'home', segment: '' }
]
})
src/index.html:
<head>
...
<base href="/" />
...
</head>
node_modules/#ionic/app-scripts/dist/dev-server/http-server.js:
function createHttpServer(config) {
var app = express();
app.set('serveConfig', config);
app.get('/', serveIndex);
app.use('/', express.static(config.wwwDir));
app.use("/" + serve_config_1.LOGGER_DIR, express.static(path.join(__dirname, '..', '..', 'bin'), { maxAge: 31536000 }));
// Lab routes
app.use(serve_config_1.IONIC_LAB_URL + '/static', express.static(path.join(__dirname, '..', '..', 'lab', 'static')));
app.get(serve_config_1.IONIC_LAB_URL, lab_1.LabAppView);
app.get(serve_config_1.IONIC_LAB_URL + '/api/v1/cordova', lab_1.ApiCordovaProject);
app.get(serve_config_1.IONIC_LAB_URL + '/api/v1/app-config', lab_1.ApiPackageJson);
app.get('/cordova.js', servePlatformResource, serveMockCordovaJS);
app.get('/cordova_plugins.js', servePlatformResource);
app.get('/plugins/*', servePlatformResource);
if (config.useProxy) {
setupProxies(app);
}
app.all('/*', serveIndex);
return app;
}
The line app.all('/*', serveIndex); is what will redirect any 404 file or directory not found errors to index.html. The locationStrategy: 'path' setting can then work normally with deeplinks and redirects under these circumstances.
Try to use pathLocationStrategy instead of HashLocationStrategy.
Add this in app.module.ts
import { LocationStrategy,
PathLocationStrategy } from '#angular/common';
...
#NgModule({
...
providers: [
{
provide: LocationStrategy,
useClass: PathLocationStrategy
},
...
Or other way is
IonicModule.forRoot(MyApp, {
locationStrategy: 'path'
})
And make sure to have a valid base href.
So here is the list of things which I did. Hope this helps.
We need to remove # in path of every url because Google Analytics rejects the urls with # in them. In App Module , add {locationStrategy: 'path'} to your App Module as follows :
IonicModule.forRoot(MyApp, {
locationStrategy: 'path'
})
2 .Now # is removed from the url. But when you refresh or directly access the url, this wont work because this is expected behaviour for any SPA. When you refresh the page , server tried to find the page at the location mentioned. As stated by #Parth Ghiya above For eg: if you hit localhost/abc , then server tries to find abc/index.html which actually doesn't exist.So to resolve this , you have wrote configurations on my server i.e to point every request to index.html . I am using node express server to deploy the app. Use the following code to route every request to index.html -
var express = require('express');
var path = require('path')
var app = express();
app.use(express.static(path.resolve(__dirname, "www")));
app.use('/*', function(req, res){
res.sendFile(__dirname+ '/www' + '/index.html');
});
app.set('port', process.env.PORT || 3000);
app.listen(app.get('port'), function() {
console.log("listening to Port", app.get("port"));
});

How do I properly setup app-test.js when unit testing ExtJS using Jasmine?

I am fairly new to both Jasmine(2.2) as well as ExtJS(4.2.1). I am following the Unit Testing instructions in the Sencha docs to test the feed-viewer example ExtJS application. I cannot seem to get the app-test file quite right to load the application.
Here is my app-test.js
Ext.Loader.setConfig({ enabled : true });
var Application = null;
Ext.onReady( function() {
Application = Ext.create('app', {
name: 'FV',
controllers: [
'Articles'
],
launch: function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
jasmineEnv.execute();
}
});
});
Here is the app.js
Ext.application({
name: 'FV',
paths: {
'Ext.ux': '../../../examples/ux/'
},
controllers: [
'Articles',
'Feeds'
],
autocreateViewport: true
});
And, in case this may be a part of the issue, my folders are structured as such:
FV/
- app/
- app-test/
- resources/
- specs/
- app.js
- app-test.js
- run-test.html
My test to see that the application has been properly loaded is failing. Chrome's dev tools show me two errors. The first is an XMLHttpRequest cannot load, and the second is an Uncaught TypeError: Object is not a function being thrown at the third line of app-test.js
I feel like there's something remarkably simple I may have missed.
Any help would be immensely appreciated!
I finally got it working. Here is my revised app-test.js
Ext.Loader.setConfig({ enabled : true });
Ext.ns('FV');
Ext.application({
name: 'FV',
appFolder: 'app',
controllers: ['Articles'],
autoCreateViewport: false,
launch: function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
jasmineEnv.execute();
}
});
Considering how different this is from the tutorial in the Sencha docs, and how much Jasmine has changed since 1.1.0 (which is the version their tutorial uses), it's definitely time for them to do an update.

How to use ember-pusher in ember-cli project

pusher in ember-cli project. I am sorry but i find if difficult to get my head around js tools.
Ember pusher github
steps done so for.
Inside ember-cli project: bower install --save pusher
In broccoli.js file added line: app.import('vendor/pusher/dist/pusher.js');
in .jshintrc
"predef": {
"document": true,
"window": true,
"MyappENV": true,
"Pusher": true
}
Then copied ember-pusher.amd.js from git mentioned link and saved in /vendor folder.
In broccoli.js file added line:
var App = Ember.Application.extend({
modulePrefix: 'Myapp', // TODO: loaded via config
Resolver: Resolver,
PUSHER_OPTS: {
key: '586f8kjhfkdf8d7f9',
connection: {},
logAllEvents: true
},
});
5.In app.js.
var App = Ember.Application.extend({
modulePrefix: 'Myapp',
Resolver: Resolver,
PUSHER_OPTS: {
key: '586f8kjhfkdf8d7f9',
connection: {},
logAllEvents: true
}
});
6. In application.js controller
import Ember from 'ember';
export
default Ember.Controller.extend({
PUSHER_SUBSCRIPTIONS: {
myChannel: ['my-event']
},
actions: {
myEvent: function () {
console.log('Event my event was triggered xxxxxxxxxxxxxxxxxxx');
}
}
});
I donot get any error message but pusher dashboard does not show any connections
app.import('vendor/ember-pusher/ember-pusher.amd.js', {
exports: {
'ember-pusher': [
'controller',
'binding',
'clientevents',
'initialize'
]
}
});
There is now an ember addon for this, with instructions in the README: https://github.com/ivanvotti/ember-cli-pusher
Here's what I did to get it working:
bower install --save pusher
Download ember-pusher.js to vendor/ember-pusher/ember-pusher.js from https://github.com/jamiebikies/ember-pusher#download
Add the following to your Brocfile.js
app.import('bower_components/pusher/dist/pusher.js');
app.import('vendor/ember-pusher/ember-pusher.js');
Add the following to config/environment.js
ENV.APP.PUSHER_OPTS = { key: 'your-app-key', connection: { } }
Log events from one of your controllers
import Ember from 'ember';
export default Ember.Controller.extend(EmberPusher.Bindings, {
logPusherEvents: true,
PUSHER_SUBSCRIPTIONS: {
myChannel: ['my-event']
}
}