How to configure an AngularJS app at load time? - configuration

I want to do something like this (but obviously not this exactly, because this function doesn't work this way)
angular.bootstrap( $("#myelement"), ['myModule'], {foo: bar} );
I want to pass in a configuration object, since we may want to have more than one instance of the app on a page, with different settings, etc. All I can think of are ugly workarounds. I'm thinking the best thing would be to override an "Options" service of my own making, but I still can't figure out the proper way to do that (tersely).
Thanks in advance!

How about you try something like this:
angular.module('configFoo', []).run(function() {});
angular.module('configBar', []).run(function() {});
angular.bootstrap(myEl, ['myModule', 'configFoo']);
angular.bootstrap(myOtherEl, ['myModule', 'configBar']);
http://docs.angularjs.org/api/angular.Module for all available module methods (you're probably only interested in .run() and .config())

Here is a working code:
http://jsfiddle.net/x060aph7/
angular.module('myModule', [])
.controller('myController', function($scope,myConfig) {
$scope.name = 'inst '+myConfig.foo;
})
;
var aConfig = [{foo:1},{foo:2},{foo:3}];
aConfig.forEach(function(config){
angular.module('fooConfig',[]).value('myConfig', config);
angular.bootstrap(getDiv(), ['myModule','fooConfig']);
});
function getDiv(){
var mDiv = document.createElement('div');
mDiv.setAttribute('ng-controller','myController');
mDiv.innerHTML = '<span>{{name}}</span>';
document.body.appendChild(mDiv);
return mDiv;
}

The following example helped us out bootstrapping a widget to a page. First a div is made - with a bit of jQuery - for the widget to load a template with an ng-include, it is controlled by WidgetLogoController. Next a module WidgetConfig is created that holds the widget's configuration.
$('#pageWidget').html(`<ng-include src="'/dist/templates/widgetLogo.html'"></ng-include>`)
.attr('ng-controller','WidgetLogoController');
var widgetConfig = {
'widgetId': data.pageWidgetId,
'areaId': data.area,
'pageId': data.pageId
};
angular.module('WidgetConfig', []).value('WidgetConfig', widgetConfig);
angular.bootstrap(document.getElementById('pageWidget'), ['Widget', 'WidgetConfig']);
Widget module includes the WidgetConfig configuration but also has a spot for it own in CONFIG:
(function (window, angular) {
'use strict';
window.app = angular.module('Widget', ['ngFileUpload', 'WidgetConfig'])
.constant('CONFIG', {
BASE_URL: 'http://osage.brandportal.com/'
});
})(window, angular);
WidgetController can access CONFIG and WidgetConfig.
(function (app) {
'use strict';
app.controller('WidgetLogoController', ['CONFIG', 'WidgetConfig',
function(CONFIG, WidgetConfig){
console.log('---WidgetLogoController');
console.log('CONFIG', CONFIG);
console.log('WidgetConfig', WidgetConfig);
}]);
}(app));

What about:
Load config and than load angular:
angular.element(document).ready(() => {
$.get('config', // url to my configuration
{},
function (data) {
window.config = data;
angular.bootstrap(document, ['myApp']);
}
);
});
Access the config:
angular.module('myApp').run(myAppRun);
function myAppRun($window) {
$window.config; // here I have config
}

Related

How do I load a WASM module in a Vue component without initializing the module every time?

I have created a Rust library of type cdylib using
cargo web build --target=wasm32-unknown-unknown
I use a modified version of the "rust-wasm-loader" NPM package to build and load the WASM file. rust-wasm-loader uses this as a way to use the Rust code:
const wasm = require('./main.rs')
wasm.initialize().then(module => {
// Use your module here
const doub = module.cwrap('doub', 'number', ['number'])
console.log(doub(21))
})
I do not want to initialize the module every time I want to use the code. How do I load the module and use it like a library?
Since the loading of WebAssembly is asynchronous and may actually take some time for large modules, you need to handle the state when the module is not loaded, and then let the rest of the application know when the WebAssembly module is loaded.
You do not say how you are handling state in your Vue application, but if you are e.g. using Vuex you can do something like this:
const doubPlugin = store => {
wasm.initialize().then(module => {
const doub = module.cwrap('doub', 'number', ['number'])
store.subscribe((mutation, state) => {
if (mutation.type === 'DOUB_REQUEST') {
store.commit('DOUB_RESULT', doub(mutation.payload))
}
})
store.commit('DOUB_READY')
})
}
const store = new Vuex.Store({
state,
mutations,
plugins: [doubPlugin]
})
I've done a similar thing in an Elm/WebAssembly application (relevant JavaScript), so if you want to see how this can be applied in practice you can check that out.
Making a wrapper JS module that performs initialization and re-exports the promise seems like the most straightforward approach.
// main.js
module.exports = require("./main.rs").initialize().then(module => {
return {
doub: module.cwrap('doub', 'number', ['number'])
};
});
Then anything can do
require("./main.js").then(api => {
console.log(api.doub(21));
});
and will always get the same module. Or alternatively you could invert the async part and do
// main.js
const api = require("./main.rs").initialize().then(module => {
return {
doub: module.cwrap('doub', 'number', ['number'])
};
});
exports.doub = async function (val) {
return (await api).doub(val);
};
Then users of your module could do
const api = require("./main.js");
api.doub(21).then(result => {
console.log(result);
});
I created a class to wrap the WebAssembly loading and created a cwrap for every function:
class mkLib {
ready = false
_mod = require("./main.rs").initialize().then(module => {
this._mod = module
this.doub = module.cwrap('doub', 'number', ['number'])
this.ready = true
})
}
export default mkLib
In the Vue component's data there is a variable for the new class and in watch I wait for a change in the ready property:
data () {
return {
mod: new mkLib,
ready: false
}
},
watch: {
'mod.ready': function () {
this.ready = true
// now this.mod.FUNC(PARAMS) can be used
console.log(this.mod.doub(20))
}
}

Generating HTML report in gulp using lighthouse

I am using gulp for a project and I added lighthouse to the gulp tasks like this:
gulp.task("lighthouse", function(){
return launchChromeAndRunLighthouse('http://localhost:3800', flags, perfConfig).then(results => {
console.log(results);
});
});
And this is my launchChromeAndRunLighthouse() function
function launchChromeAndRunLighthouse(url, flags = {}, config = null) {
return chromeLauncher.launch().then(chrome => {
flags.port = chrome.port;
return lighthouse(url, flags, config).then(results =>
chrome.kill().then(() => results));
});
}
It gives me the json output in command line. I can post my json here and get the report.
Is there any way I can generate the HTML report using gulp ?
You are welcome to start a bounty if you think this question will be helpful for future readers.
The answer from #EMC is fine, but it requires multiple steps to generate the HTML from that point. However, you can use it like this (written in TypeScript, should be very similar in JavaScript):
const { write } = await import(root('./node_modules/lighthouse/lighthouse-cli/printer'));
Then call it:
await write(results, 'html', 'report.html');
UPDATE
There have been some changes to the lighthouse repo. I now enable programmatic HTML reports as follows:
const { write } = await import(root('./node_modules/lighthouse/lighthouse-cli/printer'));
const reportGenerator = await import(root('./node_modules/lighthouse/lighthouse-core/report/report-generator'));
// ...lighthouse setup
const raw = await lighthouse(url, flags, config);
await write(reportGenerator.generateReportHtml(raw.lhr), 'html', root('report.html'));
I know it's hacky, but it solves the problem :).
I've run into this issue too. I found somewhere in the github issues that you can't use the html option programmatically, but Lighthouse does expose the report generator, so you can write simple file write and open functions around it to get the same effect.
const ReportGenerator = require('../node_modules/lighthouse/lighthouse-core/report/v2/report-generator.js');
I do
let opts = {
chromeFlags: ['--show-paint-rects'],
output: 'html'
}; ...
const lighthouseResults = await lighthouse(urlToTest, opts, config = null);
and later
JSON.stringify(lighthouseResults.lhr)
to get the json
and
lighthouseResults.report.toString('UTF-8'),
to get the html
You can define the preconfig in the gulp as
const preconfig = {logLevel: 'info', output: 'html', onlyCategories: ['performance','accessibility','best-practices','seo'],port: (new URL(browser.wsEndpoint())).port};
The output option can be used as the html or json or csv. This preconfig is nothing but the configuration for the lighthouse based on how we want it to run and give us the solution.

Angularjs $resource and $http synchronous call?

I want write two services one with a $http.get method and one with $resource
This service should receive a Json Object and looks like this, at the moment this code is direct in my controller and not in a service:
var csvPromise= $http.get(base_url + 'DataSource/1').success(function(data) {
$scope.data4=JSON.stringify(data);
});
The problem is, I want save received data in $scope.data4 and I want use this data after the $http.get call but the value is empty.
Direct after this call there is and Object that needs this value:
new myObject($scope.data4)
so myObject must wait so long until the data has arrived.
or can I make a synchronous call with $http or $resource ?
How can i do this ? I have found so many examples with promise and .then but nothing has worked for me.
EDIT: I have now written a service but it didn`t work:
var test=angular.module('myApp.getCSV', ['ngResource']);
test.factory('getCSV',function($log, $http,$q, $resource){
return {
getData: function (id) {
var csvPromise= $http.get(base_url +'DataSource/'+id)
.success(function(data) {
return data;
});
return csvPromise;
}
}
});
and then in my controller I call this:
getCSV.getData(1).then(function(theData){
$scope.data4=JSON.stringify(theData);
new myObject( $scope.data4); });
but this did not work. I thought if the $http.get receives the data then the then Function is called.
I don't believe you can do synchronous calls. That said, you have at least two options:
1) Pass in the data using the $routeProvider resolve feature. From the documentation:
An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected
An example on how to use this:
$routeProvider
.when('/your/path', {
templateUrl: '/app/yourtemplate.html',
controller: 'yourController',
resolve: {
data: ['$route', '$http', function($route, $http) {
return $http.get(base_url +'DataSource/1');
}]
}
})
And then in your controller:
app.controller('yourController', ['$scope', 'data', function($scope, data) {
$scope.data4 = JSON.stringufy(data);
var yourObj = new myObject($scope.data4);
}]);
2) The second option is to use promises and only instantiate your new myObject($scope.data4) once the promise successfully completes.
Your code needs to be changed just a bit:
$scope.data4 = '';
var csvPromise= $http.get(base_url +'DataSource/1');
csvPromise.then(function(data){
$scope.data4 = JSON.stringify(data);
}, function(data){
//error handling should go here
window.alert(data);
});
This should give you what it sounds to me like you need.
As i know, there's no way to sync~ call the http or resource. They're hard coded on AngularJS core file :
xhr.open(method, url, true);
And you don't want to hurt your users too by blocking the browser wait the data arrived. You'll better show how you make the nothing has worked for me so we can start working to fix it.
Have you try call new myObject($scope.data4) inside success method?
$http.get(...).success(function(data){
$scope.data4 = JSON.stringify(data); // I've no idea why do you need this.
var stuff = new myObject($scope.data4); // THis is now your.
});

Read local file in AngularJS

I used to work with RequireJS and Backbone and used requirejs/text and requirejs-plugins to load local json files I normally use for configuration.
How does one achieve the same with AngularJS?
Everyone seems to suggest to use $http, but is this the only way?
Do I really need to make 20 calls if I have 20 configuration files?
Maybe something like ng-constant is the "preferred" way?
This is what I did. But it uses $http though so I'm hoping someone has a better solution.
app.js:
var myModule = angular.module('myApp', []);
myModule.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/', {
templateUrl: 'html/home.html',
controller: 'MainCtrl as ctrl',
resolve: {
initializeData: function($q, $timeout, myService) {
return myService.promiseToHaveData();
}
}
});
});
myService.js:
var myModule = angular.module('myApp');
myModule.service('myService', function($http, $q) {
var _this = this;
this.promiseToHaveData = function() {
var defer = $q.defer();
$http.get('someFile.json')
.success(function(data) {
angular.extend(_this, data);
defer.resolve();
})
.error(function() {
defer.reject('could not find someFile.json');
});
return defer.promise;
}
});
Then I can inject myService anywhere and it will have all the fields from the json file.
I guess alternatively you could just make your .json files .js files, have them expose a global variable, and reference them in your index.html
Can you use jQuery's getJSON function?
E.g something like:
$.getJSON("config-1.json", function( data ) {
// do whatever you want
});
Here's an AngularJs service that uses the FileReader API:
http://odetocode.com/blogs/scott/archive/2013/07/03/building-a-filereader-service-for-angularjs-the-service.aspx

fake model response in backbone.js

How can I fake a REST response in my model s.t. it does not really go to the service but returns a fixed json?
If possible show me a version that does it with overriding sync() and a version that overrides fetch(). I failed with both so this will be a good education for as for the difference between them.
Backbone.Model.extend({
fetch: function(){
var model = this;
model.set({yourStatic: "Json Here"});
}
}
This should work. From the Backbone documentation:
fetch():
Resets the model's state from the server by delegating to Backbone.sync
If your question is related to unit testing your code without the need for a live API, have a look at Sinon.JS. It helps mocking entire API server responses for testing purposes.
Here's an example from the Sinon docs that mocks the $.ajax function of jQuery:
{
setUp: function () {
sinon.spy(jQuery, "ajax");
},
tearDown: function () {
jQuery.ajax.restore(); // Unwraps the spy
},
"test should inspect jQuery.getJSON's usage of jQuery.ajax": function () {
jQuery.getJSON("/some/resource");
assert(jQuery.ajax.calledOnce);
assertEquals("/some/resource", jQuery.ajax.getCall(0).args[0].url);
assertEquals("json", jQuery.ajax.getCall(0).args[0].dataType);
}
}
Take a look at backbone-faux-server. It will allow you to handle (and 'fake' a response for) any sync op (fetch, save, etc) per Model (or Collection).
Sinon.js is a good candidate, although if you want to simulate more than a few responses, it might become a lot of work to setup headers, handle write logic, etc.
Building up on Sinon.js, FakeRest goes a step further and simulates a complete REST API based on a JSON object - all client-side.
My code like that
// config
const TEST_JSON = require('./test.json')
const API_MAP = {
testA: 'someroot'
}
const FAKE_API_MAP = {
testA: TEST_JSON
}
// here's model
let BaseModel = Backbone.Model.extend({
url: function() {
return `${HOST}${API_MAP[this.resourceName]}/`
}
})
let FakeModel = Backbone.Model.extend({
fetch: function(options) {
return this.sync('', this, _.extend({}, options));
},
sync: function(method, model, options) {
this.set(FAKE_API_MAP[this.resourceName], this.options)
this.trigger('sync', this);
},
});
// now it's easy for switch them
let modelA = new BaseModel({
resourceName: 'testA'
})
modelA.fetch()
let fakeModelA = new FakeModel({
resourceName: 'testA'
})
fakeModelA.fetch()