Value retrived by vue-cookie won't trigger lifecycle method "updated" - javascript

I'm trying to save and read multiple json objects from my vue-application with the vue-cookie package (Version 1.1.4). The snippet below runs really well and the json object is saved and retrivied as expected.
However, I noticed as soon as the data is retrieved via cookies and i want to change data inside the object on the web page, the "updated" lifecycle method will not trigger. This behaviour is really awkward as I am only adding the cookieToJson() method to the beforMount() method. The Vue debugger also shows that the value is changed. I think the data is not reactive anymore, but how can I fix this.
data () {
return {
json: {
a: 0,
b: 1,
},
}
},
methods: {
jsonToCookie(name) {
const data = "{\"" + name + "\": " + JSON.stringify(this[name])+"}";
this.$cookie.set(name, data, { expires: '1M' }, '/app');
},
cookieToJson(name) {
const data = JSON.parse(this.$cookie.get(name));
if(data==null) return
for(var i = 0; i < Object.keys(data).length; i++) {
var name = Object.keys(data)[i]
this[name] = data[name];
}
},
beforeMount() {
console.log("beforeMount")
this.cookieToJson("json")
},
updated() {
console.log("updated")
this.jsonToCookie("json")
},
}

In case anyone run into a similar problem, I am using the localStorage now.

Related

How to parse json if Key'name dynamicly changes each time node js

I receive JSON data from the service, but the keys change in the data with each request, below I will give an example in three versions.
Exmaple 1:
{
"trackingPayloads": {
"Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}',
"ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
Example 2:
{
"trackingPayloads": {
"36tW7DqZ3H9KKBEAumZmowmUwmDRmVCjQgv5zi9GM3Kz": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}',
"OgtE51n3YtvrVXWLFjPmpnRt2k5DExF7ovxmBTZrZ6wV": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
Example 3:
{
"trackingPayloads": {
"k2toY29glt2JEp9Wi1X5M7ocno0E0mS4JQVyDuGyQ2rM": "{"title":"Red Shoes","index":3,"id":"17777","type":"category"}'",
"5ef2ec3c3573eebecc9690b69619ec7b9c93b609": "{"title":"White Shoes","index":3,"id":"17777","type":"category"}',
}
}
As you can see, the data included in the keys does not change since I am requesting the same information, but the key will change with each request.
Please help, what are the options to get the data Title, Index and any other content in these keys using node js?
Only one option came to my mind - to rename the keys upon receipt in 1,2,3 ... and then get data from them, but this needs to be done dynamically, since about 120 requests per minute are made, you need to get this data quickly, there are no options to save it to a file (I didn’t understand how)
UPDATE, added my code.
I am attaching an example of my code, the idea is to eventually get the data I need from the right keys from trackingPayloads, please help with the code <3
const AwaitAPIResponse = await ProductAPI(product_sku);
const $ = cheerio.load(AwaitAPIResponse);
const JSONDATA = [];
$('pre').each(function() {
JSONDATA.push($(this).text());
});
const ProductJson = JSON.parse(JSONDATA[0]) // this is where I get all the data
const MainJson = ProductJson["trackingPayloads"] // here I go to the trackingPayloads you saw above
How can I get the data I need?
You can use Object.keys() to get all the different keys of an object and use a loop to go through them.
Therefore, you can rework this code in such a way that each of the values is stored as an element in an array, maybe makes the data easier to work with:
const convert = object => {
const ret = []
for (const key of Object.keys(object)) {
ret.push(object[key])
}
return ret
}
This will give you following result for your use case:
[{"title":"Red Shoes","index":3,"id":"17777","type":"category"},
{"title":"Red Shoes","index":3,"id":"17777","type":"category"}]
The way you'd call this is as follows:
const some_parsed_json = {
"k2toY29glt2JEp9Wi1X5M7ocno0E0mS4JQVyDuGyQ2rM": {
title:"Red Shoes",
index:3,
id:"17777",
type:"category"
},
"5ef2ec3c3573eebecc9690b69619ec7b9c93b609": {
title:"Red Shoes",
index:3,
id:"17777",
type:"category"
}
}
const json_object_values = convertor(some_parsed_json)
If you don't car about the key you could use Object.values on the received object to get the values
Object.values(payload)
// With your example it will return:
// [{"title":"Red Shoes","index":3,"id":"17777","type":"category"},
// {"title":"Red Shoes","index":3,"id":"17777","type":"category"}]
or in a more complete example
async function getParsedValues() {
const responseString = await service.getData(); // '{"trackingPayloads":{"Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq":{"title":"Red Shoes","index":3,"id":"17777","type":"category"},"ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk":{"title":"White Shoes","index":3,"id":"17777","type":"category"}}}'
const parsedResponse = JSON.parse(responseString); // { trackingPayloads: { Rltyn4gLRIWRKj9kS0YpWXytG81GZwcPWjEE7f31ALlq: { title:'RedShoes', index: 3, id: '17777', type: 'category' }, ywtA6OyM0hzVZZvnUjxoxJDI1Er9ArfNr8XKyi1D5Zzk:{title:'WhiteShoes', index: 3, id: '17777', type: 'category' } }}
const values = Object.values(parsedResponse); // [{"title":"Red Shoes","index":3,"id":"17777","type":"category"}, {title:'WhiteShoes', index: 3, id: '17777', type: 'category' }]
return values;
}

watch: true for nested objects - NuxtJS (Vue), latest version

I have a form with many fields attached to a data - this.myData:
data: function() {
return {
isDataChanged: false,
myData: {},
myChangedData: {
default: '',
default1: {},
default2: []
},
}
},
myData is populated from a response from the server and it populates the form values.
myChangedData is for the new values, which are changed v-on:input="onChangeMyData($event, 'default')":
onChangeMyData(e, name, required = false){
const val = e.target.value.trim();
this.myChangedData[name] = val;
console.log(this.myChangedData)
this.checkIsmyDataChanged();
},
I can use the same method, providing a key as a second param. With the method checkIsmyDataChanged I am checking is it changed some field in the form. This method loops through myChangedData and compares its properties with changedData and if there is a difference this.isDataChanged = true.
The problem is that, I have a complicated structure of mydata/mydatachanged. default1 has objects in it and default1 is an array of objects. This means that, I can't use onChangeMyData, but other methods with different checks (validations) and now I need to call in all of them this.checkIsmyDataChanged();.
I created a watch for myChangedData:
watch:{
myChangedData: {
handler: function (newVal) {
console.log('change')
},
deep: true
},
},
, but it doesn't execute on change data
Did you try with Vue.set ? Source
Change this.myChangedData[name] = val; to
this.$set(this.myChangedData, 'name', val)
Thanks to that, the modification on the object should be detected and execute the watcher.

Show length of data array in VueJS after getting the data from API

I'm using Vue JS to get some data from an API. I want to get the length of this array and log to console via a method.
But it always logs the value of "0" instead of the real length.
I can access the data in the html without any problem and show the length ( {{ termine.length }} ).
But I want to do it using the method "logToConsole".
It seems to be a problem to access the data (which seems to be undefined in the moment of function call).
I fetch the data on "created", and output the length in "mounted", so it should be available (?).
Do you have any idea why I cannot access the data of the array in my "mounted" function, even after getting the data in "created"?
new Vue ({
el: "#calendar",
data: {
termine: [],
},
},
created() {
fetch("https://www.myurl.com/data/?json&function=GetEvents")
.then(response => response.json())
.then((data) => {
this.termine = data;
})
},
mounted() {
this.logToConsole();
},
methods: {
logToConsole: function () {
console.log(this.termine.length);
},
},
})
Of course, created hook is triggered before mounted.
But you know, you are setting termine property using API response and it is happen with async.
If you simple set termine property with simple statement like this.termine = ['a', 'b'], it will logs 2.
If you want log the data after getting the value using API, you could use watch.
like:
watch: {
termine(val) {
console.log(val)
}
}

Watch elements of simple array

Vue dont see changes in simple array items.
I am learning Vue.js and have problem with watcher.
Namely i am trying to watch changes in array, and change one data value.
Every time i add a new item and change or delete an existing item, I want to change the value.
data() {
return {
change: false,
array: ['one','two','three','four']
}
},
watch:{
array:{
deep:true,
handler(){
this.change = true;
}
}
}
Vue just see when array length is changed but not particular element.
You can watch nested values in an object as shown in the docs use a dot-like-string notation.
var vm = new Vue({
data: {
e: {
f: {
g: 5
}
}
},
watch: {
// watch vm.e.f's value: {g: 5}
'e.f': function (val, oldVal) { /* ... */ }
}
})
To my knowledge you CANNOT do this with arrays (i.e. array[0]) because the reference my shift or be removed. I think the best way to do what you want is to compare the newValue and oldValue in the watcher handler function if the whole array changes.
// from vuejs docs
watch: {
// whenever question changes, this function will run
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},

IIFE View Model seems to be undefined

I am using Mithril.JS and it looks like my vm is undefined where as prior it wasn't.
I searched around and there is very little out there in terms of mithril.js.
Code:
var app = {};
var apiData;
app.getData = function () {
m.request({
method: 'GET',
url: '/api/stocks',
}).then(function(data){
data = apiData;
})
};
app.App = function(data){ // model class
this.plotCfg = {
chart: {
renderTo: "plot"
},
rangeSelector: {
selected: 4
},
yAxis: {
labels: {
formatter: function () {
return (this.value > 0 ? ' + ' : '') + this.value + '%';
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
plotOptions: {
series: {
compare: 'percent',
showInNavigator: true
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
valueDecimals: 2,
split: true
},
series: [{
name: 'Kyle\'s Chart',
data: apiData
}]
};
};
app.controller = function() { // controller
this.apk = new app.App();
this.cfg = this.apk.plotCfg;
};
app.plotter = function(ctrl) { // config class
return function(elem,isin) {
if(!isin) {
m.startComputation();
var chart = Highcharts.StockChart(ctrl.cfg);
m.endComputation();
}
};
};
app.view = function(ctrl) { // view
return m("#plot[style=height:400px]", {config: app.plotter(ctrl)})
};
app.Stock = function(data) {
this.date_added = m.prop(new Date());
this.symbol = m.prop(data.symbol);
this.id = m.prop(data.id)
};
app.SymbolList = Array;
app.vm = (function() {
var vm = {}
vm.init = function() {
//a running list of todos
vm.list = new app.SymbolList();
//a slot to store the name of a new todo before it is created
app.parseData = function (data) {
for (var i =0; i< list.length ;i++) {
console.log(list[i].stock);
var stockSymbol = data[i].stock;
vm.list.push(new app.Stock({symbol : stockSymbol}));
}
app.parseData(apiData);
vm.symbol = m.prop("");
//adds a todo to the list, and clears the description field for user convenience
vm.add = function() {
var data = vm.symbol();
if (vm.symbol()) {
data = {'text': data.toUpperCase()};
m.request({method: 'POST',
url: '/api/stocks',
data: data,
}).then(function(list) {
vm.list = [];
for (var i =0; i< list.length ;i++) {
console.log(list[i].stock);
var stockSymbol = list[i].stock;
vm.list.push(new app.Stock({symbol : stockSymbol}));
}
return;
})
vm.symbol("");
}
};
}
return vm
}
}())
app.controller2 = function() {
app.vm.init();
}
app.view2 = function() {
return [
m('input', { onchange: m.withAttr('value', app.vm.symbol), value: app.vm.symbol()}),
m('button.btn.btn-active.btn-primary', {onclick: app.vm.add}, 'Add Stock'),
m('ul', [
app.vm.list.map(function(item , index) {
return m("li", [
m('p', item.symbol())
])
})
])
]
};
m.mount(document.getElementById('chart'), {controller: app.controller, view: app.view}); //mount chart
m.mount(document.getElementById('app'), {controller: app.controller2, view: app.view2}); //mount list
<div id="app"></div>
<div id="chart"></div>
<script src="https://cdn.rawgit.com/lhorie/mithril.js/v0.2.5/mithril.js"></script>
<script src="https://code.highcharts.com/stock/highstock.js"></script>
The error that pops up in chrome is this:
app.js:119 Uncaught TypeError: Cannot read property 'init' of undefined
at new app.controllerT (app.js:119)
at ea (mithril.js:1408)
at Function.k.mount.k.module (mithril.js:1462)
at app.js:135
It was fine before I added the second mount-point, view and controller.
Any ideas?
The problem is that app.vm doesn't expose init.
The current code for app.vm looks like this:
app.vm = (function(){
var vm = {}
vm.init = function(){
/* lots of stuff... */
return vm
}
}())
This means the internal vm.init returns vm, but the app.vm IIFE doesn't return anything. It should be:
app.vm = (function(){
var vm = {}
vm.init = function(){
/* lots of stuff... */
}
return vm
}())
It's very difficult to read your application structure because it's full of a variety of exotic patterns that don't seem to be useful. Admittedly, the 'vm' closure is a pattern introduced in Mithril guides, but I think it's far easier to write, reason about and debug applications if we avoid all these closures, initialisation calls, constructors, nested objects and namespaces.
The idea behind 'view models' comes from the state of web app development when Mithril was originally released (early 2014), when one of the principle concerns in front-end app development was a perceived lack of structure, and Mithril felt it necessary to show people how to structure objects. But structure of this form is only useful if it clarifies intent - in the code above it confuses things. For example, app.getData isn't called anywhere, and it assigns an empty global variable to its own argument before disposing of it. This kind of thing would be easier to reason about if we had less objects.
Here's the same code with some extra fixes and an alternate structure. The principles at work in this refactor:
We're no longer writing any of our own constructors or closures, resulting in less dynamic code execution and avoiding potential for errors like app.vm.init
We're no longer attaching things to objects unless that structure is useful or meaningful, and using simple variables or declaring things at the point of use if they're only used once, resulting in less references and less structural complexity
We use object literals - var x = { y : 'z' } instead of var x = {}; x.y = 'z' so we can see holistic structures rather than having to mentally interpret code execution to work out how objects will be built at runtime
Instead of using one big generic app.vm to store everything, we separate our app model into the places where they are relevant and use functions to pass values from one place to another, allowing us to split our complexity. I'll elaborate on this after showing the code:
// Model data
var seriesData = []
// Model functions
function addToSeries(data){
seriesData.push.apply(seriesData,data)
}
function getData( symbol ){
m.request( {method: 'POST',
url: '/api/stocks',
data: { text : symbol.toUpperCase() },
} ).then(function(list) {
return list.map(function( item ){
return makeStock( { symbol : item.stock } )
} )
} )
}
function makeStock( data ) {
return {
date_added : new Date(),
symbol : data.symbol,
id : data.id
}
}
// View data
var chartConfig = {
rangeSelector: {
selected: 4
},
yAxis: {
labels: {
formatter: function () {
return (this.value > 0 ? ' + ' : '') + this.value + '%';
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
plotOptions: {
series: {
compare: 'percent',
showInNavigator: true
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
valueDecimals: 2,
split: true
},
series: [{
name: 'Kyle\'s Chart',
data: seriesData
}]
}
// Components
var chartComponent = {
view : function(ctrl) {
return m("#plot[style=height:400px]", {
config: function(elem,isin) {
if(!isin)
Highcharts.StockChart(elem, chartConfig)
}
})
}
}
var todosComponent = {
controller : function(){
return {
symbol : m.prop('')
}
},
view : function( ctrl ){
return [
m('input', {
onchange: m.withAttr('value', ctrl.symbol),
value: ctrl.symbol()
}),
m('button.btn.btn-active.btn-primary', {
onclick: function(){
if( ctrl.symbol() )
getData( ctrl.symbol() )
.then( function( data ){
addToSeries( data )
} )
ctrl.symbol('')
}
}, 'Add Stock'),
m('ul',
todos.map(function(item) {
return m("li",
m('p', item.symbol)
)
})
)
]
}
}
// UI initialisation
m.mount(document.getElementById('chart'), chartComponent)
m.mount(document.getElementById('app'), todosComponent)
There is no more app, or vm, or list. These end up being unhelpful because they're so vague and generic they get used to store everything - and when one object contains everything, you may as well have those things freely available.
The core dynamic data list is now called seriesData. It's just an array. In order to interact with it, we have 3 simple functions for mutating the series data, fetching new data, and creating a new data point from input. There's no need for constructors here, and no need for props - props are a Mithril utility for being able to conveniently read and write data from an input - they're incompatible with the Highcharts API in any case.
That's all the model data we need. Next we have the code specific to our UI. The Highcharts config object references seriesData, but apart from that its an esoteric object written to conform with Highcharts API. We leave out renderTo, because that's determined dynamically by our Mithril UI.
Next comes the components, which we write as object literals instead of piecing them together later - a component controller only makes sense in relation to its view. The chartComponent doesn't actually need a controller, since it has no state and just reads previously defined model and view data. We supply the element reference directly to the Highcharts API in the config function. Because this function is only used once in a single place, we declare it inline instead of defining it in one place and binding it somewhere else. start/endComputation are unnecessary since the process is synchronous and there's no need to stop Mithril rendering during this process.
I couldn't quite work out how the 'todos' model was meant to work, but I assumed that the second component is designed to provide an alternate view of data points and allow user input to define and fetch more data. We store the 'symbol' prop in the controller here, since it's a stateful property that's used exclusively by the view. It's our only stateful property relating to this component, so that's all we define in the controller. Earlier on we simplified the model-related functions - now in the view we interact with these, passing in the symbol data explicitly instead of defining it elsewhere and retrieving it in another place. We also reset the value here, since that's an aspect of this component's logic, not the overal data model.

Categories

Resources