App_1 has a view and a template for this view. It would like to "aggregate" information into this view from other sources (i.e. other apps), without having to add or change anything itself. Perhaps App_2 wants to put "hello world" into this designated area in App_1's view. What is the best way of achieving this?
Signals come to mind naturally. But what should signal receivers send back? In my mind, App_1 template includes a custom tag that sends a signal and receives a list of templates (e.g. 'app_2/template.html') back, and it will register each one. However, it seems like inclusion_tag only supports 1 template rendering for each tag.
What recourse do I have?
I ended up doing what I described, but without the default inclusion_tag support. I made a custom tag that sent signals, collected template names, and created a Node that renders each template in series and concatenates the result. This seems to work fine, and has the same logic as the inclusion tag shebang.
I hope I'm understanding right, but it would seem that your solution to this problem is in your second sentence: 'aggregate'. The view function in App_1 should aggregate, or collect, all of the necessary data, then pass it to the template via a context variable.
I think the question that needs to be resolved is: How does the view function know which data to aggregate?
Typically a view function is coded ahead of time with a knowledge of which data it needs to use. For example, a calendar app might be hard-coded to fetch Event model objects and pass those to the template (via context). In your case however, it seems that the data coming into App_1 is completely arbitrary and not previously defined.
You mentioned signals, but I think the problem that you're going to have here is that signals only give notification that an event has occurred. They don't allow the reeving function to pass data back, nor does the code that emits the signal wait for all of the signal receivers to finish processing before it continues.
Instead perhaps you could establish a registration system. App_ 1 maintains a list of something. The other apps 'register' items to that list and when the view function in App_1 fires, it turns the list into usable data, then passes it to the template via a context variable.
Related
Context:
I was going through various guides, tutorials and also posts here in the stackoverflow.
I need to handle process, e.g., - “pick topic —> show author list —> delete all —> ask confirmation —> show result —> go back to author list”
The obvious (and simplistic) approach is to create sets of views for each process.
And save process state in session variables or pass via URL-s (however, I discovered this method can be very brittle if too many information is passed). Also I need to tell each view where to go next, and this mushrooms into a bulky chain of instructions repeated for each URL request.
Instead, I want two extra things:
encapsulate a process at single place
re-use views, e.g. to re-use Topic List view to pick a single topic. That is, to work in “picker mode”. (btw, if I try to do this with multiple view classes, the next view class needs to know what to do and where to return…)
I also checked django workflow libs, and so on. and still not convinced.
Question:
if I create global object to encapsulate process and reference it from the current session - is this a good idea? My main concern is that - as sessions come and go, who will be cleaning up this global object?
is it possible to somehow make this clean up automatic? that is, when session goes away, some method is called where I can clean up or help GC to collect unneeded object? Like C++ destructors?
after working further with code, it occured to me that if I add global object to the session (and nowhere else) - it must be garbage collected in due course, because session itself is managed by Django.....
also, reading https://docs.djangoproject.com/en/3.1/topics/class-based-views/intro/ in my 3rd pass, found that Django's as_view() instatiates a new instance of class based views on each HTTP request.
so, everything is per request basis, and data will be transient. So only way to maintain state across HTTP requests is to either persist in the database or use sessions (or keep passing "state" from each request to request via URL parameters, but it is bulky, unsafe. my understanding is that this is a fallback mechanism if cookies are not allowed and session can be simulated via this way, e.g. PHP does it).
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.
This problem has been bugging me for awhile and I can not seem to get around it. So I stripped it down to the most basic level.
1. created a new XPage and bound it to an exiting form
2. created a panel called 'displayPanel'
3. inside the panel create a comboBox and give it a few values and a default value of any valid value.
4. Set an onChange event that does a partial refresh of displayPanel
5. Add a computed field that simply displays the value of the comboBox.
6 added a button that does a partial refresh of displayPanel onClick.
Open the XPage and the computed field is blank, make a change and the computed field displays.
open the XPage again and click the refresh button and the computed field displays
Now this is a very simple example but what I need to do is actually more complex but the value of the comboBox (Does not need to be a comboBox) is not available until a refresh is performed. This is only an issue on a new document when it first gets it's defaults.
I have added this:
view.postScript("XSP.partialRefreshGet('#{id:displayPanel}')")
to every one of the page events but it does not appear to do an actual page refresh like clicking the button or making a change.
I'm at a loss as to how to make this work. If I could get this simple example to work the rest of what I need is easy.
Thanks
Fredrik is on the right track -- you should set the value manually during an event -- but I would add two caveats:
Call setValue instead of replaceItemValue (e.g. document1.setValue("myComboBox", "Default Value");). The comparative advantages might not be applicable in this specific case, but you should be in the habit of always calling setValue instead of replaceItemValue (and getValue instead of getItemValue) so that, when you've encountered a scenario where it makes a real difference, you just get that benefit for free... the rest of the time, the methods are equivalent, so you might as well just use the one that requires less typing. :)
You'll probably need to do this in afterPageLoad: the data source may not be ready yet during beforePageLoad; depending on why you're referencing the default value elsewhere in your page, beforeRenderResponse might be too late; and afterRenderResponse would definitely be too late...
...which leads me to why the defaultValue attribute does not behave the way we might expect it to, especially for those of us with experience developing Notes client apps.
The XPages engine splits the processing of every HTTP request into several "phases". Depending on the type of request (initial page load, partial refresh event, etc.) and other factors, the lifecycle will consist of as many as 6 phases and as few as 2.
This tutorial page provides an excellent description of these phases, but in the context of this question, the following are specifically of interest:
Apply Request Values
When an event runs against a page that has already been loaded (e.g. a user clicks a button, or selects a value from a combobox that has an onChange event, etc.), the HTTP request sent to the server to trigger the event includes POST data that represents the value of all editable components. This phase temporarily stores these values in the submittedValue property of any affected components, but the data source doesn't "know" what the new values are yet.
Process Validations
This phase runs any applicable validators and, if any fail, skips straight to the last phase (which means it never runs the code of the event that was triggered).
Update Model Values
If no validations fail (or none are defined), this phase takes the submitted values and actually stores them in the corresponding data source. Until this point, the data source is completely unaware that there has been any user interaction. This is intended to avoid prematurely polluting any back end data with user input that might not even be valid. Remember, not every data source is a "document"; it might be relational data that is changed via an UPDATE the instant setValue is called... which is basically what this phase does: it takes the submittedValue and calls setValue on the corresponding data source (and then sets submittedValue to null). This separation allows components to simply be visual representations of the state of the back end data -- visual representations that the user interacts with; our code should always be interacting directly with the back end data via the abstraction layer of a data source.
Render Response
Once all of the other phases have run (or been skipped), this phase sends a response to the consumer. In other words, it sends HTML (or JSON, or XML, or PDF, etc.) back to the browser. But the most salient point in the context of this question is that the prior phases are always skipped on initial page load. When the user first accesses a page, they haven't had a chance to enter any data yet, so there are no request values to be applied. Since no data has been posted, there's nothing to validate. And -- most pertinent of all -- there's nothing to be pushed to the data model. So a representation of the component tree (or a subset of it, in the case of a partial refresh event) always needs to be sent to the user, but until the user interacts with that representation, the data source remains in whatever state it was when the most recent response was rendered... which is why, if you want a specific property of the data source to have a specific value before the user interacts with it, your code needs to set that property's value manually.
In summary, components are visual. Data sources are abstract. Perhaps this behavior would be more self-explanatory if the component property had simply been called defaultClientValue instead of defaultValue, because that's essentially what it is: a default suggestion to the user for what data to send back to the data source. But until they do, the data source has not received that value. So if, on initial page load, you need a data source property to have a value that it wouldn't already have in its default state, you should manually call setValue to give it the desired value.
P.S. Ironically, if you'd called partialRefreshPost instead of partialRefreshGet, you likely would have achieved the result you were looking for, because the former sends all the form data, while the latter just asks the existing component state to be rendered again. But then you're forcing all of the form data to be sent just to update one data source property, so it's better to simply do what's described above.
In my application, I am sending periodic cron and background task requests to refresh the cache of pages. While sending a force_refresh kwarg from the view is easy, there's no apparent way to send the force_refresh kwarg to methods being accessed from the template. There are plenty of these I'm using, and it would just make things more complex to start calling all these methods from the view.
So I've been trying to overwrite the template render method so that I pass in the force_refresh kwarg whenever a method is being accessed, if the given response is for a background task request.
I realize that it might simply lead to unexpected problems to add this kwarg to all methods being called, and a try/except ArgumentError block wouldn't exactly be a robust solution, if you have any recommendations about a better way to handle this (hopefully besides accessing each of these methods from the view!), it would be useful to hear them.
Sorry, but your use case is precisely what the view function is for.
In the view function you gather all the data. From this you create a dictionary of the latest data which will be used by the template.
All logic goes in the view. The template has minimal processing.
Lets say I have a website with a small application that lists the 10 newest members, or something dynamic like that. I want this to view on every page, perhaps in a sidebar. How would i go about doing this.
My thoughts on the matter. I might not get the whole django thing just yet, but when I have a url like /foo/ calling a view bar - but what info do I have to send to the template from this view. Does every view have to send the info to the template (just so I can view my app) or is there someway to call this from the template instead.
I have tried to read through the documentation, but its seems I just can't understand this.
The usual way to provide '10 newest members' type of information from other apps is via a template tag. See this article by James Bennett on best practices (although note it's a bit out of date, as it was written before the inclusion_tag and simple_tag shortcuts were available).
Create a template tag that you can call on the page
Create a context processor and inject extra context variables onto each pageload
I'm sure there are other ways of doing this but those are probably the most logical two. The first gives you more power and will waste less processing time (for pages where you don't want to display the data) but a context processor is much more simple to write (you don't have to bend over backwards to please the template_tag gods).
Both are valuable things to know so there you go. Go and learn!
"Does every view have to send the info to the template (just so I can view my app)"
Yes.
"Is there someway to call this from the template instead."
No.
Your views are just functions. Functions can call other functions. That's ordinary good design. You can still do ordinary good design in Django.
You do have the ability to provide a "context". This is still done in the views to provide additional "context" for the templates. See http://docs.djangoproject.com/en/dev/ref/templates/api/#writing-your-own-context-processors for writing your own context processor.
Nothing (well almost nothing) is done in the template except render the objects provided by the view into HTML (or XML).
If you have a page that is an amalgamation of stuff from many small apps, then you have two tiers of apps.
Independent Apps.
Composite Apps that depend on Composite or Independent Apps.
Your composite app can call other app view functions to gather data.
Your composite app template can include other app template elements to present that data.
You have all the power of Python to decompose the independent apps into "data production" functions, view functions, template components and final page templates.
An independent app Page will use a view function and a template. The view function will use the data production functions. The template will use template components.
Decomposition still works, even in Django.