Nested objects in javascript how to freeze it? - freeze

I am trying to freeze javascript nested object. As I am new to JS I am facing difficulty to freeze the nested objects.
const obj = { prop: { innerProp: 1 } };
obj.prop.innerProp = 5;
console.log(obj.prop.innerProp); // 5
Is it possible do freeze nested objects?

You can use the following example to freeze nested objects
function deepFreeze (o) {
Object.freeze(o);
if (o === undefined) {
return o;
}
Object.getOwnPropertyNames(o).forEach(function (prop) {
if (o[prop] !== null
&& (typeof o[prop] === "object" || typeof o[prop] === "function")
&& !Object.isFrozen(o[prop])) {
deepFreeze(o[prop]);
}
});
return o;
};

You can use Immutable.js which is a library for working with immutable values. Moreover, in terms of performance, especially if you constantly make use of immutable variables in project, Immutable.js is a great choice, since deepFreeze more costly in terms of performance.

Related

_.defaultsDeep() with Vue.set()

I need to set default values from model to component object in my vue.js application.
I found the perfect solution in lodash defaultsDeep defaultsDeep(this.widget, this.widgetModel), but the values don't get reactive obviously (added props not reactive), so I need something similar to _.defaultsDeep(), but with a callback to vm.$set() OR make all properties of object reactive after set defaults, OR even add defaultsDeepWith function to lodash
I looked to source code of defaultsDeep, but looks like i don't have enough experience to understand that, also i looked to vue-deepset librariy and seems it don't fit to my case (library better fit to stringed properties), also project based on vue.js 2
const defaultsDeepWithSet = (targetObj, sourceObj) => {
for (let prop in sourceObj) {
if (sourceObj.hasOwnProperty(prop)) {
if (!targetObj.hasOwnProperty(prop)) {
this.$set(targetObj, prop, sourceObj[prop])
}
if (isObject(sourceObj[prop])) {
defaultsDeepWithSet(targetObj[prop], sourceObj[prop])
}
}
}
}
defaultsDeepWithSet(this.widget, this.widgetModel)
Obama awards obama a medal meme
I've run into this same issue with _.defaultsDeep() and other similar utils. If this is a prominent issue and you don't want Vue to bleed into every corner of your codebase, then consider fixing reactivity after the fact.
I happen to be using Vue 2 Composition API, so it looks like this:
import { reactive, isReactive, isRaw } from '#vue/composition-api';
const fixReactivity = (obj) => {
// Done?
if (!obj) return obj;
if (typeof obj !== 'object') return obj;
// Force reactive
if (!isReactive(obj)) {
// See: https://github.com/vuejs/composition-api/blob/6247ba3a1e593b297321f68c1b7bb0dee2c3ea1e/src/reactivity/reactive.ts#L43
const canBeReactive = !(!(Object.prototype.toString.call(obj) === '[object Object]' || Array.isArray(obj)) || isRaw(obj) || !Object.isExtensible(obj));
if (canBeReactive) reactive(obj);
}
const isArray = Array.isArray(obj);
for (const key in obj) {
if (!obj.hasOwnProperty(key)) continue;
// Fix the children
const val = obj[key];
fixReactivity(val);
// Fix assignment for objects
if (!isArray) {
const prop = Object.getOwnPropertyDescriptor(obj, key);
const needsFix = ('value' in prop) && prop.writable;
if (needsFix) {
Vue.delete(obj, key);
Vue.set(obj, key, val);
}
}
}
return obj;
};

How to making sure at least one checkbox is checked using vuejs

I would like to guarantee that at least one checkboxes are checked and the price are correct calculated.
https://jsfiddle.net/snoke/1xrzy57u/1/
methods: {
calc: function (item) {
item.isChecked = !item.isChecked
this.total = 0;
for (i = 0; i < this.items.length; i++) {
if(this.items[i].isChecked === true) {
this.total += this.items[i].price;
}
}
// fullPackagePrice
if(this.items[0].isChecked === true && this.items[1].isChecked === true && this.items[2].isChecked === true) {
this.total = this.fullPackagePrice;
}
// Trying to guarantee that have at least one checkbox checked
if(this.items[0].isChecked === false && this.items[1].isChecked === false && this.items[2].isChecked === false) {
this.total = this.items[0].price;
this.items[0].isChecked = true;
}
}
}
A good fit for this would be using computed properties instead of a method.
Read more about these here: https://v2.vuejs.org/v2/guide/computed.html#Computed-Properties
A computed property observes all referenced data and when one piece changes, the function is re-run and re-evaluated.
What you could do is first create a allowCheckout computed property like this:
allowCheckout() {
return this.items[0].isChecked || this.items[1].isChecked || this.items[2].isChecked;
}
You will then use it within the button like this:
<button :disabled="allowCheckout"...
This will disable the button when no items are checked.
Next, you'll also want to create a second computed property for the total price
totalPrice() {
// Perform similar checking here to update this.total
}
Lastly, you'll want to change your checkboxes to no longer use v-on:change but to instead use v-model for the relevant parameter for each.
This way your checkbox status will be bound to the true/falseness of the variables.
If you still want to go with your method, you can implement at like shown in this updated fiddle and set a variable atLeastOneItemIsChecked like this:
this.atLeastOneItemIsChecked = this.items.find(item => item.isChecked) !== undefined
Do not force the user to always check a checkbox. Instead, display a hint and disable the button using :disable and tailwind css resulting in this:

One line condition not working in data in Vue js

I have a component that is going to have data named isVendorOrAgent that should be false or true based on a property that the component gets. When I put this condition on the data section of the component, it doesn't work and always returns false but when I put that in created() and change the isVendorOrAgent in created(), it works.
How can I make this work?
This is the condition that is not working:
data : () => {
return {
isVendorOrAgent : (this.entityName == "vendor" || this.entityName == "agent") ? true : false;
}
}
but this works when the default is false:
created(){
if(this.entityName == "vendor" || this.entityName == "agent"){
this.isVendorOrAgent = true;
}
}
Try this code sample:
data () {
return {
isVendorOrAgent: Boolean(this.entityName === "vendor" || this.entityName === "agent")
}
}
What is different?
Now data is not an arrow function, because arrow functions do not change the context, so this won't be what it should be inside the component
We now store a Boolean value
We are now using strict equality ===, which is just a good habit
You may also take a look at computed properties: https://v2.vuejs.org/v2/guide/computed.html
Your problem could be solved like this:
computed () {
return {
isVendorOrAgent: Boolean(this.entityName === "vendor" || this.entityName === "agent")
}
}
The second way is preferable if you need this property to be reactive.

sails js model 'array' type validation and multiple select

I'm adding a type validation for my model, example:
selectList: {
type: 'array'
}
The selectList input comes from HTML Form's Multiple Select.
Problem
When user selects zero or one option only, in the case of zero, we get undefined/object type, in case of 1 selection we get 'string' type. Saving it to the selectList will fail.
What is the best solution to handle this issue?
Any ideas? using beforeValidation doesn't sound a good solution to me.
Why not use beforeValidation?
beforeValidation: function(obj, cb) {
if (typeof obj.field === "array") {
cb(null, obj);
}
if (typeof obj.field === "string")
{
var temp = obj.field ;
obj.field = new Array();
obj.field [0] = temp;
}
cb(null, obj);
}

Trouble Animating svgX of HighCharts Column Label

BOUNTY UPDATE: I'm putting a bounty on this, so I wanted to give a little bit more information in case someone comes up with a different solution than modifying my code. The object is to animate the actual category position in HighCharts for bar and column charts. Animating the actual "bar/columns" seems to be built in to HighCharts, however, the label positions is what I'm having trouble with. See below for JSFiddle. Also, I know this question is dealing with SVG, but I need the animation support in IE8 as well.
I'm currently on a mission to animate the reorganization of categories in HighCharts for bar and column charts.
I have bar charts working fine, with the ability to reorganize categories and labels, with labels animating with the following code:
$(this).animate({'svgY': c.labelPositions[myX]}, {'duration': animDuration, 'queue': false});
Now I'm working on the columns, and I'm having significant trouble getting the labels to animate. The code is relatively the same:
$(this).animate({'svgX': c.labelPositions[myX]}, {'duration': animDuration, 'queue': false});
I'm using jQuery SVG to allow the animation of SVG elements, you can find it here.
You can view the jsFiddle I'm working on here. Just click the "Go" buttons under each chart to see them in action.
The actual "hack" to allow category animating is the Highcharts.Series.prototype.update = function(changes, callback){ function.
Just playing around trying to get something to work, I found that I could animate the svgY of the Column labels, but svgX just seems to not function at all.
Actual HighCharts.js hacks are welcome.
I took a look into your code and improved it a little bit. I did the following:
Unified code for column/bar using single variable that stores the attribute we're going to update
Removed jQuerySVG dependency, instead my code uses built-in Highcharts animate method
Fixed some minor bugs
I tested this with IE7+/Chrome/Firefox, it works fine in all of them.
Here you can find my version of Highcharts.Series.prototype.update:
Highcharts.Series.prototype.update = function (changes, callback) {
var series = this,
chart = this.chart,
options = chart.options,
axis = chart.xAxis[0],
ticks = axis.ticks,
type = chart.options.chart.type,
animDuration = 400,
attr = type === 'bar' ? 'y' : 'x',
animOpt = {},
text;
if (options.chart.animation && options.chart.animation.duration) {
animDuration = options.chart.animation.duration;
}
if (type == "bar" || type == "column") {
if (typeof chart.labelPositions === "undefined") {
chart.labelPositions = [];
$.each(ticks, function () {
chart.labelPositions.push(this.label[attr]);
});
}
for (var category in changes) {
for (var i = 0; i < series.points.length; i++) {
if (typeof series.points[i].originalCategory === "undefined") {
series.points[i].originalCategory = series.points[i].category;
}
if (series.points[i].originalCategory == category) {
$.each(ticks, function () {
text = this.label.text || this.label.element.innerHTML; // use innerHTML for oldIE
if (text == category) {
var myX = (typeof changes[category].x !== "undefined") ? changes[category].x : series.points[i].x;
series.points[i].update(changes[category], false, {
duration: animDuration
});
animOpt[attr] = parseInt(chart.labelPositions[myX]);
// This is the line that animates the bar chart labels.
this.label.animate(animOpt, {
'duration': animDuration,
'queue': false
});
return false;
}
});
}
}
}
chart.redraw();
if (typeof callback !== "undefined") {
setTimeout(function () { callback(); }, animDuration);
}
}
}
Check out the demo: http://jsfiddle.net/evq5s/17