I would like to send constructors via EJSON through methods:
server.js
Meteor.methods({
'testConstructor' (doc) {
console.log(doc) // {}
}
})
client.js
Meteor.call({ type: String })
I thought of adding the types via EJSON.addType but it currently only supports instances and not constructors. I tried to wrap the String constructor inside a class like this:
Meteor.startup(function () {
class StrWrap {
constructor (val) {
this.val = val
}
toJSONValue () {
return {
value: this.val
}
}
typeName () {
return String.name
}
}
EJSON.addType(String, function (json) {
return String
})
const str = EJSON.stringify({ type: String})
console.log(str) // {}
})
Still no chance. In a related issue it has been mentioned, that the EJSON supports String, Number etc. but I think that was targeting the instances of these classes.
I currently work this around using native JSON replacer for JSON.stringify and resolver for JSON.parse but this adds a full conversion layer to every DDP protocol interaction and I'd like to support constructors out of the box, so I can send schemas around for service discovery.
Is there a difference between these two function declarations in an es6 class? Is the scope this same (this)? Is one way preferred?
class Node {
// function declaration 1
test () {
}
// function declaration 2
test = () => {
}
}
The second way creates an instance function
class Node {
test = () => {
}
}
is the same as
class Node {
construtor() {
this.test = () => {
};
}
}
So effectively it is created a new function, bound to the new object's instance, every time an object is created. It's just a shorter way to say it.
The advantage is you can pass those methods to a callback. Example
class Node {
constructor(name) {
this.name = name;
}
test1 = () => {
console.log(this.name);
}
test2() {
console.log(this.name);
}
}
const node = new Node('foo');
setTimeout(node.test1); // works
setTimeout(node.test2); // fails will have wrong this
setTimeout(node.test2.bind(node)); // works
Note that when to use an arrow function on a method and when not to is really up to the situation
const node = new Node('foo');
someElement.addEventListener(click, node.test1);
In the case above when the event listener is called this will reference node. If that's what you want great but event listeners have this set to the element they're attached to, in this case someElement so you lose that. If you didn't need it great. If you did need it then an arrow function was the wrong thing to use.
I have a Polymer element of which the public API should allow to bind to a function which the user can define, i.e. it should allow to pass in a function implementation. I have tried quite a few approaches but only 1 worked. Now I'm wondering if this is the proper / correct way or not.
To rephrase: what is the proper way to bind a function as part of a dom-module's public API? The only way I have this achieved somewhat is as follows:
<dom-module id="channel-search">
<template>
<remote-dropdown
id="dropdown"
label-text="Type channel name"
url='{{_findRecordUrl}}'
url-transformer='{{urlTransformer}}'
result-to-list-transformer='{{resultToListTransformer}}'
class="layout horizontal"
style='width: 100%'>
</remote-dropdown>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'channel-search',
properties: {
_findRecordUrl: {
type: String,
value: 'http://127.0.0.1:9292/epics-boot-info.psi.ch/find-channel.aspx'
}
},
/*
* Here in the attached function, I define the methods which will then be bound to
* the respective properties of the remote-dropdown element
*/
attached: function() {
this.urlTransformer = function(baseUrl, currentInput) {
return baseUrl + '/' + currentInput;
};
this.resultToListTransformer = function(findRecordList) {
var responseList = findRecordList.map(function(res) {
return res.Channel;
});
return responseList;
};
}
});
})();
</script>
So, I needed to define the functions in the attached callback in order for them to be properly bound to the remote-dropdown element's public API.
I hoped it would be a bit clearer / easier, maybe like so:
<script>
(function() {
Polymer({
is: 'channel-search',
properties: {
_findRecordUrl: {
type: String,
value: 'http://127.0.0.1:9292/find-channel.aspx'
}
},
urlTransformer: function(baseUrl, currentInput) {
return baseUrl + '/' + currentInput;
};
resultToListTransformer: function(findRecordList) {
var responseList = findRecordList.map(function(res) {
return res.Channel;
});
return responseList;
};
});
})();
</script>
i.e. simply define the function implementation as part of the element's definition and then bind those to the embedded remote-dropdown element. However, that never seemed to work as I thought it would (also not variations thereof) - surely also because of my limited knowledge of Polymer/Javascript internals.
My question is: is the solution using the attached callback the proper way to achieve what I am trying to do? If not, what would be the correct way to implement this?
You can probably do that using computed properties or computed bindings. Your first answer looks like a variation of the latter.
Issue:
I have a lot of small helper functions that don't necessarily need to live in a component(or maybe they can but they will make that component bloated with a lot of code).My lazy side just wants to just let those all just be some sort of global functions that the components can call.I really want to make good ReactJs code.
Question:
What are the best practices in terms of global helper functions in Reactjs? Should I force them into some sort of component or just shove them into the other components?
Basic Example:
function helperfunction1(a, b) {
//does some work
return someValue;
}
function helperfunction2(c, d) {
//does some work
return someOtherValue;
}
function helperfunction3(e, f) {
//does some work
return anotherValue;
}
function helperfunction4(a, c) {
//does some work
return someValueAgain;
}
var SomeComponent =
React.createClass({
//Has bunch of methods
//Uses some helper functions
render: function () {
}
});
var SomeOtherComponent =
React.createClass({
//Has bunch of methods
//Uses some helper functions
render: function () {
}
});
You can export multiple functions from a file, no React needed per se:
Helpers.js:
export function plus(a, b) {
return a + b;
}
export function minus(a, b) {
return a - b;
}
export function multiply(a, b) {
return a * b;
}
export function divide(a, b) {
return a / b;
}
You can then import the functions you need:
import { multiply, divide } from './Helpers'
You can use a module-bundling tool like Webpack or Browserify for that.
Put your reusable functions in a CommonJS module.
Do not use Mixins, they will probably be deprecated in next versions of React as there's no standard way to declare mixins in React with ES6 syntax and they prefer to wait for ES7 that will probably standardize mixins. And there's no point coupling your reusable code to React unless it uses React lifecycle's methods.
You can use modulejs.
or you can use mixins (https://facebook.github.io/react/docs/reusable-components.html#mixins)
Sample for mixins: https://jsfiddle.net/q88yzups/1/
var MyCommonFunc = {
helperFunction1: function() {
alert('herper function1');
},
doSomething: function(){
alert('dosomething');
}
}
var Hello = React.createClass({
mixins: [MyCommonFunc],
render: function() {
this.doSomething();
return <div onClick={this.helperFunction1}>Hello {this.props.name} </div>;
}
});
React.render(<Hello name="World" />, document.getElementById('container'));
Just another option, if you don't want to split into a separate module, you could create a private method in your parent component like below and use freely within this component or pass to the child components via props..
var YourComponent = React.createClass({
globalConfig: function() {
return {
testFunc: function () {
console.log('testing...');
},
};
}(),
......
render: function() {
this.globalConfig.testFunc(); // use directly
<ChildComponent testFunc={this.globalConfig.testFunc} /> // pass to child
.....
All untested, but that's the idea...
Use a React context to do something like this. It's built for this exact use case;
Doc: https://reactjs.org/docs/context.html
react can be avoided altogether, like Michiel says
though a slight improvement would be to put all those pure js functions in a single fle then connect it to your html start page and the functions will be available everywhere:
just with a
<script src='./global-func.js' ></script>
its a dirty hack but it works ;)
you wont have to import the file into every component class you make
I want to create a directive that links to an attribute. The attribute specifies the function that should be called on the scope. But I also want to pass an argument to the function that is determined inside the link function.
<div my-method='theMethodToBeCalled'></div>
In the link function I bind to a jQuery event, which passes an argument I need to pass to the function:
app.directive("myMethod",function($parse) {
restrict:'A',
link:function(scope,element,attrs) {
var expressionHandler = $parse(attrs.myMethod);
$(element).on('theEvent',function( e, rowid ) {
id = // some function called to determine id based on rowid
scope.$apply(function() {expressionHandler(id);});
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}
Without passing the id I can get it working, but as soon as I try to pass an argument, the function is not called anymore
Marko's solution works well.
To contrast with recommended Angular way (as shown by treeface's plunkr) is to use a callback expression which does not require defining the expressionHandler. In marko's example change:
In template
<div my-method="theMethodToBeCalled(myParam)"></div>
In directive link function
$(element).click(function( e, rowid ) {
scope.method({myParam: id});
});
This does have one disadvantage compared to marko's solution - on first load theMethodToBeCalled function will be invoked with myParam === undefined.
A working exampe can be found at #treeface Plunker
Just to add some info to the other answers - using & is a good way if you need an isolated scope.
The main downside of marko's solution is that it forces you to create an isolated scope on an element, but you can only have one of those on an element (otherwise you'll run into an angular error: Multiple directives [directive1, directive2] asking for isolated scope)
This means you :
can't use it on an element hat has an isolated scope itself
can't use two directives with this solution on the same element
Since the original question uses a directive with restrict:'A' both situations might arise quite often in bigger applications, and using an isolated scope here is not a good practice and also unnecessary. In fact rekna had a good intuition in this case, and almost had it working, the only thing he was doing wrong was calling the $parsed function wrong (see what it returns here: https://docs.angularjs.org/api/ng/service/$parse ).
TL;DR; Fixed question code
<div my-method='theMethodToBeCalled(id)'></div>
and the code
app.directive("myMethod",function($parse) {
restrict:'A',
link:function(scope,element,attrs) {
// here you can parse any attribute (so this could as well be,
// myDirectiveCallback or multiple ones if you need them )
var expressionHandler = $parse(attrs.myMethod);
$(element).on('theEvent',function( e, rowid ) {
calculatedId = // some function called to determine id based on rowid
// HERE: call the parsed function correctly (with scope AND params object)
expressionHandler(scope, {id:calculatedId});
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}
Not knowing exactly what you want to do... but still here's a possible solution.
Create a scope with a '&'-property in the local scope.
It "provides a way to execute an expression in the context of the parent scope" (see the directive documentation for details).
I also noticed that you used a shorthand linking function and shoved in object attributes in there. You can't do that. It is more clear (imho) to just return the directive-definition object. See my code below.
Here's a code sample and a fiddle.
<div ng-app="myApp">
<div ng-controller="myController">
<div my-method='theMethodToBeCalled'>Click me</div>
</div>
</div>
<script>
var app = angular.module('myApp',[]);
app.directive("myMethod",function($parse) {
var directiveDefinitionObject = {
restrict: 'A',
scope: { method:'&myMethod' },
link: function(scope,element,attrs) {
var expressionHandler = scope.method();
var id = "123";
$(element).click(function( e, rowid ) {
expressionHandler(id);
});
}
};
return directiveDefinitionObject;
});
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) {
alert(id);
};
});
</script>
You can create a directive that executes a function call with params by using the attrName: "&" to reference the expression in the outer scope.
We want to replace the ng-click directive with ng-click-x:
<button ng-click-x="add(a,b)">Add</button>
If we had this scope:
$scope.a = 2;
$scope.b = 2;
$scope.add = function (a, b) {
$scope.result = parseFloat(a) + parseFloat(b);
}
We could write our directive like so:
angular.module("ng-click-x", [])
.directive('ngClickX', [function () {
return {
scope: {
// Reference the outer scope
fn: "&ngClickX",
},
restrict: "A",
link: function(scope, elem) {
function callFn () {
scope.$apply(scope.fn());
}
elem[0].addEventListener('click', callFn);
}
};
}]);
Here is a live demo:
http://plnkr.co/edit/4QOGLD?p=info
Here's what worked for me.
Html using the directive
<tr orderitemdirective remove="vm.removeOrderItem(orderItem)" order-item="orderitem"></tr>
Html of the directive: orderitem.directive.html
<md-button type="submit" ng-click="remove({orderItem:orderItem})">
(...)
</md-button>
Directive's scope:
scope: {
orderItem: '=',
remove: "&",
My solution:
on polymer raise an event (eg. complete)
define a directive linking the event to control function
Directive
/*global define */
define(['angular', './my-module'], function(angular, directives) {
'use strict';
directives.directive('polimerBinding', ['$compile', function($compile) {
return {
restrict: 'A',
scope: {
method:'&polimerBinding'
},
link : function(scope, element, attrs) {
var el = element[0];
var expressionHandler = scope.method();
var siemEvent = attrs['polimerEvent'];
if (!siemEvent) {
siemEvent = 'complete';
}
el.addEventListener(siemEvent, function (e, options) {
expressionHandler(e.detail);
})
}
};
}]);
});
Polymer component
<dom-module id="search">
<template>
<h3>Search</h3>
<div class="input-group">
<textarea placeholder="search by expression (eg. temperature>100)"
rows="10" cols="100" value="{{text::input}}"></textarea>
<p>
<button id="button" class="btn input-group__addon">Search</button>
</p>
</div>
</template>
<script>
Polymer({
is: 'search',
properties: {
text: {
type: String,
notify: true
},
},
regularSearch: function(e) {
console.log(this.range);
this.fire('complete', {'text': this.text});
},
listeners: {
'button.click': 'regularSearch',
}
});
</script>
</dom-module>
Page
<search id="search" polimer-binding="searchData"
siem-event="complete" range="{{range}}"></siem-search>
searchData is the control function
$scope.searchData = function(searchObject) {
alert('searchData '+ searchObject.text + ' ' + searchObject.range);
}
This should work.
<div my-method='theMethodToBeCalled'></div>
app.directive("myMethod",function($parse) {
restrict:'A',
scope: {theMethodToBeCalled: "="}
link:function(scope,element,attrs) {
$(element).on('theEvent',function( e, rowid ) {
id = // some function called to determine id based on rowid
scope.theMethodToBeCalled(id);
}
}
}
app.controller("myController",function($scope) {
$scope.theMethodToBeCalled = function(id) { alert(id); };
}