As controllers are deprecated in Ember I try to get rid of them in my applications. So in all cases where I used the rout's method getControllerFor('nameOfTheRoute') to access other controller's methods or properties
I now would like to call something like getRouteFor('nameOfTheRoute') but this method does not exists. So what can I do to access other routes. I would be very thankful for any suggestion.
Edit:
I have an app, that is a bit like Sequel Pro for Mac OS. This app has a basic application layout with two areas one for tables (on the left) and one for table rows (on the right). Below both areas are buttons to perform actions on a table or a table row. In ember-speak these buttons are defined in the applications route. As long as no table is selected (hence there are no table rows and the rows-route is not loaded) the buttons for the rows are not active. If table rows get loaded into the rows area the buttons are getting activated. And here is the problem: When I click on a button for the rows I want to perform an action in the rows-route. When using a controller, I can use getControllerFor()and access the actions-hash. But how could I perform an action on the rows-route? Or how can change my setup to reach my goal?
Why do you need to access other routes? If you want to access parent routes, send an action to the parent route. If those routes that you want to access are not parent routes, calling them are not suitable since it conflicts with DDAU. Maybe in that case, the behaviour you want to access should be placed in a service. Or defining a component more suitable for that case.
Related
I have a list applet with a drilldown in one of the columns, and I want to enable or disable it based on another field's value. Something like a dynamic drilldown, but instead of choosing a different view, I want to disable the navigation for some of the records.
Can this be done in Siebel 7.8?
Ideally without server scripting... and definitely without ugly browser scripting hacks, please.
I have tried creating a dynamic drilldown, but it doesn't work because I have to specify a target view in my parent default drilldown. If I use an inactive view for that, then the whole dynamic drilldown is ignored and Siebel simply uses the one with the next sequence number.
I can think of a couple of ways to implement it, but both are far from ideal:
Writing some server script to detect the drilldown event before it happens, and abort it if needed with a RaiseErrorText message. It should be doable... but I'd rather disable the drilldown than throw an ugly error to my users.
Placing the drilldown in a calculated field, and make it have no text when there should be no navigation. If there is no text, there is nothing the users can click to drilldown, right? But I would have to add a new column just for the drilldown, which would be confusing for the users.
If you don't want any scripting. There are 2 ways.
First way is using toggle applet.
1.Main Applet will have the drill down down object. And Toggle applet will not have the drill down objects.
2.Create a field in the BC to use it in the Toggle Applet condition .Toggle Applet will be displayed when the drilldown is not required.
Second way you can achieve it is through visibility Type attribute in the drilldown object as well as visibility Applet Type attribue at the view level.
I am trying to build a UI with left side bar having filters and right side having actual filtered data.
For loading data into the dynamic part of the UI(right side), which approach is considered better in terms of code quality and app performance ?
Use sub routes (for dynamic part of the UI)
Use separate components that load their own data (for dynamic part of
the UI)
There is not a direct correct answer for that; you can use both ways but here is a few things to consider and in the end I generally prefer to use sub-routes due to the following:
Waiting for UI to load: In case you are using separate components to load their own data; then you need to handle the loading state of the components. What I mean is; if you simply use sub-routes; then model hooks (model, beforeModel, etc.) will wait for the promises to be solved before displaying the data. If you simply provide a loading template (see the guide for details) it will be displayed by default. In case you use components, you might need to deal with displaying an overlay/spinner to give a better UX.
Error Handling: Similarly like loading state management; Ember has already built in support for error handling during route hook methods. You will need to deal with that on your own if you prefer components to make the remote calls. (See guide for details)
Application State: Ember is SPA framework; it is common practice to synchronize application state with the URL. If you use sub-routes; you can simply make use of the query parameters (see the guide for details) and you will be able to share the URL with others and the application will load with the same state. It is a little bit trickier to do the same with components; you still need to use query parameters within the routes and pass the parameters to the components to do that.
Use of component hook methods: If you intend to use the components then you will most likely need to use component hook methods to open the application with default filter values. This means you will need to make some remote call to the server within one or more of init, willRender, didReceiveAttrs component hook methods. I personally do not like remote calling within those methods; because I feel like this should better be done within routes and data should be passed to the components; but this is my personal taste of coding that you should approach the case differently and this is perfectly fine.
Data down, actions up keeps components flexible
In your specific example, I'll propose a third option: separate components that emit actions, have their data loaded by the route's controller, and never manipulate their passed parameters directly in alignment with DDAU.
I would have one component, search-filter searchParams=searchParams onFilterChange=(action 'filterChanged'), for the search filter and another component that is search-results data=searchResults to display the data.
Let's look at the search filter first. Using actions provides you with maximum flexibility since the search filter simply emits some sort of search object on change. Your controller action would look like:
actions: {
filterChanged(searchParams){
this.set('searchParams', searchParams);
//make the search and then update `searchResults`
}
}
which means your search-filter component would aggregate all of the search filter fields into a single search object that's used as the lone parameter of the onFilterChange.
You may think now, "well, why not just do the searching from within the component?" You can, but doing so in a DRY way would mean that on load, you first pass params to the component, a default search is made on didInsertElement which emits a result in an action, which updates the searchResults value. I find this control flow to not be the most obvious. Furthermore, you'd probably need to emit an onSearchError callback, and then potentially other actions / helper options if the act of searching / what search filter params can be applied together ever becomes conditionally dependent on the page in the app.
A component that takes in a search object and emits an action every time a search filter field changes is dead simple to reason about. Since the searchParams are one-way bound, any route that uses this component in it's template can control whether a field field updates (by optionally preventing the updating of searchParams in an invalid case) or whether the search ever fires based of validation rules that may differ between routes. Plus, theres no mocking of dependencies during unit testing.
Think twice before using subroutes
For the subroutes part of your question, I've found deeply nested routes to almost always be an antipattern. By deeply, I mean beyond app->first-child->second child where the first child is a sort of menu like structure that controls the changing between the different displays at the second child level by simple {{link-to}} helpers. If I have to share state between parents and children, I create a first-child-routes-shared-state service rather than doing the modelFor or controllerFor song and dance.
You must also consider when debating using children route vs handlebars {{if}} {{else}} sections whether the back button behavior should return to the previous step or return to the route before you entered the whole section. In a Wire transfer wizard that goes from create -> review -> complete, should I really be able to press the back button from complete to review after already having made the payment?
In the searchFilter + displayData case, they're always in the same route for me. If the search values need to be persistent on URL refresh, I opt for query params.
Lastly, note well that just because /users/:id/profile implies nesting, you can also just use this.route('user-profile', { 'path' : 'users/:id/profile' }) and avoid the nesting altogether.
I am attempting to learn ember.js to produce a simple-ish web application. I have read around the subject and completed the basic tutorial on emberjs.com . So far, I have managed to create the beginnings of an API using PHP and I have been able to retrieve that data into my fledgling application. So far so good.
As a starter, I wish to retrieve a list of users from the server and populate a select with these values. The select is to sit on the menu bar and the selection from this drives the rest of the data for the application. I have placed the model in models/users.js. The application menu I have defined in templates/application.hbs (not sure if this is considered correct or not).
I am unable to get the users data for the select retrieved from the server unless I visit the users route that I have also setup. How can I ensure that the users data is populated at application initialisation? How would I apply/filter the rest of the application data upon selection from this component - would I need to set a global variable or something?
Additionally, I am using materializecss, which requires $('select').material_select(); to be run in order to render the select input. I have this as:
$( document ).ready(function(){
$('select').material_select();
});
in templates/route.js and works fine on initial page load, but upon navigation to another area and back again, the select is not rendered, and from what I have read, I need to call material_select() again, but I can't for the life of me work out where this call should go.
If anyone is willing to take the time to help me better understand things, I will be very grateful.
Cheers
Tim
Preloading Clarification
I wish to have a select box on the main menu bar (menu-bar component). The user will use this to select a user. All content in the app will be filtered to the currently selected user. I currently have an API endpoint (/users.php) which returns the required data, but I am only able to get my application to call this when I visit the users route, which obviously I won't be doing when the app loads initially. So I am thinking I need to preload the data somehow, unless there is a better way that I am not aware of?
Create a component for menu and put the component in application.hbs
And in component put the materialize stuff like this : (notice to the place)
//components/menu-bar.js
didInsertElement(){
this._super(...arguments);
Ember.$( document ).ready(function(){
Ember.$('select').material_select();
});
}
And
//templates/application.hbs
{{menu-bar}}
{{outlet}}
UPDATE
Create a service holding current user selection and use it in other places that you want be changed by changing the user selection.
These codes is just for showing that the solution for what you want (changing the other parts of app based on changing the selected user in menu) is services.
//services/current-selection.js
...
selectedUser: null,
...
and
//components/menu-bar.js
currentSelection: Ember.inject.service(),
actions: {
selectUser(user){
this.set('currentSelection.selectedUser', user);
}
}
and in another place you want to be notified when selection changed :
//components/user-info.js
currentSelection: Ember.inject.service(),
and corresponding template
//templates/components/user-info.hbs
{{#if currentSelection.selectedUser}}
<span>Current Username: <strong>{{currentSelection.selectedUser}}</strong></span>
{{/if}}
Update For pre-loading users
Actually it's not pre-loading
In menu-bar component you load a select box and populate it by calling a service to backend. If you put this component in application.hbs you could ensure what you want is satisfied.
//components/menu-bar.js
store: Ember.inject.store(),
users: Ember.computed(function(){
return this.get('store').findAll('user');
}),
OR
In application route model hook you fetch those users and pass to component like {{menu-bar users=model}}
And for these two options you can use users in menu-bar template.
But pay attention to that if the users count is a lot, this is very bad for performance or UX. If your users are a lot it's better to use from a autocomplete component
On one Ember route, I have one side a list of records (foos) on the left, and on the other side a form. Usually, I'd route it like so: /foos/bar/1/edit. However, I also have a list of baz records, that can be displayed on the left, next to the form.
Ideally, I'd like to be able to have the user be able to alternate between the two lists on the left, while preserving the form on the right.
My first attempt to do this was to load both lists in the same route, and toggle them with a tab jQuery plugin. However, this poses several problems related to pagination and back button/refreshing.
I've also tried putting the lists in their own routes (i.e. /foos/bars/1/edit and /bazs/bars/1/edit). But I can't figure out how to link to a different list without losing the edit page.
Is there a better way to accomplish this?
I think the best way would be using the separate routes but instead of /foos/bars/1/edit and /bazs/bars/1/edit having the form in the main parent resource then foos and bazs as the sub resources. That way when you transition the routes for which list you want displayed it will changed the template that gets rendered in the outlet but maintaining the form.
My application has a list view (master) containing a data sheet view in a sub view element.
In the list view, I would like to use some control like a button or a combo box to filter the data in the sub view. How can I pass a parameter for the filter from the master view to the sub view?
I don't believe the scenario you are looking at here will be directly possible within the Access web app context. Let me explain.
In Access 2013 web apps, there is no macro action available to requery or refresh a specific control on a view. The same goes for trying to refresh a Subview control on a view. The only way you can pass parameters to a different view in the web app context is by using the OpenPopup macro action. In that case the view will open as a popup which is not what you want here either.
So you might not be able to achieve your end goal. One suggestion that might work is to have say an unbound text box control on the main parent view. For the Subview control, use that unbound control as the Master Field (in the property list). Access will attempt to match records from this unbound control to whatever field you designate as the Child Field property. If you update that unbound text box control on the main view, Access should filter the results in the Subview. I "think" that will work.
It works in my app. SubForm updates with filter when focus leaves the txtbox. Unfortunately, you can only search one field per subview as it is set as a property at design time and AFAIK there is no way to change at runtime.