VueJS - Script not loaded when direct access to subpage - javascript

I'm currently working on a website with vue.js, and I use vue-route.
There's a subroute (/blog/post-slug) that load a script (Prism: http://prismjs.com/) and fires it in the ready() state :
import Prism from 'prismjs'
module.exports = {
Route: {
waitForData: true
},
data () {
return {
post: { }
}
},
ready () {
Prism.highlightAll()
}
}
When I reach the page from a link inside the site, it’s all good, loaded and working, the result is ok in the page.
But if I reload the page or access it with a direct link from an external link, it’s not good.
Here’s my route config, if it helps:
/**
* Router config
*/
module.exports = {
/**
* Config
* #type {Object}
*/
config: {
hasbang: false,
history: true,
saveScrollPosition: true,
linkActiveClass: 'site-menu__link--active',
transitionOnLoad: true
},
/**
* Before route starts transitioning
* #param {Object} options.from Route we are transitioning from
* #param {Object} options.to Route we are transitioning to
* #param {Function} options.next Progress to the next step of the transition
* #param {Function} options.abort Cancel / Reject the transition
* #param {Function} options.redirect Cancel and redirect to a different route
* #return {void}
*/
before ({from, to, next, abort, redirect}) {
next()
},
/**
* After route has transitioned
* #param {Object} options.from Route we are transitioning from
* #param {Object} options.to Route we are transitioning to
* #return {void}
*/
after ({from, to}) {
if (typeof to.title !== 'undefined') {
document.title = to.title
}
}
}
I guess something in particular happen when the view is loaded from inside the app, and not directly, but can’t find what.
What am I missing?
Thanks in advance for any hint.

Related

async in JavaScript - When to use await (and when to not) - 'await' has no effect on the type of this expression .ts(80007)

I am seeing this issue a lot in my code and I've seen similar posts about it on here but they don't seem linked to my issue specifically, which makes me think I'm missing something somewhere.
The comment 'await' has no effect on the type of this expression.ts(80007) comes up (as an example case) in the async method below...
/**
* Deletes the account for the currently logged in user.
*/
async deleteAccount() {
Logger.log('Deleting the currently logged in user', 'Auth')
const service = ServiceFactory.profile()
const operationState = await service.deleteUser()
if (!operationState.succeeded)
operationState.throw()
},
Dropping through the stack, this calls through as follows to this method in the ProfileService class which is returned above in ServiceFactory.profile()...
/**
* Deletes a user.
* #returns {ServiceOperation} The operation result.
* #memberof ProfileService
*/
async deleteUser() {
const operationState = new ServiceOperation('Delete User', true)
const url = `${ this.baseUrl }?SchemeId=${ this.schemeId }`
Logger.log(url, 'Request [DELETE]')
try {
return this.processResponse(await Axios.delete(url), operationState, 'deleteUser')
} catch (err) {
return this.failOperation(err, operationState, 'deleteUser')
}
}
The issue seems related to the processResponse method on the base class that transforms my JSON responses from my back end into ServiceOperation classes which tracks running state, success, merges multiple results and transforms different types of error responses to be consistent for the app to handle - the code for which is largely irrelevant to add here...
/**
* Handles a succesfull data changing operation and processes the response safely.
*
* #param {Object} response The service response.
* #param {ServiceOperation} operationState
* #param {string} [methodName='?'] The name of the calling method.
* #returns {ServiceOperation} The modified operation state.
*
* #memberof Service
*/
processResponse(response, operationState, methodName = '?') {
if (response.data !== false && !response.data || response.data == null) {
operationState.complete(true)
} else {
operationState.complete(true, response.data)
}
Logger.logObject(response, `Operation completed [${ methodName }]`, this.loggingCategory)
return operationState
}
failOperation is simply processResponse fail call method...
/**
* Handles a failed operation.
*
* #param {Error} error
* #param {ServiceOperation} operationState
* #param {string} [methodName='?'] The name of the calling method.
* #returns {ServiceOperation} The modified operation state.
*
* #memberof Service
*/
failOperation(error, operationState, methodName = '?') {
operationState.fail(error)
Logger.logObject(operationState, `Data service failed [${ methodName }]`, this.loggingCategory)
return operationState
}
Both of the above methods are part of the Service class which is extended to form the ProfileService class earlier on that contains the deleteUser function (The Service class acts as a base class for each of the service classes in the app.
Where I'm a bit confused here is that processResponse isn't async (and doesn't need to be). The awaited call is awaited in the method that calls it. Is this just a case of the editor not picking it up or, as I suspect, is it that I'm losing the underlying promise when I process the data - If that's the case how can I get the promise to surface up for the method result that processResponse needs?
processResponse is a generic method that handles all the service method results I have in my app so merging the methods isn't viable. I've considered making processResponse itself async and using await on it but that seems logically like it would do absolutely nothing as there is nothing to really await.
Functionally the app code seems to be working fine so I'm loathe to start pulling it apart at this level until I understand exactly why it's moaning.
EXTRA (but probably irrelevant) INFO
This is the definition of the ServiceOperation class that the Service class and it's derivatives make use of. This is for completion only and is largely irrelevant.
/**
* Defines an operation performed through a service.
*
* #export
* #class ServiceOperation
*/
export default class ServiceOperation {
/**
*Creates an instance of ServiceOperation.
* #param {string} name
* #param {boolean} [started=false]
* #memberof ServiceOperation
*/
constructor(name, started = false) {
this.name = name
this.running = started
this.completed = false
this.succeeded = false
}
/**
* Completes a service operation.
*
* #param {boolean} succeeded
* #param {object} data Any data or error info returned
* #returns {ServiceOperation} itself.
* #memberof ServiceOperation
*/
complete(succeeded, data) {
this.running = false
this.completed = true
this.succeeded = succeeded
if (data !== undefined && data !== null || this.data !== undefined && this.data !== null)
this.data = data
return this
}
/**
* Fails a service operation.
*
* #param {string} error An error
* #returns {ServiceOperation} itself.
* #memberof ServiceOperation
*/
fail(error) {
this.complete(false)
if (error && error.response && error.response.data) {
this.data = error.response.data
this.errorObject = error
this.error = error.response.data.message ? error.response.data.message : error.message
this.errorObject.message = this.error
} else {
this.error = error && error.message ? error.message : error
this.errorObject = error
}
return this
}
/**
* Throws the wrapped error object back for interrogation.
*
* #memberof ServiceOperation
*/
throw() {
if (this.errorObject)
throw this.errorObject
else if (this.error)
throw new Error(this.error)
else
throw new Error('Service Operation error')
}
/**
* Merges another operation into this one, updating it's info.
*
* #param {ServiceOperation} operation
* #param {boolean} includeData If not set then the data item will be eradicated if it is present.
* #memberof ServiceOperation
*/
updateMergeFrom(operation, includeData = false) {
this.running = operation.running
this.completed = operation.completed
this.succeeded = operation.succeeded
if (!includeData && this.data)
delete this.data
else if (includeData && operation.data)
this.data = operation.data
if (operation.stillPending !== undefined)
this.stillPending = operation.stillPending
if (!this.error && operation.error)
this.error = operation.error
}
/**
* Generates an immediately completed operation.
*
* #static
* #type {ServiceOperation}
* #memberof ServiceOperation
*/
static get immediate() {
return new ServiceOperation('Immediate').complete(true)
}
}
I found the answer to this digging through this article...
https://github.com/microsoft/TypeScript/issues/34508
This seems to be a warning generated by the TypeScript compiler (although my project isn't even using TypeScript so - {shrug})
In order to make it go away you need to edit your JSDoc entry as follows...
/**
* Deletes a user.
* #returns {ServiceOperation} The operation result.
* #memberof ProfileService
*/
async deleteUser() { }
becomes
/**
* Deletes a user.
* #returns {Promise<ServiceOperation>} The operation result.
* #memberof ProfileService
*/
async deleteUser() { }
Thanks to #Bergi for getting my brain to look in the right place.

How do I test nested function in methods property in Vue?

I have a Vue component with one method...
methods: {
launchOpinionLab () {
initOpinionLab({
clientId: this.clientId,
flow: this.flow,
srcCorrelationId: this.srcCorrelationId
})
window.OOo.inlineFeedbackShow()
}
initOpinionLab() is a exported function as such
initOpinionLab.js
/**
* Initializes the OpinionLab library with custom data.
*
* #param {Object} data - any data we want to attach to user feedback submissions
*/
export default function initOpinionLab (data) {
const opinionLab = window.OOo
/**
* Callback to launch the feedback comment card.
* This assigns our custom data to the OpinionLab instance.
*
* #param {Event} event - window event
*/
opinionLab.inlineFeedbackShow = (event) => {
let replacePattern = '://' + window.location.host
if (window.location.host !== 'checkout.mastercard.com') {
replacePattern = '://test.checkout.mastercard.com'
}
opinionLab.oo_feedback = new opinionLab.Ocode({
referrerRewrite: {
searchPattern: /:\/\/[^/]*/,
replacePattern: replacePattern
},
customVariables: data
})
// Now that oo_feedback has been re-initialized with the custom
// var and context of the current page, launch the comment card
opinionLab.oo_launch(event, 'oo_feedback')
}
/**
* Launches opinionLab.
*
* #param {Event} event
* #param {Object} feedback - opinionLab-structured feedback object
*/
opinionLab.oo_launch = (event, feedback) => {
opinionLab[feedback].show(event || window.event)
}
}
At the top of my unit test is the mock of that module, jest.mock('./initOpinionLab', () => jest.fn()).
My goal is to assert that initOpinionLab({}) is called with the correct props. When I try to spy on it, I get "Cannot spy the initOpinionLab property because it is not a function; undefined given instead". How do I write a robust test for this?
Or, what if I moved initOpinionLab() to mounted? Can I better test it then?
component.spec.js
it('[positive] initOpinionLab should be initialized with props' , () => {
const initOpinionLab = jest.fn()
jest.spyOn(wrapper.vm, 'initOpinionLab')
wrapper.vm.launchOpinionLab()
expect(initOpinionLab).toHaveBeenCalled()
})

Error on a MasterDetail page

So I'm trying to recreate this app on SAP Web IDE:
But I'm continuously getting this error:
This is my App.Controller.js code:
sap.ui.define([
"pt/procensus/ui5rv/controller/BaseController",
"sap/ui/model/json/JSONModel"
], function (BaseController, JSONModel) {
"use strict";
return BaseController.extend("pt.procensus.ui5rv.controller.App", {
onInit : function () {
var oViewModel,
fnSetAppNotBusy,
oListSelector = this.getOwnerComponent().oListSelector,
iOriginalBusyDelay = this.getView().getBusyIndicatorDelay();
oViewModel = new JSONModel({
busy : true,
delay : 0
});
this.setModel(oViewModel, "appView");
fnSetAppNotBusy = function() {
oViewModel.setProperty("/busy", false);
oViewModel.setProperty("/delay", iOriginalBusyDelay);
};
this.getOwnerComponent().oWhenMetadataIsLoaded.
then(fnSetAppNotBusy, fnSetAppNotBusy);
// Makes sure that master view is hidden in split app
// after a new list entry has been selected.
oListSelector.attachListSelectionChange(function () {
this.byId("idAppControl").hideMaster();
}, this);
// apply content density mode to root view
this.getView().addStyleClass(this.getOwnerComponent().getContentDensityClass());
}
});
}
);
This is my Base.Controller.js code:
/*global history */
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/core/routing/History"
], function(Controller, History) {
"use strict";
return Controller.extend("pt.procensus.ui5rv.controller.BaseController", {
/**
* Convenience method for accessing the router in every controller of the application.
* #public
* #returns {sap.ui.core.routing.Router} the router for this component
*/
getRouter: function() {
return this.getOwnerComponent().getRouter();
},
/**
* Convenience method for getting the view model by name in every controller of the application.
* #public
* #param {string} sName the model name
* #returns {sap.ui.model.Model} the model instance
*/
getModel: function(sName) {
return this.getView().getModel(sName);
},
/**
* Convenience method for setting the view model in every controller of the application.
* #public
* #param {sap.ui.model.Model} oModel the model instance
* #param {string} sName the model name
* #returns {sap.ui.mvc.View} the view instance
*/
setModel: function(oModel, sName) {
return this.getView().setModel(oModel, sName);
},
/**
* Convenience method for getting the resource bundle.
* #public
* #returns {sap.ui.model.resource.ResourceModel} the resourceModel of the component
*/
getResourceBundle: function() {
return this.getOwnerComponent().getModel("i18n").getResourceBundle();
},
/**
* Event handler for navigating back.
* It checks if there is a history entry. If yes, history.go(-1) will happen.
* If not, it will replace the current entry of the browser history with the master route.
* #public
*/
onNavBack: function() {
var sPreviousHash = History.getInstance().getPreviousHash();
if (sPreviousHash !== undefined) {
// The history contains a previous entry
history.go(-1);
} else {
// Otherwise we go backwards with a forward history
var bReplace = true;
this.getRouter().navTo("master", {}, bReplace);
}
}
});
});
My App Folders:
And I can't understand why is this happening. I've already removed the 'then' part and it gives me more errors... :/
Any help will be very much appreciated :)
Can you try this? At least this is how it works in my App.controller:
this.getOwnerComponent().getModel().metadataLoaded()
.then(fnSetAppNotBusy, fnSetAppNotBusy);

How to include js files in header of wordpress pages that are activated on-click

I am attempting to use wordpress to build a website that integrates google maps. I am doing some overlays with the maps and use the google developers API and Python to make the appropriate javascript. I have successfully written the js files and Python necessary to accomplish this.
My website is built in Worpress and I would like add a page (not the home page) that has n links and each one would populate a box with the corresponding map. I can take care of the layout and design issues but I am at a loss on how to:
a) Include the javascript as a file that
b) gets called upon clicking the link and thus populates that map without calling a new page
That is, the javascript is HUGE because it may include thousands of lat/lon points. Therefore including n of these written into the header is unreasonable. I want to simply call it from filename.js when the link is clicked.
There is a plugin that allows me to include whatever I want in the header. So, if I can find out where to put the *.js files (or txt file) in the directory tree and how to have the corresponding file activated upon click I should be good. Thanks!
This Display different maps with onClick event - Google Maps V3. kind of helps with doing an on-click display but everyone's solution was to make one map. I cannot do that. I am overlaying vast amounts of data.
Here is a way you can get that done. (Jump down to the get started part of the script.)
For brevity, I've included a bunch of scripts in one 'file', but you'll want to break them in to individual files.
You may also need to try the html and js in jsbin js bin example, b/c SO may or may not allow the dynamic loading of js.
(function(undefined) {
/**
* #author (#colecmc)
* #method turn collection into an array
* #param {object} collection - NodeList, HTMLCollection, etc. Should have an "item" method and/or a "length" property
*/
ToArray = collection => Array.prototype.slice.call(collection);
/** \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ **/
Observer = (function(undefined) {
/**
* pub sub
*/
'use strict';
var subUid = -1;
return {
topics: {},
subscribe: function(topic, func) {
/**
* #param {string} topic
* #param {function} func
* #returns {string} - a token such as '3'
* #example Observer.subscribe('any-valid-string',function(name,resp){
console.log(resp.prop);
});
*/
if (!Observer.topics[topic]) {
Observer.topics[topic] = [];
}
var token = (++subUid).toString();
Observer.topics[topic].push({
token: token,
func: func
});
return token;
},
publish: function publish(topic, args) {
/**
* #param {string} topic
* #param {object} args
* #returns {boolean} - true if topic is valid, false otherwise
* #example Observer.publish('any-valid-string',{
prop: 'this is a test'
});
*/
if (!Observer.topics[topic]) {
return false;
}
setTimeout(function() {
var subscribers = Observer.topics[topic],
len = subscribers ? subscribers.length : 0;
while (len--) {
subscribers[len].func(topic, args);
}
}, 0);
return true;
},
unsubscribe: function unsubscribe(token) {
/**
* #param {string} token - value should be saved from the original subscription
* #example Observer.unsubscribe('2');
* #example Observer.unsubscribe(member); - where member is the value returned by Observer.subscribe();
*/
var m,
forEachTopic = function(i) {
if (Observer.topics[m][i].token === token) {
Observer.topics[m].splice(i, 1);
return token;
}
};
for (m in Observer.topics) {
if (Observer.topics.hasOwnProperty(m)) {
Observer.topics[m].forEach(forEachTopic);
}
}
return false;
}
};
}(undefined));
/** \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ **/
SetAttributes = function(el, attrs) {
/**
* #author (#colecmc)
* #method simple for in loop to help with creating elements programmatically
* #param {object} el - HTMLElement attributes are getting added to
* #param {object} attrs - object literal with key/values for desired attributes
* #example SetAttributes(info,{
* 'id' : 'utswFormInfo'
* 'class' : 'my-class-name'
* });
*/
'use strict';
var key;
for (key in attrs) {
if (attrs.hasOwnProperty(key)) {
el.setAttribute(key, attrs[key]);
}
}
return el;
};
/** \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ **/
GetScript = function(url, fullPath) {
/**
* #author (#colecmc)
* #version 1.0.4
* #requires Swlxws.SetAttributes, Swlxws.Observer
* #method dynamically add script tags to the page.
* #param {string} url - relative path and file name - do not include extension
* #param {string} fullPath - absolute path
* #example GetScript('myLocalScript');
* #example GetScript('','https://www.google-analytics.com/analytics.js');
*/
'use strict';
function onLoad(event) {
var result;
if (event.type === 'load') {
result = 1;
} else {
result = -1;
}
Observer.publish('get-script-onload-complete', {
successful: result,
eventData: event
});
}
var JSPATH = '/js/',
/* or where ever you keep js files */
el = document.createElement('script'),
attrs = {
defer: true,
src: null,
type: 'text/javascript'
};
/** look for a string based, protocol agnostic, js file url */
if (typeof fullPath === 'string' && fullPath.indexOf('http') === 0) {
attrs.src = fullPath;
}
/** look for any string with at least 1 character and prefix our root js dir, then append extension */
if (typeof url === 'string' && url.length >= 1) {
attrs.src = JSPATH + url + '.js';
}
SetAttributes(el, attrs);
el.addEventListener('load', onLoad);
el.addEventListener('error', onLoad);
document.body.appendChild(el);
return el;
};
/** \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ **/
/**
* Get Started
*/
function onClick(event) {
GetScript('', event.target.dataset.namespaceUrl);
}
Observer.subscribe('get-script-onload-complete', function(name, resp) {
/** check to make resp is what you expect, ie: the correct script loaded */
/** then it is safe to use */
});
ToArray(document.querySelectorAll('.load-scripts')).map(script => script.addEventListener('click', onClick, false));
}(undefined));
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>How to include js files in header of wordpress pages that are activated on-click</title>
</head>
<body>
Load Google Analytics
</body>
</html>
You can use the function wp_enqueue_script() to load the necessary JS files on only the templates you want.
As for your large data set, I recommend that you cache it in an external .json file and use wp_enqueue_script() to load it only when necessary.
Well if the onclick event suggestion is pretty much what you want and you are just concerned about the large amount of data. Then there are a few ways to tackle it. I am not sure if the dataset is a js file or php/json files but i came across a similar issue on one of my projects, dont remember properly but i was doing something with maxmind's ip/location data set.
So i just splitted the large file into 3 smaller ones. Then i looped through each of the file and if the stuff that i was looking for was found in the file then i just breaked out. And definitely as Brian suggested caching and using a CDN would help a lot.

JSDoc: Return object structure

How can I tell JSDoc about the structure of an object that is returned. I have found the #return {{field1: type, field2: type, ...}} description syntax and tried it:
/**
* Returns a coordinate from a given mouse or touch event
* #param {TouchEvent|MouseEvent|jQuery.Event} e
* A valid mouse or touch event or a jQuery event wrapping such an
* event.
* #param {string} [type="page"]
* A string representing the type of location that should be
* returned. Can be either "page", "client" or "screen".
* #return {{x: Number, y: Number}}
* The location of the event
*/
var getEventLocation = function(e, type) {
...
return {x: xLocation, y: yLocation};
}
While this parses successfully, the resulting documentation simply states:
Returns:
The location of an event
Type: Object
I am developing an API and need people to know about the object that they will get returned. Is this possible in JSDoc? I am using JSDoc3.3.0-beta1.
Define your structure separately using a #typedef:
/**
* #typedef {Object} Point
* #property {number} x - The X Coordinate
* #property {number} y - The Y Coordinate
*/
And use it as the return type:
/**
* Returns a coordinate from a given mouse or touch event
* #param {TouchEvent|MouseEvent|jQuery.Event} e
* A valid mouse or touch event or a jQuery event wrapping such an
* event.
* #param {string} [type="page"]
* A string representing the type of location that should be
* returned. Can be either "page", "client" or "screen".
* #return {Point}
* The location of the event
*/
var getEventLocation = function(e, type) {
...
return {x: xLocation, y: yLocation};
}
An alternative to the suggestions already posted, you can use this format:
/**
* Get the connection state.
*
* #returns {Object} connection The connection state.
* #returns {boolean} connection.isConnected Whether the authenticated user is currently connected.
* #returns {boolean} connection.isPending Whether the authenticated user's connection is currently pending.
* #returns {Object} connection.error The error object if an error occurred.
* #returns {string} connection.error.message The error message.
* #returns {string} connection.error.stack The stack trace of the error.
*/
getConnection () {
return {
isConnected: true,
isPending: false,
error
}
}
which will give the following documentation output:
Get the connection state.
getConnection(): Object
Returns
Object: connection The connection state.
boolean: connection.isConnected Whether the authenticated user is currently connected.
boolean: connection.isPending Whether the authenticated users connection is currently pending.
Object: connection.error The error object if an error occurred.
string: connection.error.message The error message.
string: connection.error.stack The stack trace of the error.
A clean solution is to write a class and return that.
/**
* #class Point
* #type {Object}
* #property {number} x The X-coordinate.
* #property {number} y The Y-coordinate.
*/
function Point(x, y) {
return {
x: x,
y: y
};
}
/**
* #returns {Point} The location of the event.
*/
var getEventLocation = function(e, type) {
...
return new Point(x, y);
};

Categories

Resources