noflo: Dynamic component loading - Component [name] not available with base [path] - javascript

I am getting the error:
Component [name] not available with base [path]
when trying to dynamically attach components to a network's ComponentLoader instance.
var components = []; // This is populated with noflo.Component instances at runtime.
var graph = {
properties: { name: 'Test components' },
processes: {
log: { component: 'log' },
split: { component: 'split' }
},
connections: [
{
data: 'John Doe',
tgt: { process: 'split', port: 'in' }
},
{
src: { process: 'split', port: 'left' },
tgt: { process: 'log', port: 'in' }
},
{
src: { process: 'split', port: 'right' },
tgt: { process: 'log', port: 'in' }
}
]
};
noflo.graph.loadJSON(graph, function(g) {
noflo.createNetwork(g, function(n) {
var getComponent = function(c) {
return c;
}
for (var i = 0; i < components.length; i++) {
var c = components[i];
n.loader.components[c.key] = {};
n.loader.components[c.key].getComponent = getComponent.bind(null, c);
};
});
});
I have also tried assigning the component directly to the property for the components collection in the loader:
n.loader.components[c.key] = c;

You can utilize the NoFlo ComponentLoader's registerComponent method for this. The trick is to start the NoFlo Network in delayed mode by passing it true as the third parameter. This way it won't start execution immediately, and you can first register your components to it.
// Create NoFlo network in delayed mode (see the third param in the end)
noflo.createNetwork(g, function(n) {
// Register the custom components
components.forEach (function (component) {
n.loader.registerComponent('myproject', component.key, {
getComponent: function () { return component; }
});
});
// Once the components have been registered we can wire up and start it
n.connect(function () {
n.start();
});
}, true);
The other option would be to register a custom ComponentLoader in your project. That way you wouldn't need to do anything special when starting networks.
See for example how this is done in noflo-polymer (manifest, custom loader) on browser or MicroFlo (manifest, custom loader) on Node.js.
Custom component loaders have been supported since NoFlo 0.5.1.

Related

Xstate machine - Load feature flags

What we need to do: We need to feature flag a few things in our current state machine
My ideal solution: Always load, no matter what state is, all feature flags and assign them to the state machine context
Attempts: Tried using async actions and invoke services, however, I cannot find a way to always run either of them
This basically my state machine and how I envisioned loading feature flag. However, the invoke.src function just gets called for the first time when I'm first loading the state machine.
Every time that I hydrate the state machine and the state machine is in one of the state, for example create, the invoke.src function does not get called therefore no FF is loaded into the context
const stateMachine = createStateMachine({
id: 'state-machine',
invoke: {
src: async () => {
return await featureFlagService.load();
},
onDone: {
actions: assign(_context, event) => ({ featureFlagEvaluations: event.data }),
}
},
states: {
'create': { ... },
'retrieve': { ... },
}
});
Does anyone have any idea of how to implement such use case?
You should use the actor model approach. Each time when you need to refresh/fetch the FF you should spawn FF-machine and on done call parentSend() message which will update the context to your main SM(state-machine)
const stateMachine = createStateMachine({
id: 'state-machine',
invoke: {
src: async () => {
return await featureFlagService.load();
},
onDone: [{
actions: assign({
ffActorRef: () => spawn(featureFlagMachine, 'ffActor'),
}),
}],
states: {
'create': { ... },
'retrieve': { ... },
},
on:{
REFRESH_FEATURE_FLAG : [{
actions: assign(_context, event) => ({ featureFlagEvaluations: event.data }),
}]
}
});
const featureFlagMachine = createStateMachine({
id: 'ff-machine',
initial: 'retrieve',
invoke: {
src: async () => {
return await featureFlagService.load();
},
onDone: [{
actions: ['notifyRefresh']
}],
states: {
'create': { ... },
'retrieve': { ... },
},
},
{
actions: {
notifyRefresh: sendParent((ctx, event) => {
return {
type: 'REFRESH_FEATURE_FLAG',
data: { featureFlagEvaluations: event.data },
};
}),
},
}
}
);

Using XState, how can I access name of current state in an action?

I'm playing around learning XState and wanted to include an action in a machine that would just log the current state to console.
Defining a simple example machine like so, how would I go about this? Also note the questions in the comments in the code.
import { createMachine, interpret } from "xstate"
const sm = createMachine({
initial: 'foo',
states: {
foo: {
entry: 'logState', // Can I only reference an action by string?
// Or can I add arguments here somehow?
on: {
TOGGLE: {target: 'bar'}
}
},
bar: {
entry: 'logState',
on: {
TOGGLE: {target: 'foo'}
}
}
}
},
{
actions: {
logState(/* What arguments can go here? */) => {
// What do I do here?
}
}
});
I know that actions are called with context and event as arguments but I don't see a way to get the current state from either of those. Am I missing something here?
For a simple use case like yours, you could try recording the state on transition.
let currentState;
const service = interpret(machine).onTransition(state => {
if (state.value != currentState) {
// TODO: terminate timer if any and start a new one
currentState = state.value;
}
});
Then use the value in your actions.
See more here: https://github.com/statelyai/xstate/discussions/1294
Actions receive three arguments - context, event and meta. meta have property state, which is current state.
import { createMachine } from "xstate";
let metaDemo = createMachine(
{
id: "meta-demo",
initial: "ping",
states: {
ping: {
entry: ["logStateValues"],
after: { TIMEOUT: "pong" },
},
pong: {
entry: ["logStateValues"],
after: { TIMEOUT: "ping" },
},
},
},
{
delays: {
TIMEOUT: 3000,
},
actions: {
logStateValues(ctx, event, meta) {
if (meta.state.matches("ping")) {
console.log("It's PING!");
} else if (meta.state.matches("pong")) {
console.log("And now it's PONG");
} else {
console.log(
`This is not supposed to happen. State is: ${meta.state
.toStrings()
.join(".")}`
);
}
},
},
}
);

I failed to call component's method inside of vue-froala-wysiwyg event

in laravel 8/2.6/bootstrap 4.6/Axios app using vue-froala-wysiwyg I need to get content of entered text and
call component's method with entered text.
I define event as I read here https://github.com/froala/vue-froala-wysiwyg :
import VueFroala from 'vue-froala-wysiwyg' // https://froala.com/wysiwyg-editor/docs
Vue.use(VueFroala) // https://github.com/froala/vue-froala-wysiwyg
Vue.config.productionTip = false
// let self = this
export default {
name: "RoomChat",
components: {},
data() {
return {
currentRoom: null,
...
froalaEditorConfig: {
events: {
initialized: function () {
console.log('froalaEditorConfig initialized')
},
'contentChanged': function () { // https://froala.com/wysiwyg-editor/docs/events/
console.log('froalaEditorConfig contentChanged this::')
// this - is reference to froala component
console.log(this);
console.log('this.el::')
// I tryied to get ref to root component in several ways
console.log(this.el) // undefined
console.log('this.el.$parent::')
console.log(this.el.$parent) // undefined
console.log('this.$parent::')
console.log(this.$parent) // undefined
console.log('froalaEditorConfig contentChanged this.html.get()::')
console.log(this.html.get());
parent.actionUser(this.html.get()) // I got parent.actionUser is not a function error
},
},
},
...
methods: {
actionUser(enteredText) {
...
Which way is valid?
ADDED :
Searching for decision I found
https://medium.com/dataseries/vue-js-components-parent-child-and-root-f1fcbe422feb
article with this.$root described but making inside of my event :
console.log('this.$root::')
console.log(this.$root)
Undefined is outputted.
What I see inside of event outputting this : https://prnt.sc/1r6pp4y and https://prnt.sc/1r6ptoh
Thanks!
I found decision with sending vueComponent instance as parameter :
data:(vm) => ({
...
froalaEditorConfig: {
events: {
initialized: function () {
},
contentChanged () {
vm.actionUser(this.html.get()) // That works ok
},
},
....
},
}), // data(vm) => ({

NGXS - Testing Lazy Selectors failure

we are using ngxs and we do have some lazy selectors defined in separated files from the state definition
export class SectionSelectors {
#Selector([CatalogState])
static ById(state: CatalogModel) {
return function getSectionById(id: number): Section {
const selectedSection: Section = state.sections[id];
return selectedSection;
};
}
}
And we have test cases like
import { TestBed } from '#angular/core/testing';
import { Section } from '#miq-catalog/catalog';
import { NgxsModule, Store } from '#ngxs/store';
import { CatalogModel, CatalogState } from './catalog.state';
import { SectionSelectors } from './section.selectors';
describe('SectionSelectors', () => {
it('should select the section by id', () => {
const one: Section = { sectionId: 1, title: '', columns: [] };
const two: Section = { sectionId: 2, title: '', columns: [] };
const state: CatalogModel = {
catalog: [],
sections: { 1: one, 2: two },
columns: {},
catalogLoaded: true,
};
const selectionFunction = SectionSelectors.ById(state);
const result = selectionFunction(1);
expect(result).toBeDefined();
expect(result).toBe(one);
expect(result.sectionId).toBe(1);
const result2 = selectionFunction(2);
expect(result2).toBeDefined();
expect(result2).toBe(two);
expect(result2.sectionId).toBe(2);
});
});
We are passing the state to the selector however we are getting the next error
An error was thrown in afterAll
Uncaught ReferenceError: Cannot access 'CatalogState' before initialization
ReferenceError: Cannot access 'CatalogState' before initialization
I noticed that if I move these selector to the CatalogState (where the #State definition is) the problem is fixed. But this is forcing us to put all selectors there and we think it's good to have them scoped on their own related files so we don't "pollute" with mixed selectors.
Is there a way we can fix the test case? Does someone already faced this Lazy Selector testing before?
As complementary info this is how our State looks like
#State({
name: 'Catalog',
defaults: {
catalogLoaded: false,
columns: {},
sections: {},
catalog: [],
},
})
export class CatalogState {
constructor(private store: Store) {}
#Action(RetrieveCatalogInfo)
#Action(ChangeColumnConfig)
#Action(ClearCatalog)
public executeAction(ctx: StateContext<CatalogModel>, params: ExecutableAction<CatalogModel>) {
return params.execute({ ctx, store: this.store });
}
}
This should not be a problem with the latest version of NGXS (since v3.6.1).

How to avoid [Vue warn] for custom directive?

I created a custom directive and it's working good, but when I run the mocha test for the component where I use this custom directive I receive this warning message [Vue warn]: Failed to resolve directive: scroll-text, tell me please how to fix that
test file:
import { shallowMount } from "#vue/test-utils"
import { scrollText } from "z-common/services"
import ZSourcesList from "./ZSourcesList"
Vue.use(scrollText)
const stubs = [
"z-text-field",
"v-progress-circular",
"v-icon",
"z-btn"
]
describe("ZSourcesList.vue", () => {
const sources = []
for (let i = 0; i < 20; i++) {
sources.push({
field: "source",
// format numbers to get 2 diggit number with leading zero 1 -> 01
value: `cluster-${i.toLocaleString('en-US', { minimumIntegerDigits: 2, useGrouping: false })}`,
__typename: "SuggestV2Result"
})
}
it("displays 'No matching sources found' if there are no sources", () => {
const wrapper = shallowMount(ZSourcesList, {
mocks: {
$apollo: {
queries: {
suggestions: {
loading: false,
},
},
},
},
stubs,
sync: false,
data() {
return {
suggestions: [],
}
},
})
expect(wrapper.find(".notification .z-note")).to.exist
})
})
Try registering your custom directive on a local vue instance and then mounting to that local vue instance.
import { shallowMount, createLocalVue } from "#vue/test-utils"
import { scrollText } from "z-common/services"
import ZSourcesList from "./ZSourcesList"
const localVue = createLocalVue()
localVue.use(scrollText) // Register the plugin to local vue
const stubs = [
"z-text-field",
"v-progress-circular",
"v-icon",
"z-btn"
]
describe("ZSourcesList.vue", () => {
const sources = []
for (let i = 0; i < 20; i++) {
sources.push({
field: "source",
// format numbers to get 2 diggit number with leading zero 1 -> 01
value: `cluster-${i.toLocaleString('en-US', { minimumIntegerDigits: 2, useGrouping: false })}`,
__typename: "SuggestV2Result"
})
}
it("displays 'No matching sources found' if there are no sources", () => {
const wrapper = shallowMount(ZSourcesList, {
mocks: {
$apollo: {
queries: {
suggestions: {
loading: false,
},
},
},
},
localVue, // Mount this component to localVue
stubs,
sync: false,
data() {
return {
suggestions: [],
}
},
})
expect(wrapper.find(".notification .z-note")).to.exist
})
})
Using a local vue instance instead of the global in test cases will also prevent polluting the global vue instance and help to prevent side effects in other test cases.

Categories