I'm creating a new OJET Application using the default nav template. My ViewModel code is becoming huge as I have many validations and APIs to call. How can I separate my entire validation logic in a separate JS file and import this file into my ViewModel. I tried to create a new file at the main.js level and added it to the define section of m viewmodel, but that's not working.
OJET has the library require.js which enables lazy loading.
In your ViewModel code, you must define your dependencies, in a similar way as here, and pass it as a parameter to be able to call this import (be careful with the order of the dependencies in define):
define([..., 'your-dependency-here', ...],
function(..., dependency, ...) {
dependency.function();
You need also to pass this new file in path_mapping.json in your js folder, something like this:
"your-dependency-here": {
"debug": {
"path": "path/to/dependency/from/jsFolder"
},
"release": {
"path": "path/to/dependency/from/jsFolder"
}
}
Also, do not create a loop of your dependencies, which cause an error in require.js.
Related
My reactjs app consists of a bunch of typescript files with a clean separation of GUI and model. The webworker needs most of model files, so maybe half of all files. I could imagine loading the webworker from exactly the same URL as the app as the model does reference neither the GUI nor React nor other unavailable stuff (At least, it shouldn't, and if so, it'd easy to clean it up).
There seem to be some problems:
finding the correct javascript files
injecting proper start up code into them
and probably others I haven't thought about yet.
The communication to the webworker is not a problem as all I need is a single async call passing and receiving some simple data.
There may be more issues like e.g., https://github.com/microsoft/TypeScript/issues/20595.
Before I learnt what I really need, I tried e.g., ttps://www.npmjs.com/package/#koale/useworker, which is nice, but seems to be able to deal with plain javascript dependencies only.
Finding the correct javascript files
What I can see in index.html is
<script src="/myapp/static/js/bundle.js"></script>
<script src="/myapp/static/js/0.chunk.js"></script>
<script src="/myapp/static/js/main.chunk.js"></script>
<script src="/myapp/main.4e45e2b4b645351b7733.hot-update.js"></script>
I guess, I could live without hot updates, however the names of the other three files change in production to something like "/myapp/static/js/2.28cf00cf.chunk.js".
Injecting proper start up code into them
When the worker loads, it executes some webpack code generated code which most probably crashes it. I'd need to avoid it somehow.
The questions
Is this doable at all?
Does it make sense or is there a better approach?
For a seamless integration of worker code with main-thread code, I recommend using comlink-loader. For example, if you have a main.ts file and a thingy.worker.ts file, you could seamlessly load it as a worker by using TS imports:
// main.ts
import { getThing } from "./thingy.worker.ts"; // make sure the file name ends with .worker.ts
async function test() {
console.log(`the thingy is: ${await getThing()}`);
}
// thingy.worker.ts
export async function getThing() {
return 3;
}
You'll need to add it to your webpack config like this:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.worker\.(js|ts)$/i,
use: [{
loader: 'comlink-loader',
options: {
singleton: true
}
}]
}
]
}
}
The best part is that your editor's intellisense will work across modules and type safety will not be compromised. More documentation is available here.
You need to compile a separate js file for your WebWorker. There is a React lib called react-webworker that makes this easier for you. It in turn uses WebPack’s worker-plugin.
I need to design a application where i have 1 parent application and under that application I have more 5 more application .
for eg.
Parent
-- child1
-- child2
-- child3
and based on user subscription i have to enable only those applications that user subscribed for.
Each child application has its own UI/UX based on user choice (like theme icons , logo etc.) .
Angular doesn't have the concept of "app" or "application", because a good (better) practice is always to have one app running at a time. To quote from the docs:
While it's possible to bootstrap more than one AngularJS application per page, we don't actively test against this scenario. It's possible that you'll run into problems, especially with complex apps, so caution is advised.
But that doesn't mean you can't customize your app, because Angular provides abstractions through other different ways.
One good way to do it is to model your application into multiple modules and use the handy Depedency Injection pattern. Taking your scenario as an example, you have a Parent module which is the overall governing module to bootstrap the application, and Child1, Child2 and Child3 modules that is injectable into Parent module based on the user configuration. Something like this:
(function() {
//declare the child modules:
angular.module('child1', []);
angular.module('child2', []);
angular.module('child3', []);
//User Settings, set by server configuartion .
var userSettings = {
"userId": 1,
"modules": ['child1', 'child3']
};
//bootstrapping the Parent module, where only child1 and child3 module is injected
angular.module('Parent',[userSettings.modules])
})();
For the above method, you would need a really good architectural structure to keep track of what is injected and what is not, so that your development work can be at ease and you don't keep running into $injector:unpr error.
Another good way to do your configuration settings is at, well.. at the config phase. At config phase - where you inject all the providers, you can have literally all the free will to "configure" your app. The only hard part is you will have to write your modules and providers in such a way it is configurable. A lot of 3rd party Angular modules provides such flexibility. UI Bootstrap is one of them. Say for example, I let the user have the flexibility to either "show weeks" in ui.bootstrap's calendar,or to hide it. I could have written my code as below:
//User Settings, set by server configuartion .
var userSettings = {
"userId": 1,
"modules": ['child1', 'child3'],
"showWeeks": false
};
angular.module('Parent')
.config(['datepickerConfig', function(datepickerConfig) {
datepickerConfig.showWeeks = userSettings.showWeeks;
}]);
Oh, you mentioned about UI/UX changes?
For me, I would really want to do that in CSS and not at angular though, unless really necessary. Use CSS preprocessors, either SASS or LESS is good, where by you can write your CSS programitcally. You can write a customizable .scss or .less file, and then simply call #import and you are good to go. Of course, you can combine both the power of preproccesor and Angular's config phase. Angular Material for instance, let's you choose the pallete color via their provider functions:
angular.module('myApp', ['ngMaterial'])
.config(function($mdThemingProvider) {
$mdThemingProvider.theme('default')
.primaryPalette('pink') //choose the primary color as pink!
.accentPalette('orange'); //choose the accent color to be orange!
});
As you can see, it really boils down on how you want to architect your code base, and to what extent you wanna give the flexibility to let the user customize the app. Don't forget about server side configuration too (user credentials, DB connection strings, etc)! There is no right or wrong answers here, just which one you are more comfortable :)
I am trying to data-bind a app/Models/mymodel.js in app/widgets/mywidget/widget.xml
<Collection src="mymodel" instance="true" id="aModel" />
I get the following error:
[ERROR] : Script Error Couldn't find module: alloy/widgets/mywidget/models/mymodel for architecture: x86_64
Not specifying WPATH in widget/ctrl.js and widget/style.tss resulting in Alloy.create* methods pick up from app/ controller or models.
Is there a way to specify to use app/Model in widget/xml
Widgets are independent. They're supposed to be shared across apps. So having a dependency on the app, or a specific model, is not the way it is supposed to be and it is designed so it won't work for this reason.
If you've written the widget yourself specifically for this app remove it, and move code to a separate controller.
If you want to share the widget across apps and want to use a collection inside it, make an exported function and provide the collection to it.
In your widget create a model file with a generic name. Include that collection inside your widget.xml. Then in your widget.js create a method to import the collection
exports.setCollection = function(collection){
$.myCollection.reset(collection.models);
}
Then in your controller including the widget:
$.myWidget.setCollection($.myOtherCollection);
This will set all models of the imported collection to the widget collection. Have an ID attribute that doesn't match? Do some converting in the setCollection method so ID does match. That way it is reusable across apps.
For example, your ID attribute is ObjectId, then you this:
exports.setCollection = function(collection, IdAttribute){
_.each(collection, function(model){
model.set({id: model.get(IdAttribute)}, {silent: true});
});
$.myCollection.reset(collection.models);
}
Then in your controller including the widget:
$.myWidget.setCollection($.myOtherCollection,'ObjectId');
Then you've transformed your collection and all should work
I understand that requirejs and browserify can load my files dependent on its current context, and that it is amazing. I would really prefer to use the #section sections syntax that the razor engine uses. Was just wondering if there is a way to implement this into a typescript / angularjs application.
for example
index.html
#renderSection scripts;
// which could turn into something like
<script data-render="scripts"></scripts>
// the app.run() could declare all the scripts that will be needed on every
// page view
view.html
<script ng-section-repeat="injected in injection"></script>
// the ng-section-repeat is basically taking all the items in the
// typescript constructor and then finding out which ones are needed for
// that view.
I like the idea injecting application file dependencies in the view , without a configuration file and all the added extras that comes with the loaders.
I just want to easily define what files are needed in the actual view and get them loaded, with angular's dependency injection handling the dependency itself.
If you are handling all your dependencies with $inject then , as far as i can tell, dependency is technically already setup in the controllers, all one would need, is to load this as it is called. Which could even eliminate the need for the #section scripts completely
Update:
What i have done to sort of replicate the module loaders is to just use gulp-concat and define the file order in my gulp.config.js and then pass it to the gulp-src before running $.concat .this allows me to have the files in the gulp steam , in dependent order . They are however loaded on the first load. With gulp-uglify the files are tiny ( its now at 566Kb with 16 external libraries loading in 69ms . To put that into perspective it takes 209ms to load one google font ).
I dont know maybe i am not understanding browserify correctly but i honestly struggle to see the need for it, its seems extremely convoluted for something so simple
It is possible using external modules and an injector to do what you asked for:
I just want to easily define what files are needed in the actual view
import {UserFactory} from 'models/userFactory';
import {UserValidator} from 'models/userValidator';
import {Inject} from 'angular2/di';
and get them loaded, with angular's dependency injection handling the dependency itself.
Note: My example uses angular 2.x because I less familiar with angular 1.x and I'm sure you can do something really similar...
class SomeComponent {
userName: string;
userRating: number;
rating: number;
constructor(
#Inject(UserFactory) UserFactory
#Inject(UserValidator) UserValidator
)
{
this.UserFactory = UserFactory;
this.UserValidator = UserValidator;
}
}
Then you can use Browserify to create a bundle.js file that can be executed in a web browser.
I have a singleton that creates classes for me. Each class is in a separate file.
// singleton.js
angular.module('mymodule')
.service('singleton', SingletonClass)
// someclass1.js, someclass2.js, ...
// Multiple files with same layout. Each web page has different # of these files.
// Therefore, it's hard to inject them statically
// (ie, via aggregator service/factory).
angular.module('mymodule')
.??('someClass', function(singleton) {
classOptions = {}; // details here
// creates a class with options and adds it to `singleton`s cache
singleton.addClass(classOptions);
})
// yet another file
angular.module('mymodule')
.controller('myController', function(singleton) {
singleton.getClasses(); // gets all classes added by #addClass method
});
Basically, I want each individual "class" to be self-contained in its own file similar to how modules are self-contained. That means I don't want want singleton or any aggregator service/factory to know of the existence of these classes in its creation.
Edit:
I'd like to clarify: I'm using singleton instead of individually injecting the created classes because I don't yet know which classes will be created yet. The web page will dynamically load certain js files. Each file will inject the singleton object and use the addClass method to create a new class and store it inside the singleton object itself. The controllers using singleton.getClasses() won't know how many or which classes it's getting, but they have a uniform interface which allows it to use them in a predictable fashion.
You could try setting up providers for each class (per my comment above), though that seems . . . time consuming. Maybe you just don't need it? Make each class a separate file in a closure (like you use to need to do with jquery), and grab the singleton object from the injector:
// file 1 - Add other files for other classes.
;(function(window, $) {
$(function() {
var classOptions = {};
$('body').injector().get('singleton').addClass(classOptions);
});
})(window, jQuery);
This is not exactly the angular way, and angular purists will call for your head, but I think it will achieve what you're after if you don't want to mess with providers.