Add settings to embeddable angular widget - javascript

We're nearing the completion of working on an embedible JavaScript widget which has been built with AngularJS, to date everything it working fine, however we're now at the point where we need to allow the site owner to inject config settings into the JavaScript such as:
company_id
user
name
email
id
default overrides
In a previous iteration of our widget we used raw JavaScript, and were able to add settings like this:
Widget = window.Widget || [];
(function () {
var i, e;
i = document.createElement("script"),
i.type = 'text/javascript';
i.async = 1,
i.src = "https://path/to.js",
e = document.getElementsByTagName("script")[0],
e.parentNode.insertBefore(i, e);
})();
Widget.push(['set', {
'account_id': 'konami',
}]);
Inside our JavaScript, we simply had a window.Widget object which we overwrote with their settings and used wherever we needed.
However I'm at a loss as to how / where to receive this information and use it from inside our angular app, would appreciate any help on the best way to add in settings / configuration from when the JavaScript is loaded, and be able to use this in our services / controllers.

I ended up just adding the settings I wanted directly on an object on the window, then used that inside my angular project.
ala
_widget = window._widget || [];
_widget.company_id = '1234';
_widget.user = {name: 'Chris'};
Definitely not the pretty way I was hoping for, but it gets the job done.

Related

How to attach a javascript SDK in a react app by avoiding window object

I have got a web app written in React js in which I need to integrate a javascript SDK as documented in this link
The sample code is:
<script type="text/javascript">
var _user_id = 'al_capone'; // Set to the user's ID, username, or email address, or '' if not yet known.
var _session_id = 'unique_session_id'; // Set to a unique session ID for the visitor's current browsing session.
var _sift = window._sift = window._sift || [];
_sift.push(['_setAccount', 'INSERT_BEACON_KEY_HERE']);
_sift.push(['_setUserId', _user_id]);
_sift.push(['_setSessionId', _session_id]);
_sift.push(['_trackPageview']);
(function() {
function ls() {
var e = document.createElement('script');
e.src = 'https://cdn.sift.com/s.js';
document.body.appendChild(e);
}
if (window.attachEvent) {
window.attachEvent('onload', ls);
} else {
window.addEventListener('load', ls, false);
}
})();
</script>
Now, I'm aware that I should not use window object in React app however this sample code requires me to attach an object to the window object.
I have created a function to add the script:
function createSiftScript(sift_script_ele) {
//attach the object array to the window
var _sift = window._sift = window._sift || [];
_sift.push(['_setAccount', SIFT_SCIENCE_BEACON_KEY]);
_sift.push(['_setUserId', email]);
_sift.push(['_setSessionId', setUniqueSessionId()]);
//attach the external script
sift_script_ele = document.createElement("script");
sift_script_ele.setAttribute('id', SIFT_SCRIPT_ID);
sift_script_ele.setAttribute('type', "text/javascript");
sift_script_ele.setAttribute('src', "https://cdn.sift.com/s.js");
document.head.appendChild(sift_script_ele);
}
And a function to set the object on the window as suggested for SPAs like mine build in React/Angular:
export function pushEventsToSift() {
_sift.push(['_trackPageview']);
}
I'll call these functions in all the pages wherever needed however, accessing the window object looks dirty to me.
My question is:
If you would be doing this SDK integration in a React SPA then how would you have approached it by following the cleanest approach?
If this doesn't touch the DOM elements in React's care then you're safe in terms of it integrating with React. Regarding setting it on the page, you could throw it directly in index.html, there's nothing stopping you.
If you want it on specific pages - though wasn't this an SPA? - then you can throw the config in a React element as per their docs which should return an empty div or something that React never touches afterwards; But you'll still need to clean up the window object manually .

JavaScript - Front-end MVC

My actual question here:
I'm wondering if (beargrylls.com) uses Django or it's packages. Or some other framework. Or a custom framework? Also, if you take a look at the website (beargrylls.com), you can see that it uses a lot of paralax scrolling, sliders and cool animations. Is this custom-made or is this another framework/plugin/whatever?
If found an awesome website (beargrylls.com) on awwwards.com. I'm familiar with the MVC model that Laravel uses. So I know the basics. But I found out that (beargrylls.com) uses some kind of routing inside it's scripts!?
What I also found remarkanble is that the script(s) and the entite css of the website is loaded in inline HTML. So there are no HTTP requests, no files to load except the images
Which framework/plugin compiles this?
Example:
, Route = function t() {
classCallCheck(this, t);
var e = new Router({
xhr: !0
});
e.get("/", HomeController),
e.get("/about", AboutController),
e.get("/television", TelevisionController),
e.get("/live", LiveController),
e.get("/experiences", ExperiencesController),
e.get("/socialwall", SocialwallController),
e.get("/adventurers", AdventurersController),
e.get("/termsofuse", TermsofuseController),
e.get("/faqs", FaqsController),
e.get("/signup", SignupController),
e.error(ErrorController),
e.run()
}
, App = function t() {
classCallCheck(this, t),
Support.init(),
index.TopWhenRefresh(),
new Route
};
new App;
Another example that boosted my suspisions can be found inside it's createClass function or class. Where it assumably creates classes.
this.backgroundWrap = index.Geb.id("h-header-background-wrap"),
this.centerWrap = index.Geb.id("h-header-center-wrap"),
this.foregroundWrap = index.Geb.id("h-header-foreground-wrap"),
this.titleWrap = index.Geb.id("h-header-title-wrap"),
this.introTxt = index.Geb.id("h-intro-txt"),
this.introLine = index.Geb.id("h-intro-line"),
this.introVideo = index.Geb.id("h-intro-video"),
this.introScroll = index.Geb.id("h-intro-scroll"),
this.nav = index.Geb.class("nav")[0],
this.progress = index.Geb.id("h-intro-progress"),
this.scrollLineWrap = index.Geb.id("h-header-scroll-line-wrap"),
this.scrollTxtWrap = index.Geb.id("h-header-scroll-txt-wrap"),
this.stickyBurger = index.Geb.id("sticky-burger"),
So it seems to me that almost this entire website (routing, element classes and more) is created trough this script. I even see some mailchimp tags here.
I looked into the Django framework because of some of the links to media folders like /static/.
Penryn-starter
A starter kit for web development.
Docker
Browsersync
PHP 7.1.8
PostCSS
Css Nano
ES2015
ESLint
Rollup
Uglify JS 3
Skylake
https://github.com/ariiiman/penryn-starter
Skylake:
It's a Light JavaScript library named Skylake:
https://github.com/ariiiman/skylake
example:
const animation = new S.Merom('.element', '3dy', 0, 100, 1000, 'Power4InOut')
animation.play()
animation.reverse()
Similar functions can also be found in the Sources tab of the Google Devtools on beargrylls.com:
this.tlImg.from(this.background, "3dy", 10, 0, 2e3, "ExpoOut")
Also, in the script can skylake be found litterally:
scroll: {
throttle: !0,
skylake: "Scroll"
},
But I'm not quite sure what this actually does. I can't explain the routing class or anything of that sort. But it defenitely uses this Skylake library.
The same structure of code can be found on these websites: https://www.aristidebenoist.com/ (author of Skylake) and on http://www.jennyjohannesson.com/
though I do not think that these websites are using Skylake. Or just use other variables or something. But this:
, App = function e() {
classCallCheck(this, e),
Support.init(),
index.TopWhenRefresh(),
new Router({
xhr: !0
})
};
!function(e) {
new App
}();
}
)();
is exactly the same as on beargrylls.com
also the almost exact same Route class can be found on http://www.jennyjohannesson.com/

Possible to pass information to a browserfied file?

I have a set of widgets that are loaded dynamically depending on what the user requests. I have that aspect working (creating a script tag on the fly with the proper widget requested) as well as the browserification of the widgets into separate components.
My question is, is it possible to pass config information to the browserfied widget when it's loaded without using global variables?
For example:
var pageHead = document.getElementsByTagName("head")[0];
var script = document.createElement('script');
script.src = baseUrl +'/' + widget + '.js';
script.onload = function (event) {
// do something here maybe to pass information into the 'ad' widget
// for instance
};
pageHead.appendChild(script);
A good practice to pass configuration parameters to browsified widgets would be declare it in attributes.
<div prop1="abc" prop2="def" id="testwidget"></div>
and access them in javascript as required by using getAttribute() method...
document.getElementById("testwidget").getAttribute("prop1");
This will help you maintain configuration at the DOM level rather than keep a global variable.
Hope it helps!

non-concatenated dat.gui source with require.js. Customising, or templating dat.gui

Please excuse the SEO friendly title, but I would like to make the problem I am currently solving as accessible as possible. For those of you who are looking to customise the look and feel of dat.gui, you need to download the source and include it using require.js using the instructions here: https://code.google.com/p/dat-gui/.
And now my question.
I am working on a project that requires building a UI with heavy live integration with Javascript (I'm using three.js) and I have decided to modify dat.gui to create this ui; with a view to soon integrate it with backbone.js as a collection of views.
I wish to switch to use the dat.gui source files to edit the styling
To start, I switched over from the concatenated dat.gui.min.js, to the source using the instructions in the link above. I put the whole source in its own folder within my libraries folder, and placed the main.js file the require.js err... requires within the "src" folder. I did this due to linking dependencies within dat.gui's "GUI.js".
Everything seems to link up properly, and I am using essentially the same code as I did before to create my dat.guis, but I keep getting undefined errors, depending on how I change the gui variable either in my original code or in main.js. My understanding of require.js is VERY limited and I feel it is something integral to how it works (and that it's defiantly one of those oh so simple problems for someone with the know how)
Here's my main.js file located at /libraries/dat-gui/src
(this dir also includes text.js)
//This is main.js for using require.js
require([
'dat/gui/GUI'
], function(GUI) {
// No namespace necessary
var gui = new GUI();
});
and here's the bones of my original dat.gui definition code:
////////////////////////////////////////////////DatGui/////////////////////////////////////////////////////
var stageConfigData = function() {
this.scaleX = params.stageWidth;
this.scaleZ = params.stageDepth;
this.spinTheCamera = false;
this.lightIntensity = 1;
this.lightDistance = 0;
this.lightFrontColour = "#ffb752";
this.lightRearColour = "#3535fa";
this.lockCameraToScene = true;
this.tooltype = 3;
this.objectSelect = 'Man';
this.saveJSON = function(){
saveJSON();
};
};
var stageConfig = new stageConfigData( );
var moveConfig = new moveConfigData( );
///I think the problem is linked to defining this variable:
//var gui = new dat.GUI();//works for the minified version
var gui = new dat.GUI();//for non-concatenated
var fstage = gui.addFolder('Stage');
var fcamera = gui.addFolder('Camera');
var ffhouselts = gui.addFolder('Front of House Lights');
var fRearlts = gui.addFolder('Rear Lights');
var sandl = gui.addFolder('Saving and Loading');
fstage.add( stageConfig, 'scaleX', 1, 100, 5).name('Width of stage').onChange( function(){
stage.scale.x = ( stageConfig.scaleX );
});
fstage.add( stageConfig, 'scaleZ', 1, 100, 5).name('Depth of stage').onChange( function(){
stage.scale.z = ( stageConfig.scaleZ );
});
... //there's more but i think it's irrelevant
and the errors i am getting are either:
Uncaught ReferenceError: dat is not defined
or
Uncaught ReferenceError: GUI is not defined
depending on how I mess with those variables and the bit in main.js under //No namespace necessary. I don't understand how namespaces work as I am quite new to javascript as a whole.
Has anyone any ideas? Again, I'd say this is probably one of those real dunce moments, but I would really appreciate the help nonetheless.
Thanks a lot!
When you use require.js method there will be no global object. But you can create it yourself
require([
'dat/gui/GUI'
], function(GUI) {
window.dat = {
GUI: GUI
};
});

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

Categories

Resources