How do I avoid Stack Overflow with circular references in Backbone.js? - callback

Two attributes in my model are supposed to update each other when changed, once. The startDate and endDate. Their values are Moment objects, from the moment.js library.
Two Moment objects of identical dates are not equivalent, and so this causes an loop that exceeds the maximum stack, because they appear to Backbone to have always changed.
new moment('01/01/12') == new moment('01/01/12') // => false
The { silent: true } option seems not to help, I think because it only defers the change event rather than suppressing it alltogether, though I'm not sure.
Here's the code that overflows:
class Paydirt.Models.BlockBrowser extends Backbone.Model
initialize: =>
#on('change:startDate', #updateEndDate)
#on('change:endDate', #updateStartDate)
updateStartDate: =>
#set({ startDate: #cloneEndDate().subtract('days', #get('interval')) }, { silent: true }
updateEndDate: =>
#set({ endDate: #cloneStartDate().add('days', #get('interval')) }, { silent: true } )
cloneStartDate: => new moment(#get('startDate'))
cloneEndDate: => new moment(#get('endDate'))
I can set a global flag to prevent the callback loop as in this variation:
updateStartDate: =>
if !#changing
#changing = true
#set({ startDate: #cloneEndDate().subtract('days', #get('interval')) }, { silent: true } )
#changing = false
updateEndDate: =>
if !#changing
#changing = true
#set({ endDate: #cloneStartDate().add('days', #get('interval')) }, { silent: true } )
#changing = false
... but this is obviously a hackety solution. Is there a better pattern for me to follow in this use case?
Thank you.

Another idea:
Are you using Backbone v0.9.2? it looks like it is doing a more intensive use of the options.silent. Look here.
The options.silent behavior you're describing looks more like v0.9.1.

Two ideas:
Overwrite the Underscore method _.isEqual to manage properly your Moment objects. You can use the Proxy pattern.
Use custom events to have more control that when they are triggered.

I'm not sure how to do this in backbone, but you can compare the integer value of the moments
(moment().valueOf() === moment().valueOf()) // true
or
(+moment() === +moment) // true

Related

Adding data to map without overwriting - Dart

I have multiple maps inside maps, such as:
Program = {'day0': {'area0': {'exercise0': 'value'}}, 'day1': {'area0': {'exercise0': 'value'}}};
And, I want to add data to 'exercise0'. But, sometimes previous data will not be set. Such as creating 'area0' without setting 'day1'.
So...
Program['day0']['area0']['exercise0'] = 'failure';
...this code doesn't work, because 'area0' is not set.
I have used addAll too:
Program.addAll({
'day${dayIndex}': {
"area0": {
'area': Chest,
'exercise0': {'exercise': Chest.exercises[0]},
},
"area1": {
'area': FrontArm,
'exercise0': {'exercise': FrontArm.exercises[0]},
}
},
});
But rn this one overwrites the existing values.

How to port following hook to reasonml

I have following custom hook
function useConstant(fn) {
const ref = React.useRef()
if (!ref.current) {
ref.current = fn()
}
return ref.current
}
and it seems quite hard to port this to reasonml, I have to use type cast twice, what's the ideal way?
external toAny: 'a => 'b = "%identity";
external toBool: 'a => bool = "%identity";
let useConstant = (fn: unit => 'a) => {
let ref: React.Ref.t('a) = toAny(React.useRef());
if (!toBool(React.Ref.current(ref))) {
React.Ref.setCurrent(ref, fn());
};
React.Ref.current(ref);
};
If I understand the purpose of the hook correctly, it's really just a reimplementation of React.useMemo. But for the sake of learning, here's an implementation that should work.
let useLazy = (fn: unit => 'a): 'a => {
let ref = React.useRef(None);
switch (React.Ref.current(ref)) {
| Some(value) => value
| None =>
let value = fn();
React.Ref.setCurrent(ref, Some(value));
value;
};
};
It uses the option type, which is specifically designed for cases like this. If there's no value, we represent that using options None value, and if there is a value we use Some. Instead of using if with JavaScript's semantically unclear concept of truthiness, we pattern match on the option using switch to find that it's None and the value needs to be computed, or Some to get at the value.
The use of option and pattern matching is really common in Reason code, so it's one you should really try to understand using the links provided above for more details if needed.
Note that you could also have used Lazy for this. But that's far less commonly used and therefore also much less useful to learn.

How to use Optional in Netlogo Extensions

I want to create a netlogo primitive that may receive a boolean or may not. Therefore I want make possible to the user that he uses the primitive of these two ways:
1:
ask Walkers [
qlearningextension:learning
]
2:
ask Walkers [
qlearningextension:learning true
]
I tried to do that with OptionalType, but I could not make it. Is it possible to do what I want? If so, how can I do it?
So OptionalType unfortunately only works with CommandBlockType. For a good example of how that works, check out the sample-scala extension (maybe that's where you saw a reference to it in the first pace). OptionalType will not work with BooleanType.
There is a secondary option, that's a little hacky. You can use RepeatableType along with setting defaultOption and minimumOption in your syntax (so NetLogo knows that 0 arguments is okay/expected). Scala code example:
object RepeatableTypeTest extends api.Command {
override def getSyntax =
commandSyntax(
right = List(BooleanType | RepeatableType),
defaultOption = Some(0),
minimumOption = Some(0)
)
def perform(args: Array[api.Argument], context: api.Context) {
println(args.length)
if (args.length > 0) {
val ref = args(0).getBoolean
println(ref)
} else {
println("No argument given!")
}
}
}
Then you just have to wrap calls with the boolean argument in parenthesis, so NetLogo knows you're not starting a new command (it expects the defaultOption without the parens):
to test
sample-scala:rep-bool
(sample-scala:rep-bool true)
(sample-scala:rep-bool false)
(sample-scala:rep-bool false true false true false)
end
The problem with this, as you can see in the example, is if your users want to they can provide extra useless booleans: (sample-scala:rep-bool false true false false true false false). If your code ignores them they won't have an effect, but they could be confusing or weird to extension users.

Looping on an interval of audio element with scalajs-react

I want to build a little component containing an audio element, which capable of looping on an interval. The two ends of the interval would be a defined as properties of the component. As the timeUpdate event don't have the necessarily precision (I want at least 33Hz guaranteed), I decided to use a backend with TimerSupport, and just set the currentTime back to the starting point once it passes the end of the interval.
val AudioRef = Ref[Audio]("audio")
class PlayerBackend extends TimerSupport
val AudioPlayer = ReactComponentB[String]("AudioPlayer")
.initialState(0L)
.backend(i => new PlayerBackend())
.render_P(url => {
<.audio(
^.ref := AudioRef,
^.autoPlay := true,
^.controls := true,
<.source(^.src := "http://www.stephaniequinn.com/Music/Allegro%20from%20Duet%20in%20C%20Major.mp3"),
"Your browser does not support the audio element."
)
})
.componentDidMount({ c =>
c.backend.setInterval(Callback.log({
if (AudioRef(c).isDefined) ({
AudioRef(c).get.currentTime
}) else "nothing"
}), 1000 millisecond)
}).configure(TimerSupport.install)
.build
It this little example I just want to print the current position of the player, but for some reason (the callback closes over a copy of the backend context at the time when the component mounts?) the AudioRef(c) points to an old version of the audio element. Any idea how to fix this? I'm also interested in other designs, as I'm not really experienced neither with ScalaJS nor React.
The issue is with the log call that evaluates its parameter only once, resulting in a single value that is then logged over and over again. The correct code would be something like this:
.componentDidMount({ c =>
c.backend.setInterval(CallbackTo[Double] {
if (AudioRef(c).isDefined) ({
AudioRef(c).get.currentTime
}) else 0
} >>= Callback.log, 1000 millisecond)
})
It creates a callback that extracts the currentTime value (or nothing) and then flatMaps to another callback that logs that value.
I ended up setting the currentTime property by getting the audio element based on its id in a Callback, so my solution currently looks like this:
class PlayerBackend($: BackendScope[String, Unit]) extends TimerSupport
val AudioPlayer = ReactComponentB[String]("AudioPlayer")
.initialState(())
.backend(i => new PlayerBackend(i))
.render_P(url => {
<.audio(
^.id := "audio",
^.autoPlay := true,
^.controls := true,
<.source(^.src := "http://www.stephaniequinn.com/Music/Allegro%20from%20Duet%20in%20C%20Major.mp3"),
"Your browser does not support the audio element."
)
})
.componentDidMount({ c =>
c.backend.setInterval(
Callback({document.getElementById("audio").asInstanceOf[Audio].currentTime = 5.0}) ,
1 seconds
)
})
.configure(TimerSupport.install)
.build

Is it possible to add angularized html tags and attributes in a directive based on model data in Angularjs?

I want to build a directive that will build form inputs based on a nested object of settings that include the input type, model to bind, and html attributes. I have been pounding my head and, perhaps, am close to concluding that this is something Angular is not equipped to do. I would like to build a directive that could take an array of objects like:
[{
"label":"When did it happen",
"model": $scope.event.date,
"element":"input",
"type": "date",
"options":{
"class":"big",
"required": true,
},
},{
"label":"How did it happen?",
"model": $scope.event.cause,
"element":"textarea",
"options":{
"cols":45,
"rows":55,
},
},{
"label":"How many times did it happen?",
"model": $scope.event.times,
"element":"input",
"options":{},
}],
I have struggled through many different aspects of directives. I keep running across a few issues.
Neither the template nor the controller directive functions have access to any sort of scope that could reach any sort of data--most especially my array I've made. This means that I can't decide how to build my DOM (i.e. tag types and attributes) until later.
All compiling is done before the linking function. I am able to manipulate the DOM in the linking function but none of it is angularized. This means if I add a required attribute angular's ngValidate is not aware of it. If I try and change the tag type, I reset lose my model binding etc.
It seems that this is just the way angular runs. Is there no good way to affect the DOM tag types and attributes based on model data without specifying everything out specifically?
How about something like this:
$scope.event = {
date: new Date(),
cause:'It was the colonel in the kitchen with the revolver',
time:5, //...
$metadata: data
};
Where data here is simply the array you have already shown. (apart from the model property which would just be a string representing the property of the event, like so:
{
"label":"When did it happen",
"model":'date',
"element":"input",
"type": "date",
"options":{
"class":"big",
"required": true,
}
}
Then your directive would simply access the property given it on the parent scope. (event) And process the metadata into a usable template. Which could then get compiled.
Here is such a directive, and the directive...
myApp.directive('contentForm',function($compile,$interpolate){
var template = "<span class='lbl'>{{label}}</span><{{element ||'input'}} {{options}}"+
" ng-model='{{root+'.'+model}}'></{{element}}>";
function linkerFunction(scope,element,attrs){
if(!scope[attrs.contentForm])return;
var metadata = scope[attrs.contentForm].$metadata || false;
if(!metadata)return;
element.html(getHtml(metadata,attrs.contentForm));
$compile(element.contents())(scope);
}
return {
restrict: 'A',
replace: true,
link:linkerFunction
};
//interpolate the template with each set of metadata
function getHtml(metadata,root){
var interpolation = $interpolate(template);
var html = '';
if(angular.isArray(metadata)){
for(var i = 0; i < metadata.length; i++){
metadata[i].root = root;
metadata[i].options = processOptions(metadata[i].options);
html += interpolation(metadata[i]) + '</br>'
}
}else{
html = interpolation(metadata);
metadata.options = processOptions(metadata.options);
}
return html;
}
// parse object into html attributes
function processOptions(options){
var result = '';
for(var key in options){
if(options.hasOwnProperty(key)){
result += ' '+key+"='"+options[key]+"'"
}
}
return result.trim();
}
});
You'd probably want to change the template. I seem to have forgotten to put in a type. But that should be fairly straight forward. I hope this helps!