What I'm trying to do:
Use something similar to the "resolveComponentFactory()", but with a 'string' identifier to get Component Factories.
Once obtained, start leverage the "createComponent(Factory)" method.
Plnkr Example -> enter link description here
In the example, you will see the AddItem method
addItem(componentName:string):void{
let compFactory: ComponentFactory;
switch(componentName){
case "image":
compFactory = this.compFactoryResolver.resolveComponentFactory(PictureBoxWidget);
break;
case "text":
compFactory = this.compFactoryResolver.resolveComponentFactory(TextBoxWidget);
break;
}
//How can I resolve a component based on string
//so I don't need to hard cost list of possible options
this.container.createComponent(compFactory);
}
The "compFactoryResolver:ComponentFactoryResolver" is injected in the contructor.
As you will note, having to hard code every permutation in a switch statement is less than ideal.
when logging the ComponentFactoryResolver to the console, I've observed that it contains a Map with the various factories.
CodegenComponentFactoryResolver {_parent: AppModuleInjector, _factories: Map}
However, this map is a private and can't be easily accessed (from what I can tell).
Is there a better solution then somehow rolling my own class to get access to this factory list?
I've seen a lot of messages about people trying to create dynamic components. However, these are often about creating components at run time. the components in question here are already pre-defined, I am stuck trying to access factories using a string as a key.
Any suggestions or advice are much appreciated.
Thank you kindly.
It's either defining a map of available components,
const compMap = {
text: PictureBoxWidget,
image: TextBoxWidget
};
Or defining identifiers as static class property that will be used to generate a map,
const compMap = [PictureBoxWidget, TextBoxWidget]
.map(widget => [widget.id, widget])
.reduce((widgets, [id, widget]) => Object.assign(widgets, { [id]: widget }), {});
The map is then used like
let compFactory: ComponentFactory;
if (componentName in compMap) {
compFactory = this.compFactoryResolver.resolveComponentFactory(compMap[componentName]);
} else {
throw new Error(`Unknown ${componentName} component`);
}
There's no way how component classes can be magically identified as strings, because they aren't resolved to strings in Angular 2 DI (something that was changed since Angular 1, where all DI units were annotated as strings).
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
The community reviewed whether to reopen this question 10 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I'm kind of new in programming and I'm currently wondering myself what is the benefit of class vs a simple "list" of related functions in NodeJs.
As very simplified example, I guess I would create a user like this if I'd use Class:
class User {
constructor(email) {
this.email = email;
}
validateEmail() {
// whatever function that checks if this.email is valid
if (this.email === 'notValid') {
throw new Error();
}
return this;
}
create() {
this.validateEmail();
// whatever function that inserts user in the database
return user;
}
}
const newUser = new User('test#test.com');
const user = newUser.create();
And I would do something like this with what I call a "list" of related functions :
const validateEmail = email => {
// whatever function that checks if valid email
if (email === 'notValid') {
throw new Error();
}
return true;
};
const createUser = email => {
if (validateEmail(email)) {
// whatever function that inserts the user in the database
return user;
}
};
const user = createUser('test#test.com');
The second way seems to me that it can ends with a little less code. And don't even have to instatiate the class.
Let's say I have a node API, with multiple "User" routes and controllers. I guess I have to instantiate the User Class every time a User routes/controllers is called, right ? As a beginner, this does not sound "optimized" to me... But I'm surely missing something...
Thank for your help
Classes tie data (properties on this, the instance) to functionality (functions on the internal prototype of every instance).
If you don't ever have data that makes sense to be associated with an instance, there's not much point in having a class. Here, you do have data (the email string) and a related function (validateEmail), so a class is a possible option, though it's much better when there are more properties and more functions involved. (If there's only one property, and the property is only there so it can be called with that one function, a class does indeed seem unnecessarily verbose.)
One benefit of such a class is that, once you have an instance, you can pass that instance around to any other module (or scope), and that module can call functions related to the instance, without having to import the equivalent standalone function. For example, imagine if the consumer of your User class was in a different module, and the consumer wants to be able to create users and validate them. Then, the module that exports the bulk of the User code would either have to
(1) Export many standalone functions (which could get a bit tedious and hard to manage), or
(2) Export only a class
Being able to export just a Class and have any users of the class or its instances use class-related methods is quite handy - that way, you don't have to pass around multiple values every time you need to use a User-related function.
Imagine if module A needs to be able to create a user, module B needs to be able to verify a user, and module C needs to be able to check to see if a user has been verified or not yet. If the main User file created and exported standalone functions for each of these things, and each consuming module imported what they needed to call, things would be a bit ugly. But if the main User file created and exported a Class, it would be trivial: the creator of a User (module A) would import the class, instantiate an object const user = new User('a#a.com'), and pass it along to module B, which could then call user.validateEmail() (no imports necessary). Then eventually the user instance gets passed along to module C, which calls user.checkIfUserIsValidated() (again, no imports necessary).
With the Class method, having an object which has both instance data and related functions on the same object (the class instance) can make code a lot cleaner.
Of course, you don't ever have to use a class if you don't want to, but that's one possible benefit.
With the example you have given, you indeed have a point:
a function that validates an email does not really need an instantiated object to act on, and in fact you want to run it before creating the API's object instance.
a function that really returns a different object (user is not an instance of User) without mutating the this object, is also not the best reason for a separate class. If this (email verification) is all you will ever need to do as "extra" compared to the API's object class you already have access to (which returns user), then the second way is fine.
If however, you will have several other functionalities you want to extend that API with, like there could be:
preparing a personalised email for which you would need their name, address, email, gender,...
returning some calculated score based on several properties (age, last login date, access level, ...)
...
...then it might become nicer to use the first pattern -- although still a matter of opinion.
I would suggest to still create your own class, but to let it create the API's object immediately, and make the returned object value a property of your own instance. This new object is then a composition of the API's interface with your own extensions to it.
Maybe like so:
function validateEmail(email) {
// whatever function that checks if an email is valid
if (email === 'notValid') {
throw new Error();
}
}
class User {
constructor(email) {
this.rank = 10; // Let's say you need this property that is not offered by the API
validateEmail(email);
// whatever function that inserts user in the database
this.apiUser = api.createUser(email);
}
promote() { // Some function using the new properties
if (this.rank > 1) this.rank--;
}
prepareEmail() { // Other function using a mix
return {
sentBy: "admin#mysite.com",
to: this.apiUser.get("email"),
body: `Dear ${this.apiUser.get("name")}, your current rank is ${this.rank}`
};
}
}
const user = new User('test#test.com');
So, here you see that the main code creates one object instance. The API's object instance is encapsulated inside it. You could either allow the caller to work directly with the API, using user.apiUser.someApiMember syntax, or you could create your own wrapper functions in your own class, to be accessed as user.myApiWrapperMember.
I'm a fan of Vue which a try to use on some occasions. Anyway, there is something I always found not so handy with it: reactivity lies within $data. Well not always, as external data can be tracked by Vue, as in computed properties, in templates… But I found this way uncomfortable and not always consistent (see another question about it, here Reactivity on Variables Not Associated With Data, Computed, etc). So my decision now is use $data as the main source of reactivity and stop trying to find other ways.
However, reactivity within $data poses me a problem in what is a common case for me: many pieces of data here and there in other imported objects. This makes even more sense as I consider Vue as the View end not the business logic. Those imported objects are sometimes complex and within Vue components, I found no way to cherry pick pieces of information and kind of ask Vue to bind to them. The only way was to declare entire objects in the $data section which makes tracking very heavy: loads of setters/getters when only one would be enough in a simple component, for example.
So I designed a class called 'Reactor' whose instances role is to install getter/setters on any piece data of my wish in a complex object (or more than one). Those instances are imported into Vue components and then $watchers of Reactor instances have properties which can contain as many functions as I wish which are called when pieces of data are altered through the Reactor. To make things simple by default is filled with the same property name as the data it bounds to. This precisely those function which will update $data when external data change.
class Reactor {
constructor() {
this.$watchers = {};
}
addProperty(originalObject, keyString, aliasKeyString) {
if(aliasKeyString === undefined) {
aliasKeyString = keyString;
}
if(this[aliasKeyString] !== undefined || originalObject[keyString] === undefined) {
const errorMessage = `Reactor: cannot add property '${aliasKeyString}'!`;
console.error(errorMessage);
throw errorMessage;
}
this.$watchers[aliasKeyString] = [];
Object.defineProperty(this, aliasKeyString, {
set(newValue) {
const oldValue = originalObject[keyString];
originalObject[keyString] = newValue;
this.$watchers[aliasKeyString].forEach((f) => {
if(typeof f === "function") {
f(newValue, oldValue, aliasKeyString);
}
});
},
get() {
return originalObject[keyString];
},
});
}
}
An example can be seen in the codepen here: https://codepen.io/Djee/pen/gyVZMG
So it's sort of an 'inverted' Vue which allows updating $data on external conditions.
This pattern also helped me resolve a case which was rather difficult before: have a double-bind on an input with a filter in-between which will set the input and its attached external value straight upon #change event only. This can be seen in the same codepen given above.
I was a little surprised to have found nothing taking this in charge in Vue itself. Did I miss something obvious? This is mainly the purpose of this somewhat long introduction. I had no time to check whether Vuex would solve this nicely.
Thanks for any comments as well.
I'm using filter function for internationalization like this:
<div>{{ "hello" | message }}<div>
message is a filter function that depends on global Vue.config.lang variable.
It works great, but if I change Vue.config.lang, message doesn't rerender.
I wanted to make message rerender anytime Vue.config.lang changes, so I changed my filter function from
message => locale_messages[Vue.config.lang][message]
to
message => new Vue({
template: '{{ message }}',
computed: {
message() { return locale_messages[Vue.config.lang][message]; }
}
})
But it doesn't work. Getting this error:
Uncaught TypeError: Converting circular structure to JSON
at Object.stringify (<anonymous>)
....
Is there anything I can do to make it work? I'm new to Vue.js and can't find a working solution.
Like Evan says, Filters should be pure, so thay can't use a global variable as key to get values from externals arrays. Because of side effects.
So, there is three solutions at your problem that comes in my mind :
Replace filters by methods.
Use vue-i18-n, a simple and powerful module for translation
Use a store system (vuex) wich provides you getters, and helps you manage a global state.
Personnaly I love to use vuex and vue-i18-n together.
In that way I can centralize my data and the language in use. I can also serve specific data in several languages using the store, and let vue-i18-n cares about all the strings in the project.
New to Vue myself, so not quite sure how global variables work, but you can definitely pass params to a custom filter - even a Vue reference. You can do this:
<!-- this = a reference to the vue instance -->
<span class="display-3">{{value|FormatValue(this)}}</span>
[...]
props: ["aIsPercentNeeded"],
[...]
Vue.filter("FormatValue", function (aValue, aVueInstance)
{
if (!aVueInstance["aIsPercentNeeded"])
{
return aValue;
}
return aValue + "%";
});
I'm trying to figure out an rxjs way of doing the following:
You have two observables, one onAddObs and onRemoveObs.
let's say onAddObs.next() fires a few times, adding "A", "B", "C".
I would like to then get ["A", "B", "C"].
.toArray requires the observable be completed...yet more could come.
That's the first part. The second part is probably obvious...
I want onRemoveObs to then remove from the final resulting array.
I don't have a plunkr cuz I can't get anything close to doing this...
Thanks in advance!
UPDATE
Based on user3743222's advice, I checked out .scan, which did the job!
If anyone else has trouble with this, I've included an angular2 service which shows a nice way of doing this. The trick is to use .scan and instead of streams of what was added/removed, have streams of functions to add/remove, so you can call them from scan and pass the state.
#Injectable()
export class MyService {
public items: Observable<any>;
private operationStream: Subject<any>;
constructor() {
this.operationStream = Subject.create();
this.items = this.operationStream
// This may look strange, but if you don't start with a function, scan will not run....so we seed it with an operation that does nothing.
.startWith(items => items)
// For every operation that comes down the line, invoke it and pass it the state, and get the new state.
.scan((state, operation:Function) => operation(state), [])
.publishReplay(1).refCount();
this.items.subscribe(x => {
console.log('ITEMS CHANGED TO:', x);
})
}
public add(itemToAdd) {
// create a function which takes state as param, returns new state with itemToAdd appended
let fn = items => items.concat(itemToAdd);
this.operationStream.next(fn);
}
public remove(itemToRemove) {
// create a function which takes state as param, returns new array with itemToRemove filtered out
let fn = items => items.filter(item => item !== itemToRemove);
this.operationStream.next(fn);
}
}
You can refer to the SO question here : How to manage state without using Subject or imperative manipulation in a simple RxJS example?. It deals with the same issue as yours, i.e. two streams to perform operations on an object.
One technique among other, is to use the scan operator and a stream of operations which operates on the state kept in the scan but anyways go have a look at the links, it is very formative. This should allow you to make up some code. If that code does not work the way you want, you can come back and ask a question again here with your sample code.
I'm working on my first Ember.js app and am having some trouble connecting all the dots. It would be really helpful if I could just see all the variables available within a given handlebars template.
There is a related question, but you have to know the variable that is in scope to use it:
How do I add console.log() JavaScript logic inside of a Handlebars template?
How can I output all the variables?
a good option is to debug the value of 'this' in a template using the Handlebars helpers:
1.
{{#each}}
{{log this}}
{{/each}}
or,
2. similar to #watson suggested
{{#each}}
{{debugger}}
{{/each}}
and then drill in to the Local Scope Variables for 'this' in the Dev Tools
or alternatively, 3. you could log things directly from inside your Controller init method, such as:
App.UsersController = Ember.ArrayController.extend({
init: function() {
console.log(this);
console.log(this.getProperties('.'));
}
});
Make sure you try out Firebug - you'll get a different perspective on things, which I found helpful. But don't abandon chrome completely; you will need the Ember Inspector at some point.
I'm using the same debugging helper everyone recommends, and this is how Chrome displays it:
When I expand the same object in firebug, I get the following info, including the variables I was looking for (sources[]) and some other useful properties I hadn't seen in Chrome.
I created Barhandles a few years ago. It will use the Handlebars parser to produce the AST, and then extract variable references from it. The extractSchema method will — well — extract a schema. That schema is not based on JSON Schema or Joi or anything. It's a homegrown format that captures most of the things you could possibly extract from Handlebars template.
So, this barhandlers.extractSchema('{{foo.bar}}') produces:
{
"foo": {
"_type": "object",
"_optional": false,
"bar": {
"_type": "any",
"_optional": false
}
}
}
It will take into account that an {{#if expr}} will automatically make nested references optional. It correctly handles scope changes based on {{#with expr}} constructs, and it allows you to add support for your own custom directives as well.
http://nxt.flotsam.nl/barhandles
https://medium.com/east-pole/advanced-barhandles-4a7e64c1bc0d
We used it to do validation on the data structures that we passed into the template, and it was working pretty well for that purpose.
If you really need to dump the variables in your template, you can explore the template AST and output the content of the relevant nodes (see the compiler sources). This is not an easy task because you have to find your way through trials and errors, and the code is quite low-level and there are not so many comments.
It seems Handlerbars doesn't have a shortcut for what you're asking, so the steps would be:
precompile a template (see the command line source, I think the function is called handlebars.precompile())
explore the AST
You can do this by leveraging Handlebars.parseWithoutProcessing which takes the input template string. If you use TypeScript, that returns a specific type hbs.AST.Program. You can filter for only the moustache statements, and then iterate through these statements to get the variable names.
This method also supports Handlebars helpers, so you can get the key for that, but because of this, this function is a bit more complex as you'd need to check different properties on the moustache statement:
/**
* Getting the variables from the Handlebars template.
* Supports helpers too.
* #param input
*/
const getHandlebarsVariables = (input = '') => {
const ast = Handlebars.parseWithoutProcessing(input);
return ast.body
.filter(({ type }) => type === 'MustacheStatement')
.map((statement) => statement.params[0]?.original || statement.path?.original);
};
Here's the TypeScript version, which is a bit involved due to the conditional properties, but can help explain the types a bit more:
/**
* Getting the variables from the Handlebars template.
* Supports helpers too.
* #param input
*/
const getHandlebarsVariables = (input: string): string[] => {
const ast: hbs.AST.Program = Handlebars.parseWithoutProcessing(input);
return ast.body.filter(({ type }: hbs.AST.Statement) => (
type === 'MustacheStatement'
))
.map((statement: hbs.AST.Statement) => {
const moustacheStatement: hbs.AST.MustacheStatement = statement as hbs.AST.MustacheStatement;
const paramsExpressionList = moustacheStatement.params as hbs.AST.PathExpression[];
const pathExpression = moustacheStatement.path as hbs.AST.PathExpression;
return paramsExpressionList[0]?.original || pathExpression.original;
});
};
I've made a Codepen that illustrates this. Essentially, given the following template:
Hello, {{first_name}}! The lottery prize is {{formatCurrency prize_amount}}! Good luck!
It will use window.prompt to ask the user for their name and the prize amount. The example also implements a helper formatCurrency. You can see it here: https://codepen.io/tinacious/pen/GRqYWJE
The sample Ember app you mention defines its EmberObjects right in its app.js. So basically, what's available on the objects are the properties that are defined onto them there. (e.g. subreddit has a title, etc).
If you want a globally available way to dump an object's property schema out to the console, one approach would be to create a "debug" helper that walks the members of the passed-in contexts and writes them out. Something like:
Handlebars.registerHelper('debug', function (emberObject) {
if (emberObject && emberObject.contexts) {
var out = '';
for (var context in emberObject.contexts) {
for (var prop in context) {
out += prop + ": " + context[prop] + "\n"
}
}
if (console && console.log) {
console.log("Debug\n----------------\n" + out);
}
}
});
Then call it on whatever you want to inspect:
<div>Some markup</div>{{debug}}<div>Blah</div>
This will use whatever EmberObject is in scope, so pop it inside of an {{#each}} if you want to inspect the list elements, as opposed to the object with that list.
The variables available within a template are only constrained by the model you are using to render the template.
You should set a breakpoint in your app where you render the template and see what is in your model at that point, which will should you what you have available to put in your template.