These are chat archives for canjs/canjs

7th
Oct 2017
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 05:40
Anyone know the new CanJS 3 version of can/map/delegate? https://v2.canjs.com/docs/can.Map.prototype.delegate.html . I'm trying to do essentially the same thing... Give it a Map to watch, and if this particular property changes, do stuff
We were shockingly uncreative about some of these package renames
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 05:42
right, but it says it's deprecated
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 05:43
All of can-map is deprecated.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 05:43
uncreative isn't a bad thing by the way, it is what it says it is :)
right, I guess I'm trying to find "whatever is the new way" to watch a Map value change
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 05:45
What you're doing with the changed value matters.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 05:45
I merely want to listen for it to be changed
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 05:45
If it's in another Map, using can-map-define or upgrading to can-define/map/map will let you define a getter that listens to that map's changes.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 05:46
AH ha, there we go... where would I find info on that right there
specifically listening to the value of an item in another map, exactly
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 05:46
For can-define/map/map (this would require some migration work): https://canjs.com/doc/can-define/map/map.html
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 05:49
I am using can-define/map/map yeah
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 05:49
If you want to do this outside of a map's property, you can use can-compute instead: https://canjs.com/doc/can-compute.html
The general idea is pretty much the same between a getter on a defined map or a getter-setter in can-compute: Any other compute or property of a can-map or DefineMap that is read by the getter will cause the value to update when it changes.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 05:50
I want to have one Map A look at a property of a Super Map,and when that value changes do some things in Map A
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 05:52
So if you have a DefineMap A, and a DefineMap B (which could be coming from can-connect/super-map)...
And A.foo is a getter that reads the value of B.bar...
And (this is important) A.foo is bound
Then the getter for A.foo is called every time B.bar is updated.
In practice, that looks like this:
var B = new DefineMap({ bar: "some string" });
var AC = DefineMap.extend({ foo : { get() { return B.bar } } });
var A = new AC();
A.on("foo", function() {});  // <- this is binding.  You don't need to do this if a Stache is reading A.foo
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 05:54
I'm probably not using the "getters" like I should be. Truth be told I'm still figuring out the syntax of the new (to me) DefineMap
Ah OK, let me try that
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 05:57
A more generalized form might put B in A as a property:
var B = new DefineMap({ bar: "some string" });
var AC = DefineMap.extend({ b: {}, foo : { get() { return this.b.bar } } });
var A = new AC({ b: B });
A.on("foo", function() {});
This is analogous to A being a view model, and containing B as a data model.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:05
cool, I've got some other errors to dig through but I think that worked
thanks again!
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:09
You're welcome. Good luck with your other errors.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:21
Do I have to specifically use a setter in order to make the getters "listen and react" to the changed value?
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:22
No. Setters can be used for translating inputs or other side effects, but they're not strictly necessary. You can have a getter without a setter.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:22
because right now, based on your sample above as reference, I'm just setting B.bar = null and B.bar = 'a thing' and thought that would be enough to make the getters do their updates
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:24
It should be. Do you see the getter effect happen when you read A.foo directly?
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:28
my eqiv of A.foo is in a stache, so no that HTML isn't showing when I change the value of B.bar
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:30
OK. so if B is a DefineMap, was B.bar defined on its prototype OR present on the argument when B was instantiated?
This is important because we do all the live listening and side effect stuff on DefineMaps using JavaScript property definitions. It may or may not surprise you to know, that DefineMap can't act on properties it hasn't been informed about.
B = new DefineMap({ bar: "baz" });  // ok, on init we look at all the properties passed in
B = new (DefineMap.extend({ bar: "string" ))(); // ok, was defined on the prototoype
B = new DefineMap(); B.set("bar", "baz");  // ok.  set can set up the listeners.
B = new DefineMap(); B.bar = "baz";  // not ok.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:34
it's defined, but it's set to null until it gets a value
yeah, it's in the DefineMap directly
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:35
OK. I think there's something else going on here but I can't say what with the information provided. Possibly your Stache is looking at a different object than the one you intended.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:38
so here's my real example:
// state.js

export const AppState = DefineMap.extend({seal: false},{
  authToken: {
        type: 'string',
        value: null
    }
});

// footer.js
import AppState from '~/state';

export const ViewModel = DefineMap.extend({ seal: false },{
    loggedIn: {
        get () {
            return (AppState.authToken !== null);
        }
    },
    isAdmin: 'string',
    year: 'string'
});

// Stache:

{{#if loggedIn}}
  <li>
    <a href="{{routeUrl page='logout'}}">Logout</a>
  </li>
{{else}}
  <li>
    <a href="{{routeUrl page='login'}}">Login</a>
  </li>
{{/if}}
I'm essentially trying to get the Footer to change the link based on the state of being logged in or not
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:39
I see the problem immediately.
AppState as you have made it is a constructor class for your app state.
const AppStateClass =  DefineMap.extend({seal: false},{
  authToken: {
        type: 'string',
        value: null
    }
});
export const AppState = new AppStateClass();
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:40
sorry, that's not all of it though, it actually exports a new AppState() that the whole app shares
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:40
OK, but your loggedIn getter is still looking at the class
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:40

yep that's almost exactly what I have, I just still called is AppState

export default new AppState();

does that work or should I change the name and export?
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:42
ok, so the default export from state.js is new AppState() but it also exports the AppState class as a named export?
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:43
yeah I'm probably confusing it with that, I've changed it to AppStateClass () to avoid that, good call
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:44
OK. I would definitely still check it from the console and see if all the values in the model and viewmodel are correct.
Do you know the tricks for getting access to modules in the dev console?
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:45
not exactly but I do have a debugger setup that I can turn on that'll spit out values at the right times so I can tell what they are
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:46
For any project using Steal as its dependency manager, you can use e.g. System.import('~/state').then(c => window.AppState = c.default }) to export the default export of state.js to the window.
If instead you're doing a global build with steal-tools, all of your modules are hosted in window._define.modules
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:51
So if you're logged in, the default export of state.js should have a non-null authToken and if you get the canViewModel() function from System.import('can-view-model'), calling it on your footer component element (canViewModel("my-footer") if your footer component tag is called "my-footer") will show you the instance of the ViewModel class that was created for it.
You can then check the value of loggedIn on that ViewModel instance
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:54
sorry, I think I broke something in my session.js with that name change
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:55
Interesting.
I'm wondering if session.js was operating on the wrong object.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:57
I guess it was, which is weird.... because it was totally working lol
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:58
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 06:59
// session.js

import AppState from '~/_app/js/state';
console.log('AppState',AppState);

AppState undefined
lol yeah exactly.
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 06:59
And you definitely have a default export in state.js?
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 07:00
yep, but should it be something other than AppState?
export const AppState = new AppStateClass();
export default AppState;
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 07:00
That should work fine.
Though you could also try import {AppState} from '~/_app/js/state'; just for shits and giggles.
(that syntax imports the named export instead of the default)
The one other possibility is that these files have a circular dependency. Steal resolves that by initially letting circular deps be undefined, then letting the scripts create their exports.
Which means you can still use imports from a circular dependency, but not synchonously.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 07:06
ERROR: Failed promise: TypeError: Error evaluating myapp@2.0.0#_app/js/state
Error evaluating myapp@2.0.0#models/session
AppState is undefined
that's with import {AppState} from '~/_app/js/state';
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 07:06
Is it salient that in footer.js you were importing from "~/state"?
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 07:08
ha, wow
I mean footer.js just had import AppState from "~/_app/js/state"; in it but was never being used. Didn't think that would be undefined, but I commented that out and no errors now
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 07:14
OK so progress... errors are all gone
I login, AppState.authToken get updated, but the getters aren't firing
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 07:19
Can you get the view model from your footer or any component that has a dependent getter and see what its value is?
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 07:20
Oh I think I see what it was, I was setting the this.sessionState = false;which was changed to the getter instead
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 07:22
BTW getters can take set values as input as well.
Give them an argument like such:
foo: {
  get: function(lastSetValue) {
      return typeof lastSetValue !== "undefined" ? lastSetValue : this.someOtherProperty;
 }
}
So in this case, setting foo = "bar" will trigger an update. That doesn't happen when get() has zero arguments.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 07:28
sessionState: {
        get (lastSetValue) {
            return typeof lastSetValue !== 'undefined' ? lastSetValue : (AppState.authToken !== null);
        }
    },
logout () {
        //this.sessionPromise = AppState.session.destroy();
        AppState.authToken = null;
        this.debug.log('logout: AppState.authToken should be null', null);
        this.debug.log('logout: this.sessionState should be false', this.sessionState);
    }

me clicks the logout button ...

Login: logout: AppState.authToken should be null null debug.js:111:3
Login: logout: this.sessionState should be false true

Brad Momberger
@bmomberger-bitovi
Oct 07 2017 07:30
Did you mean this.debug.log('logout: AppState.authToken should be null', AppState.authToken);?
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 07:30
so AppState.authToken is successfully changing to null but then the this.sessionState isn't changing with the change of AppState.authToken
Brad Momberger
@bmomberger-bitovi
Oct 07 2017 07:30
Are you setting sessionState somewhere, though?
If you are using the lastSetValue form for the getter, you want to make sure you set it to undefined before reading off its value
I'm more than an hour overdue for bed, so anything else you run into will have to be dealt with later.
Jim O'Harra-Sutton
@Psykoral
Oct 07 2017 07:36
yeah no worries man, don't stay up late on my account. Thanks so much for all your help though!