Is the following definition of defaults bad practice?
var defaults = {
name:'',
surname:'',
address:{
part1:'',
part2:'',
postCode:''
}
};
If only part1 in the address object is defined by the plugin initiator the rest of address will be undefined.
What would be the best way to handle this? Or should I not use complex objects as options at all?
Ok, so it was just a case of wording in my searches before I asked the question.
I didn't know that jQuery.extend() can extend recursively by using
this.opts = $.extend(true,{}, defaults, options);
Instead of
this.opts = $.extend({}, defaults, options);
Related
I need to check for fullscreen support with my Go WASM Canvas project, before switching to fullscreen mode. I have the following code so far:
var fullscreenFunc js.Value
var fullscreenNotSupported bool
set with the following logic:
fullscreenFunc = app.Get("requestFullscreen")
if fullscreenFunc.IsUndefined() {
fullscreenFunc = app.Get("mozRequestFullScreen")
if fullscreenFunc.IsUndefined() {
fullscreenFunc = app.Get("webkitRequestFullscreen")
if fullscreenFunc.IsUndefined() {
fullscreenFunc = app.Get("msRequestFullscreen")
if fullscreenFunc.IsUndefined() {
fullscreenNotSupported = true
println("Fullscreen not supported")
}
}
}
}
I was expecting to be able to call the correct function with js.Invoke, but I see no way to tell the Invoke upon which object the call should be made. My 'app' value is being interpreted just as a param.
func Fullscreen(app js.Value) {
if fullscreenNotSupported {
return
}
fullscreenFunc.Invoke(app)
}
resulting in:
panic: JavaScript error: 'mozRequestFullScreen' called on an object that does not implement interface Element.
So am I correct in my thinking that the only way I can call the correct method, is not to store the Function, but to store a string of the function name, and then 'invoke' / 'call' it using the following approach?
app.Call(fullscreenFunctionNameString)
It feels like I misunderstood the purpose of Invoke. Is it only for js.Global() type calls?
[edit] Using 'Call', at least it seems possible to derive the function name without having to repeat the above browser specifics:
fullscreenFunctionName = fullscreenFunc.Get("name").String()
app.Call(fullscreenFunctionNameString)
It doesn't answer the question, but is probably of help to someone trying to do the same.
The arguments to invoke get turned into arguments for the javascript function it wraps. Since those fullscreen functions don't need any arguments, I think you might just need to change:
fullscreenFunc.Invoke(app)
To:
fullscreenFunc.Invoke()
...assuming app is the same JS element in both places. If not your Call solution is probably your best bet.
I am wondering what is the use of asObservable:
As per docs:
An observable sequence that hides the identity of the
source sequence.
But why would you need to hide the sequence?
When to use Subject.prototype.asObservable()
The purpose of this is to prevent leaking the "observer side" of the Subject out of an API. Basically to prevent a leaky abstraction when you don't want people to be able to "next" into the resulting observable.
Example
(NOTE: This really isn't how you should make a data source like this into an Observable, instead you should use the new Observable constructor, See below).
const myAPI = {
getData: () => {
const subject = new Subject();
const source = new SomeWeirdDataSource();
source.onMessage = (data) => subject.next({ type: 'message', data });
source.onOtherMessage = (data) => subject.next({ type: 'othermessage', data });
return subject.asObservable();
}
};
Now when someone gets the observable result from myAPI.getData() they can't next values in to the result:
const result = myAPI.getData();
result.next('LOL hax!'); // throws an error because `next` doesn't exist
You should usually be using new Observable(), though
In the example above, we're probably creating something we didn't mean to. For one, getData() isn't lazy like most observables, it's going to create the underlying data source SomeWeirdDataSource (and presumably some side effects) immediately. This also means if you retry or repeat the resulting observable, it's not going to work like you think it will.
It's better to encapsulate the creation of your data source within your observable like so:
const myAPI = {
getData: () => return new Observable(subscriber => {
const source = new SomeWeirdDataSource();
source.onMessage = (data) => subscriber.next({ type: 'message', data });
source.onOtherMessage = (data) => subscriber.next({ type: 'othermessage', data });
return () => {
// Even better, now we can tear down the data source for cancellation!
source.destroy();
};
});
}
With the code above, any behavior, including making it "not lazy" can be composed on top of the observable using RxJS's existing operators.
A Subject can act both as an observer and an observable.
An Obervable has 2 methods.
subscribe
unsubscribe
Whenever you subscribe to an observable, you get an observer which has next, error and complete methods on it.
You'd need to hide the sequence because you don't want the stream source to be publicly available in every component. You can refer to #BenLesh's example, for the same.
P.S. : When I first-time came through Reactive Javascript, I was not able to understand asObservable. Because I had to make sure I understand the basics clearly and then go for asObservable. :)
In addition to this answer I would mention that in my opinion it depends on the language in use.
For untyped (or weakly typed) languages like JavaScript it might make sense to conceal the source object from the caller by creating a delegate object like asObservable() method does. Although if you think about it it won't prevent a caller from doing observable.source.next(...). So this technique doesn't prevent the Subject API from leaking, but it indeed makes it more hidden form the caller.
On the other hand, for strongly typed languages like TypeScript the method asObservable() doesn't seem to make much sense (if any).
Statically typed languages solve the API leakage problem by simply utilizing the type system (e.g. interfaces). For example, if your getData() method is defined as returning Observable<T> then you can safely return the original Subject, and the caller will get a compilation error if attempting to call getData().next() on it.
Think about this modified example:
let myAPI: { getData: () => Observable<any> }
myAPI = {
getData: () => {
const subject = new Subject()
// ... stuff ...
return subject
}
}
myAPI.getData().next() // <--- error TS2339: Property 'next' does not exist on type 'Observable<any>'
Of course, since it all compiles to JavaScript in the end of the day there might still be cases when you want to create a delegate. But my point is that the room for those cases is much smaller then when using vanilla JavaScript , and probably in majority of cases you don't need that method.
(Typescript Only) Use Types Instead of asObservable()
I like what Alex Vayda is saying about using types instead, so I'm going to add some additional information to clarify.
If you use asObservable(), then you are running the following code.
/**
* Creates a new Observable with this Subject as the source. You can do this
* to create customize Observer-side logic of the Subject and conceal it from
* code that uses the Observable.
* #return {Observable} Observable that the Subject casts to
*/
asObservable(): Observable<T> {
const observable = new Observable<T>();
(<any>observable).source = this;
return observable;
}
This is useful for Javascript, but not needed in Typescript. I'll explain why below.
Example
export class ExampleViewModel {
// I don't want the outside codeworld to be able to set this INPUT
// so I'm going to make it private. This means it's scoped to this class
// and only this class can set it.
private _exampleData = new BehaviorSubject<ExampleData>(null);
// I do however want the outside codeworld to be able to listen to
// this data source as an OUTPUT. Therefore, I make it public so that
// any code that has reference to this class can listen to this data
// source, but can't write to it because of a type cast.
// So I write this
public exampleData$ = this._exampleData as Observable<ExampleData>;
// and not this
// public exampleData$ = this._exampleData.asObservable();
}
Both do the samething, but one doesn't add additional code calls or memory allocation to your program.
❌this._exampleData.asObservable();❌
Requires additional memory allocation and computation at runtime.
✅this._exampleData as Observable<ExampleData>;✅
Handled by the type system and will NOT add additional code or memory allocation at runtime.
Conclusion
If your colleagues try this, referenceToExampleViewModel.exampleData$.next(new ExampleData());, then it will be caught at compile time and the type system won't let them, because exampleData$ has been casted to Observable<ExampleData> and is no longer of type BehaviorSubject<ExampleData>, but they will be able to listen (.subscribe()) to that source of data or extend it (.pipe()).
This is useful when you only want a particular service or class to be setting the source of information. It also helps with separating the input from the output, which makes debugging easier.
Quoted from Marionette.Behavior documentation
Finally, the user must define a location for where their behaviors are stored.
A simple example of this would look like this:
Marionette.Behaviors.behaviorsLookup = function() {
return window.Behaviors;
}
But window.Behaviors is undefined. When I use window everything is good. Do I miss something?
It's undefined because you probably haven't defined it yet. You would create an object window.Behaviors = {} that would be attached to the window when the app starts. From there you could register behaviors off of that and reference window.Behaviors like so,
window.Behaviors.ExampleBehavior = Marionette.Behavior.extend({
defaults: {},
events: {},
//etc..
});
Then inside of your behaviorsLookup, returning window.Behaviors won't be undefined. Here's the documentation explaining it further
Is it possible to know if Backbone.Model.set() has changed/not-changed anything without events if possible? Reason being: if I use events it will look like:
listen to change:something
do something if value has changed
model.set("something", "value")
But what if change does not happen? How do I know that? Also with event handlers, I need to remove them approperately. For example, if I do it this way, I need to remove the handler if a change does not happen
Here is a simple solution that hides Events, into a simple synchronous function that returns if set has an effect.
I choose to implement this in a different function name, but you can also override the default set behavior.
var M = Backbone.Model.extend({
setThatLetsYouKnow: function(key, value){
var thisSetHasEffect = { flag: false};
this.listenToOnce(this, "change", function(){
thisSetHasEffect.flag = true;
});
this.set(key, value);
return thisSetHasEffect.flag;
}
});
And the result is:
var m = new M();
m.setThatLetsYouKnow("key",2)// return true
m.setThatLetsYouKnow("key",2)// reutrn fasle
Of Course, you need to add support to all different kind of set argument, this is just the Idea.
Sorry if this question is duplicated but I couldn't solve my problem from other solutions.
I've got this code in a sepate file included in my main index:
var getSuggestedData = {
serviceURL: $("input[name=suggestedServices]").val(),
dataR:"",
doRequest:function(){
//request data to controller
$.ajax({
url:this.serviceURL,
success:function(msg){
this.dataR = msg;
}
})
}
}
When I'm trying to get the variable "dataR" from my index this way it's UNDEFINED! PLEASE, can someone help me out?
$().ready(function() {
getSuggestedData.doRequest();
alert(getSuggestedData.dataR);
});
Thank you in advance!
The reason you are not able to access the dataR object is because it is not in the same context as the result returned from the success method.
One technique is to hold a reference to this in a variable as shown below:
var self = this;
using the jquery library!
$(this.button).bind('click',{self:this},function(event)
{
var that = event.data.self;
alert(that.num);
});
You can also check out the post below in which I explained in detailed about the "this" keyword.
http://azamsharp.com/Posts/57_I_mean__this__not__this_.aspx
If memory serves me right...
this.dataR = msg;
probably needs to be
getSuggestedData.dataR = msg
the 'this' reference would be to the object fed to jQuery, you need to reference the original object. I forget if you could access it by its name directly such as this or if you need to use another method, let me know if it doesn't work out though.