YUI3, Modules, Namespaces, calling functions - function

I would like to port the javascript code from my page to YUI3. After reading many posts (questions and answers) here and lots of information in the YUI3 page and in tutorials I have come to the conclusion that the best way to do it is by splitting the code in modules, because it allows me to load scripts dinamically only when needed.
I would like to organize the code in different submodules which should be loaded and managed (if needed) by a core module.
I think I have understood how to dinamically load them, but the problem I have now is that I am not always able to call the public methods both within a module and form one module to another. Sometimes it works, but sometimes I get the message xxx is not a function.
Probably the question is I don't understand how to set a global namespace (for example MyApp) and "play" within that namespace.
I would like to be able to call methods the following way: MyApp.Tabs.detectTabs()... both from the methods of the main module (MyApp.Core) and from the same submodule (MyApp.Tabs).
Here is the structure of my code:
Inline javascript:
var MyAppConfig = {
"tabpanels":{"ids":["navigation"]},
"events": [{"ids": ["main_login", "dictionary_login"],
"type": "click",
"callback": "MyApp.Core.updateContent",
"params":{
}
}]
};
YUI_config = {
filter: 'debug',
groups: {
'myapp': {
modules: {
'project-myapp-core': {
fullpath: 'http://www.myapp.com/scripts/Core.js',
requires: ['node-base']
},
'project-myapp-tabs': {
fullpath: 'http://www.myapp.com/scripts/Tabs.js',
requires: ['base', 'project-myapp-core', 'history', 'tabview', 'tabview-base']
}
}
}
}
};
YUI(YUI_config).use('node', 'base', 'project-myapp-core', function(Y) {
var MyApp = {};
MyApp.Core = new Y.MyApp.Core();
Y.on('domready', MyApp.Core.begin, Y, null, application);
});
Module: Core
File: http://www.myapp.com/scripts/Core.js
YUI.add('project-myapp-core', function(Y) {
function Core(config) {
Core.superclass.constructor.apply(this, arguments);
}
Core.NAME = 'myapp-core';
Core.ATTRS = {};
var MyApp;
MyApp = {};
Y.extend(Core, Y.Base, {
initializer: function (cfg) {
},
begin: function(e, MyAppConfig) {
MyApp.Core = instance = this;
if (MyAppConfig.tabpanels) {
YUI().use('node', 'project-myapp-tabs', function(Y) {
MyApp.Tabs = new Y.MyApp.Tabs();
});
}
if (MyAppConfig.events) {
MyApp.Core.prepareEvents(MyAppConfig.events);
// I get "MyApp.Core.prepareEvents is not a function"
}
},
prepareEvents: function(e) {
},
updateContent: function() {
}
});
Y.namespace('MyApp');
Y.MyApp.Core = Core;
}, '0.0.1', { requires: ['node-base'] });
Submodule: Tabs
File: http://www.myapp.com/scripts/Tabs.js
YUI.add('project-myapp-tabs', function(Y) {
function Tabs(config) {
Tabs.superclass.constructor.apply(this, arguments);
}
Tabs.NAME = 'myapp-tabs';
Tabs.ATTRS = {};
var tabView = [];
Y.extend(Tabs, Y.Base, {
initializer: function (cfg) {
},
begin: function (tabpanels) {
},
methodA: function () {
}
});
Y.namespace('MyApp');
Y.MyApp.Tabs = Tabs;
}, '0.0.1', { requires: ['base', 'project-myapp-core', 'history', 'tabview', 'tabview-base'] });
Where should I define the global variables, the namespace...? How should I call the functions?
Thanks in advance!
-- Oriol --

Since nothing depends on project-myapps-tabs, YUI doesn't include it. Try this in your inline JS:
YUI(YUI_config).use('node', 'base', 'project-myapp-tabs', function(Y) {

Related

How do I bind data to an element created with this.$createElement

I created an InfoWindow from Google maps and I want to put a vue component (specifically an input) inside the content property and bind it to a data. I'm also using vuetify and if possible, I want to use it's VTextfield component. If not, then a regular input would be ok as well.
Example:
data
data () {
return {
inputVal: null
}
}
methods
renderInfoWindow () {
let input = /* create an input and bind it to inputVal */
return new google.maps.InfoWindow({
content: input
})
}
According to the Template Compilation section in the docs,
<input v-model="inputVal">
is this render function:
function anonymous(
) {
with(this){
return _c('input', {
directives: [{
name: "model",
rawName: "v-model",
value: (inputVal),
expression: "inputVal"
}],
domProps: { "value": (inputVal) },
on: {
"input": function($event) {
if ($event.target.composing)
return;
inputVal=$event.target.value
}
},
})
}
}
I haven't used render functions yet, so I hope this is what you need.

load data from chrome.storage into vue.js data

I'm building a chrome app and I use Vue.js for the options page.
So I want to load settings from the chrome storage and put it into the vue data.
My problem is, that i can not access the vue compontens from inside the chrome storage callback. Every time i call it inside the callback, all vue elements are undefined.
Is there a way, to let the chrome storage cb function return a value, or give it an extra callback.
Here is my code
name: 'app',
data: function () {
return {
data []
}
},
methods: {
init: function() {
chrome.storage.sync.get('series', function (storageData) {
this.data = storageData //this is not possible, because this.data is undefined
})
});
}
},
created: function () {
this.init();
}
}
If using ES6 and transpiling (preferred approach). Note: arrow functions don't create a new context.
init: function() {
chrome.storage.sync.get('series', storageData => {
this.data = storageData
});
}
ES5 workaround:
init: function() {
var self = this;
chrome.storage.sync.get('series', function (storageData) {
self.data = storageData
});
}

Vue.js dynamic component loading + hot reload

I have Laravel 5.3.21 application with Vue.js 1.0.28
I'm using hot-reload workflow with browserify-hmr plugin.
Here is the simple gulpfile.js used to achieve that:
var elixir = require('laravel-elixir');
var gutil = require('gulp-util');
require('laravel-elixir-browserify-official');
require('laravel-elixir-vueify');
// If 'gulp watch' is run
if (gutil.env._.indexOf('watch') > -1) {
// Enable watchify for faster builds
elixir.config.js.browserify.watchify.enabled = true;
// Add the browserify HMR plugin
elixir.config.js.browserify.plugins.push({
name: 'browserify-hmr',
options: {
url: 'http://1.2.3.4:2096',
hostname: '1.2.3.4',
port: 2096
}
})
}
elixir.config.js.browserify.watchify.options.poll = true;
elixir(function (mix) {
mix.copy('node_modules/datatables.net-bs/css/dataTables.bootstrap.css',
'resources/assets/css/vendor/dataTables.bootstrap.css');
mix.styles([
'vendor/dataTables.bootstrap.css'
]);
mix.sass('app.scss');
mix.browserify('app.js');
});
I need to load components dynamically from the resources/assets/js/views/ folder, so I could make my front-end code modular and based on current route name $Laravel->routeName = request()->route()->getName() in Laravel.
For example:
// Global Settings.
Route::get('admin/settings', 'Admin#settings')
->name('admin_global_settings');
Then in resources/assets/js/views/admin/admin_global_settings.js I have code to initialize Vue.js component and register it with Vue.js instance:
var FeaturedOpportunities = require( '../../components/Featured-Opportunities.vue' );
window.Vue.component('FeaturedOpportunities', FeaturedOpportunities);
That is all nice but here is the problem in resources/assets/js/app.js:
window.Vue = require('vue');
require('vue-resource');
Vue.http.interceptors.push((request, next) => {
request.headers.set('X-CSRF-TOKEN', Laravel.csrfToken);
next();
});
// Problem is here, have to keep track of all my routes and their corresponding modules:
var routes = {
'organization_invites_roles': function () {
require('./views/organization/organization_invites_roles');
},
'admin_global_settings': function () {
require('./views/admin/admin_global_settings');
},
};
// Is there a way to load it dynamically?
if (Laravel.routeName) {
if (routes[Laravel.routeName]) {
routes[Laravel.routeName]();
}
}
new Vue({
el: 'body',
components: {
},
ready() {
console.log('Vue and Vueify all set to go!');
}
});
I've found some way that could probably solve this issue: Compiling dynamically required modules with Browserify but not sure if this applicable for my case.

Extending Custom Behavior

In polymer 1.0, I created custom behavior pageBehavior for two custom elements. On one of the elements, I would like to extend the behavior. After reading the docs, it seems that I would need to create another behavior and place it in array. I don't want to create another behavior because only this element will be using the extra code.
With the element and the extended behavior needed, how can I add hidePrintButton and to the properties and overwrite function fullDisplayeMode?
custom element:
<script>
Polymer({
is: "resume-page",
properties: {
hidePrintButton: {
type: Boolean,
reflectToAttribute: true,
value: true
}
},
behaviors: [pageBehavior],
fullDisplayMode: function() {
this.show = true;
this.hidePrintButton = false;
this._toggleStyles();
this.nextElementSibling.show = true;
}
});
</script>
the page behavior:
<script>
pageBehavior = {
properties: {
hideRipple: {
type: Boolean,
value: false
},
fullDisplay: {
type: Boolean,
value: false
},
show: {
type: Boolean,
reflectToAttribute: true,
value: true
}
},
_mediaQuery: function(section) {
if (window.matchMedia( "(min-width: 1200px)" )) {
section.style.width = "90%";
} else {
section.style.width ="90%";
}
},
_toggleWidth: function(section, fullDisplay) {
if (fullDisplay) {
section.style.width = "100%";
} else {
this._mediaQuery(section);
}
},
_toggleHover: function(section, fullDisplay) {
if (fullDisplay) {
section.classList.remove('enabled-hover');
} else {
section.classList.add('enabled-hover');
}
},
_toggleRipple: function(fullDisplay) {
//This is necessary because if page ripple
//is hidden to quick the animation doesn't finish
if (fullDisplay) {
setTimeout(function() {
this.hideRipple = true;
}.bind(this), 700);
} else {
this.hideRipple = false;
}
},
_toggleStyles: function(fullDisplay) {
var section = this.firstElementChild;
this._toggleRipple(fullDisplay);
this._toggleWidth(section, fullDisplay);
this._toggleHover(section, fullDisplay);
},
fullDisplayMode: function() {
this._toggleStyles(true);
this.show = true;
this.nextElementSibling.show = true;
},
homeMode: function() {
this._toggleStyles(false);
this.show = true;
this.nextElementSibling.show = false;
},
disappearMode: function() {
this.show = false;
this.nextElementSibling.show = false;
}
}
</script>
A behavior method cannot be extended. It can only be overwritten. However you could still abstract the shared logic in the behavior and have some empty methods on the behavior for customization purposes.
E.g
//In your behavior
fullDisplayMode: function() {
this.show = true;
this._toggleStyles();
this.nextElementSibling.show = true;
this.prepareFullDisplayMode();
},
prepareFullDisplayMode:function(){
//Empty inside behavior
//Elements could opt to implement it with additional logic
}
Using this pattern, one of your custom elements could add additional logic by implementing the 'prepareFullDisplayMode' while the other would not need to.
I don't know since when we can do this, but we CAN extend behaviors:
https://www.polymer-project.org/1.0/docs/devguide/behaviors#extending
I'm going to use as an example the Polymer.AppLocalizeBehavior from app-localize-behavior to set the default language.
1) Namespace your behavior, so they don't collide with others:
var MyNamespace = MyNamespace|| {};
2) Write the behavior's implementation:
MyNamespace.LocalizeImpl = {
ready() {
},
attached: function() {
this.loadResources(this.resolveUrl('../../../locales.json'));
},
properties: {
language : {
value : "en"
}
},
};
3) Add the implementation to the new behavior in an array.
MyNamespace.Localize = [Polymer.AppLocalizeBehavior, MyNamespaceLocalize.Impl]
All together:
var MyNamespace = MyNamespace || {};
MyNamespace.Localize = {
ready() {
},
attached: function() {
this.loadResources(this.resolveUrl('../../../locales.json'));
},
properties: {
language : {
value : "en"
}
},
};
MyNamespace.LocalizeBehavior = [Polymer.AppLocalizeBehavior, MyNamespace.Localize]
Then, in your element, include it like this:
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../path-to-custom-behavior/mynamespace-localize-behavior/mynamespace-localize-behavior.html">
<dom-module id="my-element">
<style is="custom-style"></style>
<template is="dom-bind">
<template is="dom-if" if="{{query}}">
<h1> {{localize("EmailActivationSuccessful")}}</h1>
</template>
<template is="dom-if" if="{{!query}}">
<h1> {{localize("EmailCodeExpired")}}</h1>
</template>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'my-element',
behaviors: [MyNamespace.LocalizeBehavior],
});
})();
</script>
Now, as you can see I've only included the MyNamespace.LocalizeBehavior and started using all the methods and functions from "Polymer.AppLocalizeBehavior"
This is a great way for setting the default language and only handling the language logic in a single element.
Explanation and notes:
All the properties, methods, functions that match the previous
behavior are overwritten. In this case, I overwrote the
"language" property from "Polymer.AppLocalizeBehavior".
Remember include the .html file where the old behavior is located only where you are extending the behavior. Afterwards you only include your custom behavior wherever and whenever you want.
In point #3, the array works like this: the first element is the behavior to extend/overwrite, and the second one is your implementation, or the extended behavior.

Displaying nested JSON with angularjs

I am trying to display nested JSON in a page. I'm not sure how to drill down into it.
In my app js file I have an parameter called initialData that I want to call a function getProducts() when the view is called...
'use strict';
var quoteApp = angular.module('quoteApp', ['ui.router']);
quoteApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
$stateProvider
// HOME STATES AND NESTED VIEWS ========================================
.state('home', {
url: '/home',
templateUrl: 'ng-views/choose.html',
controller: "quoteBuilderController",
resolve: {
initialData: ['quoteApi', function (quoteApi) {
return quoteApi.getProducts();
}]
}
})
});
my quoteApi looks like this in case you want to see it...
(function () {
'use strict';
angular.module('quoteApp').factory('quoteApi', quoteApi);
quoteApi.$inject = ['$http'];
function quoteApi($http) {
var service = {
getProducts: getProducts,
getPrices: getPrices
};
var baseUrl = 'http://www.website.com/api/Pricing';
return service;
function getProducts() {
return httpGet('/GetProductCatalogue');
}
function getPrices() {
return httpGet('/GetPrices');
}
/** Private Methods **/
function httpExecute(requestUrl, method, data){
return $http({
url: baseUrl + requestUrl,
method: method,
data: data,
headers: requestConfig.headers }).then(function(response){
return response.data;
});
}
function httpGet(url){
return httpExecute(url, 'GET');
}
}
})();
So quoteApi.getProducts() returns JSON that looks like this...
{
"Cat1": [
{
"product_id": 1,
"product_name": "Prod1"
},
{
"product_id": 2,
"product_name": "Prod2"
}
],
"Cat2": [
{
...
}
]
}
My controller for the view looks like this...
(function () {
'use strict';
angular.module('quoteApp').controller('quoteController', ['$scope', '$http', '$timeout', quoteController]);
quoteController.$inject = ['initialData', 'quoteApi'];
function quoteController($scope, initialData) {
$scope.cat1Products = initialData;
};
})();
So my question is, how can I get 'initialData' to load products from Cat1 only? Should I try to do this from the html? It seems like it should be straight forward enough but I can seem to get it. Thank you.
You need to transform your response from your http request further so you only return the piece you require, and you may also want to consider using the .then() approach:
$http.get('/someUrl').then(function(response) {
//Do something with response.data.Cat1 here
}, function(errResponse) {
console.error('Error while fetching data');
});
Just take out cat1 from your initialData object
function quoteController($scope, initialData) {
$scope.cat1Products = initialData['Cat1'];
};