Change even on map

When having a Map value with attributes whose value is an object, is there a way to bind a change event on it, without using a compute attribute?

for instance, having a map like this, check when data1 changes…

var newData = can.DefineMap.extend({
  "data1":{
    value:false
  },
  "data2":{
    value:false
  }
})


var vm = can.DefineMap.extend({
    "temp":{
      value: newData
    }
});

var myvm = new vm({});

myvm.on("temp.data1",function(){});

JS EXAMPLE

I would do this:

var temp = myvm.temp;
temp.on("data1", function(event, newVal, oldVal) {
  console.log("CHANGED TEMP");
});

Would that work for your code?

@chasen that won’t detect when the .temp property changes.

@fp_dev you should use either a computed property or a compute.

BTW, it’s a good idea to capitalize construtor functions NewData instead of newData.

1 Like

Thank you both!

I used the compute property in the JS EXAMPLE. But what if the VM is the one of a Component? should I use the event like the following?

//with Can@3.X
    Component.extend({
       tag: "my-cmp-tag",
       view: my_stache_template,
       viewModel: NewData,
       events: {
           "{scope} temp.data1": function(obj,ev,new, old){ .... }
       }
    });

you should avoid the events object generally speaking.

I do not mean to bother you but do you mean in the Component object? Or I should design my application to just get/set properties and use the live-binding?
But if I need to call function/methods to initialize external objects I don’t see any other way.

Thanks for this exchange of ideas: it very helpful for me.

Ideally your application is just getters and methods and lets live-binding do all the work.

Yes, sometimes this breaks down and that’s why the Comonent’s events object is there. In Bitballs, we use it to work with a Youtube video. But if possible, it’s best to avoid the events object.

What external objects do you need to initialize?

For instance I have a class that init a Google Map with a list of markers.
This GoogleMap is initialized when the user click on a link that will handle the display of the Component.
In this case I need to wait the Component is created (it’s tag in the stache template filled) and then init the google Map using the `event inserted of the Component.

// Map that init the main page
var status = can.Map.extend({
  showMap:{
     value:false
  },
  setShowMap: function(val){
      this.showMap = val;
  }
})


//can stache template of the main page
<button ($click)="setShowMap(true)">show map</button>
{{#if showMap}}
<can-import from="component/googleMap/googleMap" >
  {{#if isResolved}}
    <map></map>
</can-import>
{{/if}}

Here is the pseudo-code of the component

// component/googleMap/googleMap.js
import stache from "can-stache";
import Map from "can/define/map/map";
var mapStatus = Map.extend({
     mapInstance:{
             value: null;
     }
});

Component.extend({
   tag: "map",
   view: stache("<div id="locationMap"></div> <div id="mapSearchPanel> .. </div>"),
   viewModel: mapStatus,
   events:{
        inserted: function(){
                    this.viewModel.mapIntance = createMap.getInstance('locationMap');        
        }
   }
})

createMap.getInstance(div_id) is a Singleton class that will return the instance of the map in the div_id position of the DOM.

@fp_dev, that example looks like it would work: once the map[1] component is inserted, you can load the map into #locationMap.

If you’re trying to access mapInstance in the parent scope after the component has been inserted, you can use the {^to-parent} binding.

[1] Just FYI, your components should have a hyphen in them, so app-name or something like that.