Probably a simple answer but as a beginner, say for example I wanted to store a property from my ember model inside of my controller which I could then compare against new data, how could I go about doing it?
To provide more context, I have an input field where the value is retrieved using model.user_input, I want to be able to store this somewhere when the view is first loaded and then compare it when the value changes. I've tried using computed properties but they are also getting updated when the model changes.
In your controller's route, you could set up your controller such that your controller saves the initial value:
import Ember from 'ember'
export default Ember.Route.extend({
model() {
return {
'user_input': /* ... */
}
},
setupController(controller, model) {
this._super(...arguments)
controller.set('original_input', Ember.get(model, 'user_input'))
}
})
That way, when the value changes, you could simply fetch the original value via Ember.get(this, 'original_input') on your controller:
import Ember from 'ember'
export default Ember.Controller.extend({
'original_input': '',
isChanged: Ember.computed('model.user_input', function() {
return Ember.get(this, 'original_input') !== Ember.get(this, 'model.user_input')
})
})
Related
I am getting the transition data in the route js file like so:
beforeModel(transition) { console.log(transition) }
And I want to use it in a function in my controller like this:
import Controller from '#ember/controller';
export default class ListingsController extends Controller {
get pageTitle() {
if (this.transition.targetName == 'foo') {
return 'page title';
}
}
}
And then I want to display the result like this:
<h1>{{this.pageTitle}}</h1>
I cannot find a way to pass the transition data from the route to the controller. Any ideas?
While you technically can leverage the beforeModel to get the controller via this.controllerFor as #KathirMagaesh suggests, I wouldn't actually advocate for this solution. It's definitely not the normal or expected Ember pattern. Furthermore, if you look at the transition api, there is no reference to transition.targetName. If this works, this is private api and thus brittle.
If you need to change a property based on the current route, you should be using the public router service which provides some useful properties for this very purpose!
For example, in your controller you could have a computed property that leverages the router service to determine what the page title should be
import Controller from '#ember/controller';
import { computed } from '#ember/object';
import { inject } from '#ember/service';
// this injects the router service into our component via Ember's DI framework
router: inject(),
export default Controller.extend({
pageTitle: computed('router.currentRouteName', function(){
let currentRoute = this.router.currentRouteName;
if(currentRoute === 'foo'){
return 'page title';
}
// do other stuff for other routes.
})
})
This leverages currentRouteName which is the period separated name like foo.bar. You can also also access the url via currentURL which would be /foo/bar
PS. Since I haven't used ES6 classes yet, I've provided the old style ember solution. You'll probably need to use the #computed decorator or #tracked where I'm using the computed function. I only know about the Octane ember style from RFCs and awesome blog posts but am not up to date with what's landed.
PPS. If you're on old ember, the current route name / URL properties are available on the application controller.
In the beforeModel hook use
this.controllerFor(currentrRouteName).set('transition', transition);
This will set transition property in controller of the current router.
For more on controllerFor()
I have a parent route. Inside this route there is a component that is rendered.
The route has a template.hbs file that contains a HTML div element. I need to be able to change the class of this div element from within my child component.
To do this, I was planning on using a service. The idea being inject the service into both the route and child component and then bind the class of the div to a property on the service. Then, when I inject the service into the child component I can change the property and see any changes reflected by the parent route.
Problem is, the binding doesn't seem to work!
Parent Route:
Template:
<div class={{model.containerClass}}>
Route.js:
dashboardContainerManager: service('dashboard-container-manager'),
afterModel(model) {
model.set('containerClass', this.get('dashboardContainerManager').dashboardContainerClass);
SERVICE:
export default Service.extend({
dashboardContainerClass: null,
init() {
debugger; //placed to ensure one instance being made
this._super(...arguments);
this.set('dashboardContainerClass', 'container dashboard-container'); //need to set it here to prevent glimmer error
},
changeContainerClass(newClass) {
debugger;
this.set('dashboardContainerClass', newClass);
}
});
Child Component:
dashboardContainerManager: service('dashboard-container-manager'),
init() {
this._super(...arguments);
this.get('dashboardContainerManager').changeContainerClass('test');
},
The result of the above code is that the class of my div is initially set to "container dashboard-container" because that's the value that the dashboardContainerClass property on my service is initialised to. However, when the value is changed to "test" inside my component, the class of my div isn't updated which suggests it's not bound properly to the value of dashboardContainerClass. I tried using a computed property in various places as well but couldn't get anything to work.
What am I doing wrong here?!
There is a reads computed property macro that'd help you out here:
https://emberjs.com/api/ember/3.7/functions/#ember%2Fobject%2Fcomputed/reads
in your component, you'd just do something like:
import { reads } from '#ember/object/computed';
// ...
dashboardContainerManager: service('dashboard-container-manager'),
containerClass: reads('dashboardContainerManager.dashboardContainerClass'),
Update / full example:
service:
export default Service.extend({
dashboardContainerClass: 'container dashboard-container',
changeContainerClass(newClass) {
this.set('dashboardContainerClass', newClass);
}
});
route:
export default Route.extend({
// removed afterModel and service injection
});
controller:
export default Controller.extend({
containerManager: service('dashboard-container-manager'),
containerClass: reads('containerManager.dashboardContainerClass'),
});
template:
<div class='some-other-class {{containerClass}}'>
other stuff
</div>
i just wanna refresh model in route while get an action from controller and run doRefresh action in this route
this is my code
import Ember from 'ember';
export default Ember.Route.extend({
profileFormService: Ember.inject.service(),
profileFormAtributeService: Ember.inject.service(),
model(){
return Ember.RSVP.hash({
profileForms: this.get('profileFormService').find(),
profileFormsAttributes: this.get('profileFormAtributeService').findByProfileFormId("1"),
inputTypes: {data: ['CHECKBOX', 'NUMBER_FIELD', 'PHONE_NUMBER', 'TEXT_FIELD', 'EMAIL', 'TEXT_AREA', 'RADIO_BUTTON', 'DESA', 'KABUPATEN_KOTA', 'PROVINSI', 'KECAMATAN', 'MAP_POINT', 'MAP_POLYGON']}
});
},
setupController(controller, model) {
this.controllerFor('backend.setting-profile-form-attribute').set('profileForms', model.profileForms);
this.controllerFor('backend.setting-profile-form-attribute').set('profileFormsAttributes', model.profileFormsAttributes);
this.controllerFor('backend.setting-profile-form-attribute').set('inputTypes', model.inputTypes);
},
actions:{
doRefresh(param){
let context = this;
this.get('profileFormAtributeService').findByProfileFormId(param).then(function (response) {
context.set("profileFormsAttributes",response);
}), function (e) {
this.debug(e);
};
}
}
});
unfortunately this is does'nt affect the profileFormsAttributes model.
I've ben trying to debug the model with this
this.debug(this.get('controller.model.profileFormsAttributes'))
this.debug(this.get('model.profileFormsAttributes'));
but the console log said undefined
can you resolve this and explain what happen in this my route..
thank's for your concern
Your problem is that you cannot achieve the object returned from within route in action handler directly like this.get('profileFormsAttributes'); hence your setting does not work.
this.get('controller.model.profileFormsAttributes');
this.get('model.profileFormsAttributes');
Even above two statements does not work; because you cannot retrieve model or controller like this.
You have two options; either you need to save what you are going to return from model directly within model hook with this.set('model', model) or you can achieve it with this.controllerFor(this.get('routeName')).get('model')
I would recommend the second approach for your case. Please take a look at the following twiddle that I have prepared to illustrate the case for you.
Please take a look at index.js where foo attribute of object returned from model hook is set with
Ember.set(this.controllerFor(this.get('routeName')).get('model'), 'foo', 'foo updated');
I hope this helps.
I am accessing the query parameters in the route using the below code:
export default Ember.Route.extend({
afterModel: function(params, transition){
this.set('clientId', transition.queryParams.clientId);
},
setupController: function(controller) {
controller.set('clientId', this.get('clientId'));
}
});
The reason I am not using a controller is because I am feeding this data straight in to a component and I am of the understanding that in newer versions of Ember controllers will be phased out.
This is currently working however if I change any of the properties in the URL it doesn't update in the app unless I refresh the page or exit the route and re-enter it.
How can I "re-run" the route afterModel and update the properties which are passed to the component?
If the only option is to use a controller then I can implement this until a better solution comes along.
Try adding this to your route:
queryParams: {
'clientId' : {
refreshModel: true,
replace : true,
},
}
See here for more information.
My route is:
export default Ember.Route.extend({
model: function (params) {
var blocks = this.store.find('block', {'objectId': 777});
this.controllerFor("blocks").set('model', blocks);
return Ember.RSVP.hash({
object: this.store.find('object', params.id),
blocks: blocks
});
//return this.store.find('object', params.id);
}
});
My controller is:
export default Ember.ArrayController.extend({
init: function(e){
alert('jere');
}
});
Alert in init function works but next I get the error:
Error while processing route: objects.bc.index Cannot read property
'length' of null TypeError: Cannot read property 'length' of null
What is the right way to get collection of models through ajax and show it in template with custom array controller?
This code has a number of problems. First, ArrayController assumes that its model is an array, whereas your model hook is returning a hash. Second, your this.controllerFor("blocks").set('model', blocks) call attempts to set the model for the controller to a promise, which is useless. You do not set the model for the controller in the model hook like this. You set it by returning the model, which is then installed (after it resolves) into the controller by your (or the default) setupController hook.
You should not use an ArrayController, which is soon to be deprecated anyway, and instead use a plain old controller. The model for this controller will be the hash returned from the model hook. You need to access it explicitly in the template with {{model}}.
So you want something like this:
// route.js
export default Ember.Route.extend({
model: function(params) {
var blocks = this.store.find('block', {'objectId': 777});
return Ember.RSVP.hash({
object: this.store.find('object', params.id),
blocks: blocks
});
}
});
// controller.js
export default Ember.Controller.extend({
init: function(e) {
alert('jere');
}
});
In your templates, instead of
{{#each controller}}
blockProp is {{blockProp}}
as you are presumably doing now, use
{{#each block in model.blocks}}
blockProp is {{block.blockProp}}
{{/each}}
And objectProp is {{model.object.objectProp}}