Consider a following simple example, file country.gs
class Country { }
and file file subcountry.gs
class SubCountry extends Country{ }
function test(){}
Trying to run test() I get
ReferenceError: Country is not defined
If I join files or change loading order, it works fine.
Apparently, I don't want to be dependent on file load order, also clasp changes on push(sorting alphabetically), so it's definitely not a good way to rename files in order they should be compiled.
Is there an appropriate solution for this?
Example:
https://script.google.com/d/1Pipt3YN1FBGkbRRT2PyCHhugd-Xrv3zctIWYwX-cGnAjXfDckwOk7bJh/edit?usp=sharing
As written in the documentation,
This arrangement is identical to how browsers handle multiple tags in one HTML file.
Each file is like a new <script>file content </script> tag and they're added in the order they appear in Apps script editor. This is a problem only when you're using global variables. It's explicitly discouraged to use global variables.
Caution: It's not best practice to rely on a specific file parse order to avoid this issue. The sequence of script file parsing can change if script files are copied, removed, renamed, or otherwise rearranged. It's better to remove any global variable dependency on function calls if possible.
Classes are infact "special functions". You can always enclose the Class in a local scope and call, when needed as recommended in the documentation.
Snippet:
Just moving the calling function to local scope should work
/*subcountry.gs*/
function test(){
/*local scope*/class SubCountry extends Country{ }
}
To avoid declaring class in global scope as well:
/*country.gs*/
var Country;
function main(){
if (Country == undefined) Country = class Country { }
return Country;
}
/*subcountry.gs*/
function test(){
/*initialize class Country*/main()
/*local scope*/class SubCountry extends Country{ }
}
Building off the answer posted by TheMaster and the Bruce Mcpherson article shared by Alan Wells, you could try implementing your own require() function.
/* Code.gs */
function test() {
const SubCountry = require("SubCountry");
const x = new SubCountry();
}
/* SubCountry.gs */
function SubCountry() {
const Country = require("Country");
return class SubCountry extends Country {};
}
/* Country.gs */
function Country() {
return class Country {};
}
/* Require.gs */
function require(moduleName) {
const modules = {
Country: Country,
SubCountry: SubCountry,
};
return modules[moduleName]();
}
Alternatively, you could apply a more direct approach without the use of require(), but I find this to be slightly less intuitive.
/* Code.gs */
function test() {
const x = new (SubCountryClass())();
}
/* SubCountry.gs */
function SubCountryClass() {
return class SubCountry extends CountryClass() {};
}
/* Country.gs */
function CountryClass() {
return class Country {};
}
All files above, for both approaches, are intentionally presented and loaded in an order that would cause a ReferenceError if declaring the classes globally. So this should be fully independent of load order.
I'll probably go with one of solutions described here
TypeScript classes order in Google AppScript Project
using clasp and it's filePushOrder option
{
"scriptId":"1Pipt3YN1FBGkbRRT2PyCHhugd-Xrv3zctIWYwX-cGnAjXfDckwOk7bJh",
"filePushOrder": [
"country.gs",
"subcountry.gs"
]
}
Author example
https://github.com/PopGoesTheWza/clasp-filePushOrder
I enforces me to use clasp, but at least it's easy to maintain.
I've extended an element with
const PaperSliderClass = customElements.get('paper-slider');
class MyPaperSlider extends PaperSliderClass {
...
But how do I tell the polymer linter/build about it?
I tried to add the following right before my class declaration, but it didn't work.
/**
* #polymer
* #customElement
* #extends {PaperSliderClass}
*/
apparently I could just do:
#appliesMixin paper-slider
I want to navigate from one page to another and jump directly to one control in the middle of the destination page. Normally, we can use a hashtag "#" follow with the name or ID of the control.
However, this method does work that well in Angular 2. I have tried method like
Angular2 Routing with Hashtag to page anchor. With that method, the url does end with "#myId" but the page doesn't actually jump.
Do we have other ways to achive this?
Angular documentation project has AutoScrollService service. You can try to use it. auto-scroll.service.ts
/**
* A service that supports automatically scrolling elements into view
*/
#Injectable()
export class AutoScrollService {
constructor(
#Inject(DOCUMENT) private document: any,
private location: PlatformLocation) { }
/**
* Scroll to the element with id extracted from the current location hash fragment
* Scroll to top if no hash
* Don't scroll if hash not found
*/
scroll() {
const hash = this.getCurrentHash();
const element: HTMLElement = hash
? this.document.getElementById(hash)
: this.document.getElementById('top-of-page') || this.document.body;
if (element) {
element.scrollIntoView();
if (window && window.scrollBy) { window.scrollBy(0, -80); }
}
}
/**
* We can get the hash fragment from the `PlatformLocation` but
* it needs the `#` char removing from the front.
*/
private getCurrentHash() {
return this.location.hash.replace(/^#/, '');
}
}
$(document).ready(function() {
var apiRevoSlider = $('.tp-banner').show().revolution(
{
dottedOverlay:"none",
delay:9000,
startwidth:1140,
startheight:700,
hideThumbs:200,
thumbWidth:100,
thumbHeight:50,
thumbAmount:3,
navigationType:"none",
navigationArrows:"solo",
navigationStyle:"preview1",
touchenabled:"on",
onHoverStop:"on",
swipe_velocity: 0.7,
swipe_min_touches: 1,
swipe_max_touches: 1,
drag_block_vertical: false,
parallax:"mouse",
parallaxBgFreeze:"on",
parallaxLevels:[8,7,6,5,4,3,2,1],
parallaxDisableOnMobile:"on",
keyboardNavigation:"on",
navigationHAlign:"center",
navigationVAlign:"bottom",
navigationHOffset:0,
navigationVOffset:20,
soloArrowLeftHalign:"left",
soloArrowLeftValign:"center",
soloArrowLeftHOffset:20,
soloArrowLeftVOffset:0,
soloArrowRightHalign:"right",
soloArrowRightValign:"center",
soloArrowRightHOffset:20,
soloArrowRightVOffset:0,
shadow:0,
fullWidth:"off",
fullScreen:"on",
spinner:"spinner3",
stopLoop:"off",
stopAfterLoops:-1,
stopAtSlide:-1,
shuffle:"off",
forceFullWidth:"off",
fullScreenAlignForce:"off",
minFullScreenHeight:"400",
hideThumbsOnMobile:"off",
hideNavDelayOnMobile:1500,
hideBulletsOnMobile:"off",
hideArrowsOnMobile:"off",
hideThumbsUnderResolution:0,
hideSliderAtLimit:0,
hideCaptionAtLimit:0,
hideAllCaptionAtLilmit:0,
startWithSlide:0,
fullScreenOffsetContainer: ".header"
});
apiRevoSlider.bind("revolution.slide.onchange",function (e,data) {
if( $(window).width() > 992 ) {
if( $('#slider ul > li').eq(data.slideIndex-1).hasClass('light') ){
$('#header:not(.sticky-header)').addClass('light');
} else {
$('#header:not(.sticky-header)').removeClass('light');
}
MINOVATE.header.chooseLogo();
}
});
}); //ready
That may help you, btw if you had searched a bit you would have found it
/**
* Sample Directive
*/
define(['sampleLink', 'sampleCtrl'], function (link, controller) {
function sampleDir () {
return {
/**
* require is used by the link option
* can be a single controller, or an array. '^myController', or ['^controller2', '^controller1']
* controllers to be used in the link section. link (scope, element, attrs, requires)
*
* '^myController' - use ^ to search up the parents for the controller, otherwise it only looks at it's own element and will throw an error
* */
require : '',
/* restrict what type of elements this directive is rendered on E=element <directive> or A=attribute <element data-directive> */
restrict : 'EA',
/* *
* transclude in or outer scope. true = use the outer scope that this directive belongs to. false = create a scope inside the directive
* only use true when you want to create a directive that wraps arbitrary content
* when you use false and create a scope, you will want to pass in scope, or data through attributes on the element
* */
transclude : false,
/* *
* scope = create an isolate scope for the directive pass data into the directive through attributes
* This allows us to isolate the scope and reuse these directives throughout an app.
* =attr means to bind data to that attribute
* &attr means with transclude turned on this attribute will trigger evaluation of an expression in the context of the original scope
* any expression is allowed, including a function, this is ideal for binding callback functions to directive behaviors
* use this when we want the directive to expose an API for binding behaviors
* */
scope : {
inputObject : '=inputObject',
/*'close' : '&onClose'*/
},
/* templateUrl = Point to the template this directive should render */
templateUrl : '/a/tpl/modules/util/input/inputs.html',
/**
* link : use link when you want to manipulate the dom with the directive
* takes a function, inline or it can be dependency injected through requireJS
* use the following signature function lin k(scope, element, attrs) { ... }
*
* scope is an angular scope object
* element is the jqLite wrapped element that this directive matches
* attrs is a hash object with key-value pairs of normalized attribute names and their values
* */
link : link,
/**
* Specify a controller, and use controllerAs to alias the scope, if you want to reference the scope or it's functions in the template.
* You must define scope in the directive for this to be used.
*
* Q: What's the difference between controller and link? - A: Controllers can expose an API, and link can interact with controllers using require
*
* Best Practice: Only use controller when you want to expose the directive controller to another directive. Otherwise use Link
*
* This is great for building complicated directives that are made up of other directives and need to communicate with one another.
* */
controller : controller
/*controllerAs : 'input'*/
}
}
return sampleDir;
});
Is there a way to ask phpStorm to update the contents of a docblock? eg if I have the following code
//-------------------------------------------------------------------------
/**
* #param string $url
* #return $this
*/
public function setBaseUrl($url)
{
$this->baseUrl = $url;
return $this;
}
and add another parameter
//-------------------------------------------------------------------------
/**
* #param string $url
* #return $this
*/
public function setBaseUrl($url, $anotherParameter)
{
$this->baseUrl = $url;
return $this;
}
is there a way to ask phpStorm to create the #param $anotherParameter in my docblock? (in a single keystroke or by menu selection)?
Alt+Enter (Show Intention Actions) on the comment, then Enter again.
This is configurable via [Settings > Keymap] then [Other > Show Intention Actions]
Alternatively you can do the same with mouse if you click on the comment, and then on the yellow bulb that shows up.
I used to press Control-Enter inside the dock block and it used to update.
And for some reason it stopped working.
Finally I figured that PHPStorm has changed its behavior.
Now you need to put the cursor on the missing variable name and then press Control-Enter. It will update the dock block.
And of course make sure that phpDoc inspection is enabled as Steve has mentioned in the comment. Also have a read of http://blog.jetbrains.com/webide/2011/05/phpdoc-inspections/
In the new version of PHPStorm 2016.x you need to place your cursor in the missing variable name and press alt + enter then hit again enter to add the missing param to the doc block. If you need to remove a parameter you need to go to the extra param and press the same key strokes.