Chapter 3 of the AngularDart tutorial defines a rating #NgComponent (see excerpt below), that is used in index.html like this:
<rating max-rating="5" rating="ctrl.selectedRecipe.rating"></rating>
In that chapter it is also suggested that that the max-rating #NgAttr can be set via a {{...}} like this:
<rating max-rating="{{ctrl.max}}" rating="ctrl.selectedRecipe.rating"></rating>
In the RecipeController I have simply declared:
int max = 5;
If I add print("maxRating('$value')") at the top of the component's maxRating() setter body (see below), then in running the app I get the following output:
maxRating('') // printed 7 times
maxRating('5') // printed 7 times
Questions: Why is the value initially empty? I assume that it is because the interpolation has not been done yet, but then why is the setter called at all before the value is "ready"?
Excerpt of RatingComponent class definition:
#NgComponent(
selector: 'rating', ...
publishAs: 'cmp'
)
class RatingComponent {
...
#NgTwoWay('rating')
int rating;
#NgAttr('max-rating')
set maxRating(String value) {
var count = value == null ? 5 : int.parse(value);
stars = new List.generate(count, (i) => i+1);
}
As far as I know, angular dart is very eager in applying values. As soon as angular is running it starts applying values, I guess to provide a feel of responsiveness.
I've been bitten by this too and had to write more than one workaround for not yet initialized values.
The setter and getters are called by the binding mechanism to stabilize the values, as some values may depend on each other and the mechanism "brute forces" this by just setting and getting values multiple times (7 by default, IIRC).
Related
What I am Doing
I am trying to create a Sudoku solver and generator in Vue. Right now, I have the solving algorithm set up, and just need to generate new problems. I am generating problems by creating a completed Sudoku problem (complete with no bugs), then I have to remove nodes so that there is still only 1 solution to the problem.
The Problem
When I try to access a node from the multi-dimensional array that represents the board, and change it to null (what I am using to display a blank node), the board does not update that value. I am changing it with the following code: newGrid[pos[0]][pos[1]] = null; (where pos[0] is the row, pos[1] is the column , and newGrid is grid we want to mutate). Note that the array is an array with 9 arrays inside, and each of those arrays has 9 numbers (or null) which represent the values for that position in the grid. To elaborate on the bug, if I put a console.log(newGrid), there are normal looking values, and no null.
What I Know and Have Tried
I know it has to do with this specific line, and the fact that I am setting the value equal to null because changing null to another value (i.e. newGrid[pos[0]][pos[1]] = 0;) works and changes the array. The reason I don't just use a value other than null is: null renders and nothing and other values (0) render as something (null nodes should be blank), null is simple to understand in this situation (the logic is node has null, node has nothing, node is blank), and null is already implemented throughout my codebase.
Additionally, if I use console.log(newGrid[pos[0]][pos[1]]), null (the correct output) is outputted, even though console.log(newGrid) shows a number there, not null. Also, oddly enough, this works for one specific node. In row 1 (indexing starts at 0), column 8, null is set. Even though the input (completed) grid is always different, this node is always set to null. Edit: this bug had to do with the input grid already having null here, so it actually doesn't let any nulls be set.
To summarize: I expect an array with a null in a few positions I update, but I get a number instead. Also, there are no errors when the Typescript compiles to Javascript or during runtime.
Code
Given that I am not exactly sure where the problem may be (i.e. maybe I create the array wrong) I am including the minimum code with a pastebin link to the whole file (this is the full code). To restate, the goal of this function is to remove nodes from the list (by replacing them with null) in order to create a Sudoku puzzle with one solution. The code on Stack Overflow only includes some of the whole file, and the pastebin link includes the rest.
//global.d.ts
type Nullable<T> = T | null;
type Grid = Array<Array<number | null>>;
import { Solver } from './Solve';
// Inside the function that does the main work
const rowLen: number = grid.length;
const colLen: number = grid[0].length;
let newGrid: Grid = grid; // Grid is a argument for this function
let fullNodes = GetFirstFull(grid, colLen, rowLen);
let fullNodesLen: number = fullNodes.length;
// Some stuff that figures out how many solutions there are (we only want 1) is excluded
if (solutions != 1) {
fullNodesLen++;
rounds--;
} else {
newGrid[pos[0]][pos[1]] = null;
}
Note that if anything seems confusing check out the pastebin or ask. Thank you so much for taking the time to look at my problem!
Also, it isn't just 0 that works, undefined also makes it set correctly. So, this problem seems to be something with the null keyword...
EDIT:
Given that no one has responded yet, I assume: my problem is a bit hard, there isn't enough information, my post isn't good quality, or not enough people have seen it. To control the problem of not enough information, I would like to include the function that calls this function (just to see if that might be related).
generate(context: ActionContext<State, any>) {
let emptyArray = new Array(9);
for (let i = 0; i < 9; ++i)
emptyArray[i] = [null, null, null, null, null, null, null, null, null];
const fullGrid = Solver(emptyArray);
const puzzle = fullGrid ? Remover(fullGrid, 6) : state.gridLayout;
context.commit('resetBoard', puzzle);
},
Note: If you aren't familiar with Vuex, what context.commit does is changes the state (except it is changing a global state rather than a component state). Given that this function isn't refactored or very easy to read code in the first place, if you have any questions, please ask.
To solve other potential problems: I have been working on this, I have tried a lot of console.log()ing, changing the reference (newGrid) to a deepcopy, moving stuff out of the if statements, verifying code execution, and changing the way the point on the newGrid is set (i.e. by using newgrid.map() with logic to return that point as null). If you have any questions or I can help at all, please ask.
My SwiftUI segmented control picker uses plain Int ".tag(1)" etc values for its selection.
CoreData only has Int16, Int32 & Int64 options to choose from, and with any of those options it seems my picker selection and CoreData refuse to talk to each other.
How is this (??simple??) task achieved please?
I've tried every numeric based option within CoreData including Int16-64, doubles and floats, all of them break my code or simply just don't work.
Picker(selection: $addDogVM.gender, label: Text("Gender?")) {
Text("Boy ♂").tag(1)
Text("?").tag(2)
Text("Girl ♀").tag(3)
}
I expected any of the 3 CoreData Int options to work out of the box, and to be compatible with the (standard) Int used by the picker.
Each element of a segmented control is represented by an index of type Int, and this index therefore commences at 0.
So using your example of a segmented control with three segments (for example: Boy ♂, ?, Girl ♀), each segment is represented by three indexes 0, 1 & 2.
If the user selects the segmented control that represents Girl ♀, then...
segmentedControl.selectedSegmentIndex = 2
When storing a value using Core Data framework, that is to be represented as a segmented control index in the UI, I therefore always commence with 0.
Everything you read from this point onwards is programmer preference - that is and to be clear - there are a number of ways to achieve the same outcome and you should choose one that best suits you and your coding style. Note also that this can be confusing for a newcomer, so I would encourage patience. My only advice, keep things as simple as possible until you've tested and debugged and tested enough to understand the differences.
So to continue:
The Apple Documentation states that...
...on 64-bit platforms, Int is the same size as Int64.
So in the Core Data model editor (.xcdatamodeld file), I choose to apply an Integer 64 attribute type for any value that will be used as an Int in my code.
Also, somewhere, some time ago, I read that if there is no reason to use Integer 16 or Integer 32, then default to the use of Integer 64 in object model graph. (I assume Integer 16 or Integer 32 are kept for backward compatibility.) If I find that reference I'll link it here.
I could write about the use of scalar attribute types here and manually writing your managed object subclass/es by selecting in the attribute inspector Class Codegen = Manual/None, but honestly I have decided such added detail will only complicate matters.
So your "automatically generated by Core Data" managed object subclass/es (NSManagedObject) will use the optional NSNumber? wrapper...
You will therefore need to convert your persisted/saved data in your code.
I do this in two places... when I access the data and when I persist the data.
(Noting I assume your entity is of type Dog and an instance exists of dog i.e. let dog = Dog())
// access
tempGender = dog.gender as? Int
// save
dog.gender = tempGender as NSNumber?
In between, I use a "temp" var property of type Int to work with the segmented control.
// temporary property to use with segmented control
private var tempGender: Int?
UPDATE
I do the last part a little differently now...
Rather than convert the data in code, I made a simple extension to my managed object subclass to execute the conversion. So rather than accessing the Core Data attribute directly and manipulating the data in code, now I instead use this convenience var.
extension Dog {
var genderAsInt: Int {
get {
guard let gender = self.gender else { return 0 }
return Int(truncating: gender)
}
set {
self.gender = NSNumber(value: newValue)
}
}
}
Your picker code...
Picker(selection: $addDogVM.genderAsInt, label: Text("Gender?")) {
Text("Boy ♂").tag(0)
Text("?").tag(1)
Text("Girl ♀").tag(2)
}
Any questions, ask in the comments.
I would like to know if it is possible to retrieve the name of a variable.
For example if I have a method:
def printSomething(def something){
//instead of having the literal String something, I want to be able to use the name of the variable that was passed
println('something is: ' + something)
}
If I call this method as follows:
def ordinary = 58
printSomething(ordinary)
I want to get:
ordinary is 58
On the other hand if I call this method like this:
def extraOrdinary = 67
printSomething(extraOrdinary)
I want to get:
extraOrdinary is 67
Edit
I need the variable name because I have this snippet of code which runs before each TestSuite in Katalon Studio, basically it gives you the flexibility of passing GlobalVariables using a katalon.features file. The idea is from: kazurayam/KatalonPropertiesDemo
#BeforeTestSuite
def sampleBeforeTestSuite(TestSuiteContext testSuiteContext) {
KatalonProperties props = new KatalonProperties()
// get appropriate value for GlobalVariable.hostname loaded from katalon.properties files
WebUI.comment(">>> GlobalVariable.G_Url default value: \'${GlobalVariable.G_Url}\'");
//gets the internal value of GlobalVariable.G_Url, if it's empty then use the one from katalon.features file
String preferedHostname = props.getProperty('GlobalVariable.G_Url')
if (preferedHostname != null) {
GlobalVariable.G_Url = preferedHostname;
WebUI.comment(">>> GlobalVariable.G_Url new value: \'${preferedHostname}\'");
} else {
WebUI.comment(">>> GlobalVariable.G_Url stays unchanged");
}
//doing the same for other variables is a lot of duplicate code
}
Now this only handles 1 variable value, if I do this for say 20 variables, that is a lot of duplicate code, so I wanted to create a helper function:
def setProperty(KatalonProperties props, GlobalVariable var){
WebUI.comment(">>> " + var.getName()" + default value: \'${var}\'");
//gets the internal value of var, if it's null then use the one from katalon.features file
GlobalVariable preferedVar = props.getProperty(var.getName())
if (preferedVar != null) {
var = preferedVar;
WebUI.comment(">>> " + var.getName() + " new value: \'${preferedVar}\'");
} else {
WebUI.comment(">>> " + var.getName() + " stays unchanged");
}
}
Here I just put var.getName() to explain what I am looking for, that is just a method I assume.
Yes, this is possible with ASTTransformations or with Macros (Groovy 2.5+).
I currently don't have a proper dev environment, but here are some pointers:
Not that both options are not trivial, are not what I would recommend a Groovy novice and you'll have to do some research. If I remember correctly either option requires a separate build/project from your calling code to work reliable. Also either of them might give you obscure and hard to debug compile time errors, for example when your code expects a variable as parameter but a literal or a method call is passed. So: there be dragons. That being said: I have worked a lot with these things and they can be really fun ;)
Groovy Documentation for Macros
If you are on Groovy 2.5+ you can use Macros. For your use-case take a look at the #Macro methods section. Your Method will have two parameters: MacroContext macroContext, MethodCallExpression callExpression the latter being the interesting one. The MethodCallExpression has the getArguments()-Methods, which allows you to access the Abstract Syntax Tree Nodes that where passed to the method as parameter. In your case that should be a VariableExpression which has the getName() method to give you the name that you're looking for.
Developing AST transformations
This is the more complicated version. You'll still get to the same VariableExpression as with the Macro-Method, but it'll be tedious to get there as you'll have to identify the correct MethodCallExpression yourself. You start from a ClassNode and work your way to the VariableExpression yourself. I would recommend to use a local transformation and create an Annotation. But identifying the correct MethodCallExpression is not trivial.
no. it's not possible.
however think about using map as a parameter and passing name and value of the property:
def printSomething(Map m){
println m
}
printSomething(ordinary:58)
printSomething(extraOrdinary:67)
printSomething(ordinary:11,extraOrdinary:22)
this will output
[ordinary:58]
[extraOrdinary:67]
[ordinary:11, extraOrdinary:22]
UDATED
How do I go about this?
I got this from Main.hx:
function onMouseOver(e:MouseEvent){
if(Std.is(e.currentTarget, MovieClip)){
initializer (cast e.currentTarget,["scaleX",1.5,"scaleY",1.5])
}
}
Then this is the pointed function in my Animation Class
//here if i set mc:Dynamic everything goes great! but when this one
function initializer(mc:MovieClip, vars:Array<Dynamic>){
var varsLength:Int = Math.round(vars.length/2);
for(m in 0...varsLength){
ini[m] = mc[vars[2*m]];
}
}
then when i compile it, an error appears:
Error: Array access is not allowed in flash.display.MovieClip
How do I resolve this?
EDIT:
vars: are properties of the MovieClip, for example when I pass these parameters:
initializer (mcClip1,["scaleX",1.5,"scaleY",1.5])
so:
vars = ["scaleX",1.5,"scaleY",1.5]
and:
ini[m] will store "scaleX" and "scaleY"`
X-Ref: https://groups.google.com/forum/#!topic/haxelang/_hkyt__Rrzw
In AS3, you can access fields of an object via their String name using [] (array access). This is called Reflection.
In Haxe, Reflection works differently - you need to make use of the Reflect API.
It's considered bad practice - it's not type-safe, which means the compiler can do very little to help you with error messages, and it's quite slow as well. This is why the usage makes it very explicit that Reflection is actually going on (while in AS3, this fact is somewhat hidden). Consider if there are other ways of solving this problem that don't require Reflection.
Now, to get back to your example, here's what it would look like in Haxe:
function onMouseOver(e:MouseEvent){
if (Std.is(e.currentTarget, MovieClip)) {
initializer(cast e.currentTarget, ["scaleX", 1.5, "scaleY", 1.5])
}
}
function initializer(mc:MovieClip, vars:Array<Dynamic>) {
for (m in 0...Std.int(vars.length / 2)) {
ini[m] = Reflect.getProperty(mc, vars[2*m]);
}
}
Btw, your loop was running for too long since you only use half of the values in the array - if you don't divide it by two like I did, you'll end up with [scaleX, scaleY, null, null] instead of the desired [scaleX, scaleY].
Based on this XPages adding #Formulas in dialogList, my dialogList1 takes values from two concatenated views: a and b.
There is another dialogList2, which is rendered depending if the dialogList1 value is null or not, whose values should be like this:
dialogList1.value is from a => dialogList2.choices should be only from b
dialogList1.value is from b => dialogList2.choices should be only from a
I tried:
// Contr.txt_particontractcv_1 - is the value binded by dialogList1
var dbname = session.getServerName() + "!!" + "mynsf.nsf";
//var a = #Unique(#DbColumn(dbname, "vwNumeCompanii", 0)).sort();
//var b = #Unique(#DbColumn(#DbName(),"vwA",0));
//return a.concat(b);
if ( #IsMember(Contr.txt_particontractcv_1,#Unique(#DbColumn(#DbName(),"vwA",0))))
{ return #Unique(#DbColumn(dbname, "vwNumeCompanii", 0)) }
else
{ return #Unique(#DbColumn(#DbName(),"vwA",0)) }
but the dialogList2 is taking values only from vwA ( from b ) ... I think I'm missing something. Thanks for your time.
Contr.txt_particontractcv_1 cannot be used in SSJS. Dot notation works in LotusScript but not SSJS or Java because Java's runtime is not proprietary and has not been extended that way. That is why Contr.getItemValueString("txt_particontractcv_1") is required.
Some SSJS global variables allow dot notation to be used, e.g. sessionScope. But that is because it is based on a Map, so sessionScope.myProperty can only map to sessionScope.get("myProperty"). The Domino Document class does not extend the Map interface (that's one of the enhancements of the OpenNTF Domino API), so dot notation doesn't know whether to use getItemValue(), getItemValueString(), getItemValueDateTimeArray() etc.
This is also why best practice for scoped variables is also to use e.g. sessionScope.get("myVar"). When it comes to moving to Java, you will not be able to use dot notation, you will have to use the relevant method. So working that way in SSJS fosters good habits for the future.
Yep, I just modified Contr.txt_particontractcv_1 to Contr.getItemValueString("txt_particontractcv_1") and, now, it works.`