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.
Related
I'm working on a Vue/Vuetify app that has a performance problem. I've created a custom component that wraps around a standard Vuetify v-data-table component. It works fine for small amounts of data, but giving it moderate to large amounts of data causes Firefox to hang and Chrome to crash.
Here's a slightly simplified version of my code:
<script>
import ...
export default {
props: {
theValues: Array,
// other props
},
computed: {
theKeys: function() {
return this.schema.map(col => col.col);
},
schema: function() {
return this.theValues[0].schema;
},
dataForDataTable: function() {
console.time('test');
let result = [];
for (let i = 0; i < theValues[0].data.length; i++) {
let resultObj = {};
for (let j = 0; j < theKeys.length; j++) {
// The "real" logic; this causes the browser to hang/crash
// resultObj[theKeys[j]] = theValues[0].data[i][j];
// Test operation to diagnose the problem
resultObj[theKeys[j]] = Math.floor(Math.random() * Math.floor(99999));
}
result.push(resultObj);
}
console.timeEnd('test');
// For ~30k rows, timer reports that:
// Real values can take over 250,000 ms
// Randomly generated fake values take only 7 ms
return result;
},
// other computed
},
// other Vue stuff
</script>
And here's an example of what theValues actually looks like:
[
{
data: [
[25389, 24890, 49021, ...] <-- 30,000 elements
],
schema: [
{
col: "id_number",
type: "integer"
}
]
}
]
The only meaningful difference I see between the fast code and the slow code is that the slow code accesses the prop theValues on each iteration whereas the fast code doesn't touch any complicated part of Vue. (It does use theKeys, but the performance doesn't change even if I create a local deep copy of theKeys inside the function.)
Based on this, it seems like the problem is not that the data table component can't handle the amount of data I'm sending, or that the nested loops are inherently too inefficient. My best guess is that reading from the prop so much is somehow slowing Vue itself down, but I'm not 100% sure of that.
But I do ultimately need to get the information from the prop into the table. What can I do to make this load at a reasonable speed?
The performance problem is actually a symptom of your loop code rather than Vue. The most expensive data access is in your inner loop in dataForDataTable():
for (i...) {
for (j...) {
theValues[0].data[i][j] // ~50 ms average (expensive)
}
}
// => long hang for 32K items
An optimization would be to cache the array outside your loop, which dramatically improves the loop execution time and resolves the hang:
const myData = theValues[0].data
for (i...) {
for (j...) {
myData[i][j] // ~0.00145 ms average
}
}
// => ~39 ms for 32K items
demo 1
Note the same result can be computed without loops, using JavaScript APIs. This affords readability and reduced lines of code at a slight performance cost (~1ms). Specifically, use Array.prototype.map to map each value from data to an object property, obtained by Array.prototype.reduce on theKeys:
theValues[0].data
.map(values => theKeys.reduce((obj,key,i) => {
obj[key] = values[i]
return obj
}, {}))
// => ~40 ms for 32K items
demo 2
Times above measured on 2016 MacBook Pro - 2.7GHz i7, Chrome 87. Codesandbox demos might show a vast variance from the above.
Tip 1
Original text:
Accessing the prop (data) should not be an issue. Yes, data are reactive but reading it should be very efficient (Vue is just "making notes" that you are using that data for rendering)
Well it seems I was clearly wrong here...
Your component is getting data by prop but it is very probable that the data is reactive in parent component (coming from Vuex or stored in parent's data). Problem with Vue 2 reactivity system is it is based on Object.defineProperty and this system does not allow to intercept indexed array access (Vue is not able to detect code like arr[1] as an template dependency). To workaround this, if object property (theValues[0].data in your code) is accessed, it checks whether the value is array and if yes it iterates the whole array (plus all nested arrays) to mark the items as dependencies - you can read more in depth explanation here
One solution to this problem is to create local variable let data = theValues[0].data as tony19 suggests. Now the .data Vue getter is not called every time and the performance is fixed...
But if your data is immutable (never change), just use Object.freeze() and Vue will not try to detect changes of such data. This will not only make your code faster but also saves a ton of memory in case of large lists of objects
Note that this problem is fixed in Vue 3 as it uses very different reactivity system based on ES6 proxies...
Tip 2
Although Vue computed properties are highly optimized, there is still some code running every time you access the property (checking whether underlying data is dirty and computed prop needs reevaluate) and this work adds up if you use it in a tight loop as in your case...
Try to make local copy of the theKeys computed prop before executing the loop (shallow copy is enough, no need for a deep copy)
See this really good video from Vue core member
Of course the same issue applies to accessing the dataForDataTable computed prop from the template. I encourage You to try to use watcher instead of computed to implement same logic as dataForDataTable and store it's result in data to see if it makes any difference...
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).
I have a small, home-brewed implementation of the Flux pattern that I'm working with, just to get a better understanding of the pattern. Its working quite well, and I'm learning a ton! But I've run into an issue I can't wrap my head around at the moment. Apologies in advance if this has an obvious solution.
Imagine my simple Store called ExampleStore, and my simple Component ExampleComponent. In it there are:
_exampleState
getExampleState()
setExampleState()
in ExampleComponent, to stay updated:
_onChange: function() {
setState({exampleState: ExampleStore.getExampleState()})
}
in ExampleStore, after a dispatched action I use the setter:
setExampleState(newStateFromDispatchedAction);
This works perfectly. The data is flowing as it should. But I have a question, because it seems easy to break the pattern because there is no privacy enforced on my _exampleState within my Store. Since I have a getter and private setter method for _exampleState, it seems natural to me that somehow I want to enforce more privacy on the variable. Is there a nifty way to do this, that I am overlooking?
As it is now, if, for example, inside ExampleComponent I do (which I wouldn't, but I could):
this.state.exampleState = {field:'example'}
//doing this
this.state.exampleState.field = 'example2';
//changes the store _exampleState to {field:'example2'}
I have changed the value of _exampleState within ExampleStore directly, without making use of the setter. This seems dangerous (and makes me question why I'd have a private setter/public getter to begin with). This question comes after dealing with a pesky bug where a library I was using modified the state variable directly, and thereby within the Store.
Is there some good way I'm overlooking to enforce privacy on the state variables in my Store, so that they may not be changed directly through their references in ExampleComponent? Sorry if this is a dumb question and I'm overlooking something simple, thanks for the help!
Be aware that one of the basic principles of the Flux philosophy is that stores should have no (public) setters. That means you should not be able to modify the store's state if not inside the store itself.
One way of enforcing the privacy could be by keeping state variables as private, only letting the store managing them.
EDIT: to "enforce" privacy, you could also return a deep copy of your state, as it is shown in the code.
The following code, based on the official flux GitHub repository's flux-todomvc example, highlights the idea:
var AppDispatcher = require('../dispatcher/AppDispatcher');
var AppConstants = require('../constants/AppConstants');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
// This is a private state variable that can only be accessed in this file
var _exampleState = {/*...*/};
var ExampleStore = assign({}, EventEmitter.prototype, {
EXAMPLE_STATE_CHANGED: 'EXAMPLE_STATE_CHANGED',
// return a deep copy of your state so there is no way
// to modify the store's state by reference
getExampleState: function() {
return deepCopy(_exampleState);
}
/*...*/
};
// this is a private method (setter)
var _setExampleState = function(newExampleState) {
_exampleState = newExampleState;
};
ExampleStore.dispatchToken = AppDispatcher.register(function(action) {
switch(action.actionType) {
case AppConstants.CHANGE_EXAMPLE_STATE:
_setExampleState(action.newExampleState);
ExampleStore.emit(ExampleStore.EXAMPLE_STATE_CHANGED);
break;
}
});
// the implementation of deepCopy is a developer's choice
// this version of it is very inefficient
var deepCopy = function(obj) {
return JSON.parse(JSON.stringify(obj));
}
module.exports = ExampleStore;
Facebook official examples are a good way to understand how to implement the core Flux concepts.
EDIT: this is a way of "enforcing" privacy of a state variable, but it is discouraged due to the clear loss of efficiency. I guess that the main idea here is that, even though you are able to do so in some situations, changing the store's state through reference is just against Flux. It is important to notice that this enforcement is not a reality in many big libraries. In React, for instance, it is possible to modify the state of a component directly, even though that is completely not recommended.
you can wrap your store in a closure, and provide getters and setters, to prevent accidental modification of your state.
https://facebook.github.io/immutable-js (or Mori or seamless-immutable) provides the means to prevent modifications to nested data, while avoiding the need to make defensive deep clones in your getExampleState method. However, it has a huge impact on your coding style and code base. It possibly works best with a functional coding style, as is encouraged by some flux implementations, like https://github.com/rackt/redux.
Another option is to make it clearer that you don't want the state to be modified by ensuring that non-library code only sees the store state in React 'props', rather than React 'state' - not modifying props should be second nature to a React developer anyway (other bugs will occur if they modify it). This can be done using a generic flux wrapper component, such as Facebook's 'Container' - https://facebook.github.io/flux/docs/flux-utils.html
I'm currently working on an app whose database schema changes frequently. This rapid change creates a big problem for my front-end Angular code which consumes the backend JSON API (which I don't have much control over) via Restangular; take the following code for example:
<ul>
<li ng-repeat="item in items">
<h2>{{item.label}}</h2>
</li>
</ul>
There will be a lot of template tags like {{item.label}} scattered everywhere in the front-end code, so whenever the property name changes from, say "label" to "item_label", I'll need to remember where those tags are and change all of them. Of course, I could do a project wide search and replace, but that's not really ideal from an DRY stand point and it'll also be a maintenance nightmare.
My question is, does Angular (or Restangular) provide a way to map model property names to custom ones like this in Backbone?
That way, I can just have something like this
{
label: model.item_label
}
then next time when the "item_label" is changed to something else, I can just update it in this configuration object and not worry about all the references in the templates.
Thanks.
The idea with angular is that you can do whatever you want with the model. While this doesn't point you in any specific direction it does give you the opportunity to implement it in your own OO manner. Say you have an app that has a data object called ...Task a model for tasks might look like..
function Task(initJson){
this.name = initJson._name || 'New Task';
this.completed = initJson.is_completed || false;
this.doneDateTime = initJson.datetime || null;
}
Task.prototype = {
save: function(){
//do stuff with this and $http.put/post...
}
create: function(){
//do stuff with this and $http.put/post
}
//....etc
}
All of this might be wrapped up in a factory.
myApp.factory('TaskFactory',function($http){
var Tasks = []; //Or {};
//above constructor function...
//other helper methods...etc
return {
instance: Task,
collection: Tasks,
init: function(){} // get all tasks? run them through the constructor (Task), populate collection
};
})
You could then edit properties on your constructor (one place (for each data type), the only place). Although this isn't ideal if your using things like Restangular or $resource as they not equipped to be a large backing store but they just assume the properties that come across the wire, which for large, changing applications can sometimes be difficult to manage.
I ended up going with Restangular's setResponseExtractor config property based on this FAQ answer.
It looks like this:
Restangular.setResponseExtractor(function(response, operation, what, url) {
var newResponse = response;
angular.forEach(newResponse.items, function(item) {
item.label = item.item_label;
}
return newResponse;
}
When a GUI is composed of several subcomponents that I treat as individual Views with their own Presenter and Models, is there a pattern for gluing them together? Some subcomponents are persistently on the screen while others would get swapped in and out.
What would be a good factory pattern for instantiating the respective MVP triad for a subcomponent that gets added to the GUI at runtime?
How do you glue the subcomponents with the persistent "container" part of the GUI and with each other? Would there be a "God Presenter" that ties other presenters together?
Update: I'm now shooting for something similar to Eclipse's extension mechanism. Plug-ins register themselves to a global registry for the functionality that they provide. When a functionality such as returning data or rendering a View is needed, the registry is queried and the returned functions are invoked (this is pure JavaScript, so I'm not using interfaces). I'm going with a pure-plug-in approach where everything (even the main View) is a plug-in. I might also use an Event Bus to let the various Presenters communicate agnostically.
Now my more specific question becomes, when a plug-in is about to contribute a View, how should I go about initializing the MVP triad and getting the View rendered into a parent container (outside the module). I probably need to let the View render itself into a container passed from outside and inject the View and Model (if needed) into the Presenter. An alternative would be for the View to return a component that can be placed inside a container, but this would be against my tentative ideal of keeping everything that is GUI-framework-specific inside View implementations. I prefer if the factory/glue mechanism can be framework-agnostic.
OK I'll stop yammering now and wait for some feedback, and then perhaps add more clarifications on where exactly I'm stuck...
I think the design pattern you're about is mediator.
I've written a javascript framework that consisted of a mediator.
It works like this:
You create a global instance of the
mediator,
register objects under
certain names,
use the mediator in your implementation to call methods
from registered objects within any of
the objects.
If something isn't present - no errors fly around.
If there are multiple instances - they all get the call.
This is the basic code for that:
(An extract of my code. I will make a jquery plugin including that in a while. If you're willing to use it push me to do it faster ;) )
function Mediator(){
function log(a){
try {console.log(a);} catch(e){
try {opera.postError(a);} catch(e){
//alert(a);
}
}
}
var __reg={}; // { "what": [object, ...], ... } //registers an object
//what=key that will identify, obj=an object
this._register = function(what,obj){
if(__reg[what]===undefined){
__reg[what]=[];
}
__reg[what].push(obj);
} //unregisters multiple objects and deletes a key
this._unregisterAll = function(what){
if(__reg[what]===undefined){log('Overlord:_unregisterAll - no registers'); return false; }
__reg[what]=null;
return true;
}
//unregisters a single element key
this._unregister = function(what){
if(this._countRegisters()==1){
__reg[what]=null;
return true;
} else { log('Overlord:_unregister - no registers'); return false; }
}
//unregisters last added element
this._unregisterLast = function(what){
var cnt=this._countRegisters(what);
if(cnt==0) { log('Overlord:_unregisterLast - no registers'); return false; }
if(cnt==1) {
__reg[what]=null;
return true;
} else {
__reg[what][cnt-1]=null;
return true;
}
}
//returns number of registered items
this._countRegisters = function(what){
try{
return __reg[what].length;
} catch(e){log(e);
return 0;
}
} //calls a method from all objects registered under 'what' with an array of parameters. returns true if there was at least one successful call
this._call = function(what,method,params){
var occured=false;
for(var i in __reg[what]) {
try {
__reg[what][i][method](params);
occured=true;
} catch(e) {log(e);//auto reakcja
}
}
return occured;
}
//does the call, but also returns an array of values retuurned by function
this._returnAll = function(what,method,params){
var re=[];
for(var i in __reg[what]){
try {
re.push(__reg[what][i][method](params));
} catch(e) {log(e);//auto reakcja
}
}
return re;
}
//runs a method from first object for a given key
this._returnFirst = function(what,method,params){
try {
return __reg[what][0][method](params);
} catch(e) {log(e);//auto reakcja
return null;
}
}
}
I guess that "keeping the GUI-framework-specific inside View implementations" is an overall application-level design choice, rather than an absolute must (at least when you think to "view implementation" as "plugin view implementation").
You could - for example - have a very thin view layer at plugin level, and implement a super-view layer within the parent that calls the plugins: thinking to a system of plugins that all add a column to a table, you could well have the bulk of the view code at parent level ("table") and have your plugins to just pass little more than raw data: you would avoid to repeat yourself and would make your code more flexible and maintainable.
On the other hand, if your plugins provide very different types of functionality that never interact directly (for example if they are the different subsystems of a flight simulator) you will want to keep everything that is related to views at plugin level, so that the parent object would not have to even know what a given plugin deals with, but just place it's returned value somewhere in the GUI.
Other factors that would probably influence your choice are the language and framework (if any) that you are using: in my personal experience, design patterns tend to be far from language-agnostic, as each language (and framework) has its own strengths / weaknesses which make certain choices obvious and certain others very difficult to implement.
Just my 2¢ to the discussion, anyhow! :)
For now, I'm going with this approach:
An extender (an extension implementation that a plug-in exposes) that is contributing a GUI component has a getTriad (will come up with a better name later) method that instantiates, wires and returns a MVP triad. The View has a getComponent method that renders and returns a framework-specific GUI element container that can be added to a parent container (the framework-specific details of which are encapsulated within the parent Views). I'm not letting the View render itself into a container but instead letting the parent View render the child into itself. I think this is better in terms of ensuring child Views don't directly mess with parent Views.