<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">TL;DR: we need to fix workflows :-)</div><div class="gmail_quote"><br></div><div class="gmail_quote">On 15 July 2016 at 16:32, Tripp, Travis S <span dir="ltr"><<a href="mailto:travis.tripp@hpe.com" target="_blank">travis.tripp@hpe.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">If we find that there is still a need to send the scope in that can’t be reasonably worked around, then the action services could be changed to be a factory.</blockquote><div><br></div><div>I think this is a bad idea because in the angular world "factory" means singleton (which is totally opposite to what everyone else in the programming world [and the rest] thinks "factory" means but hey, angular gonna angular) and we'd be changing that, and the potential for confusion would be very high.</div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">The injected action factory should have a factory function called by the controller which creates an instance of the action in each controller.</blockquote><div><br></div><div>Controllers are already not singletons - I see no reason why mutable state shouldn't be contained in *them*. We don't need to invent something new to hold that mutable state.</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Then the init scope could be called on the instance of that action. This also would then allow actions to have state that is retained for the lifetime of the controller, while not worrying about sharing action state with other controllers.<br></blockquote><div><br></div><div><div>I don't believe retaining "init scope" is necessary at all.</div><div><br></div><div>As far as I can tell we're already creating an independent context for the actual meat of the action, we're just not keeping it independent enough. To demonstrate, I'm going to pull apart one example: images create-volume.<br></div><div><br></div><div>The files for create volume consist of (under openstack_dashboard/static/app/core/images):</div><div><br></div><div>actions/create-volume.service.js</div><div>  Is the action fired off when a user hits a "create volume" button.</div><div>workflows/create-volume.service.js</div><div>  Is the workflow service used in the modal dialog that the above action service creates.</div><div>steps/create-volume/create-volume.html</div><div>  Is the single HTML step in the above workflow service.</div><div>steps/create-volume/create-volume.controller.js</div><div>  Is the controller used in the above HTML step.</div><div><br></div><div>Note that the workflow only has one step. I think that's done so plugins can extend create-volume with additional steps. Regardless, it's the workflow that's the root of our problems here. Read on... ;-)</div><div><br></div><div>The flow through the above code is basically:</div><div><br></div><div>1. allow an interface to invoke an action perform() to create a volume</div><div>2. open a modal with a form in it to capture the volume details</div><div>3. have the service from step 1 invoke an API with the data captured in the form</div><div>4. resolve the action service perform() promise with an ActionResult<br></div><div><br></div><div>The scope usage is needed because the data transfer between the step controller and the action service is done through an event, VOLUME_CHANGED, and event passing is done through a scope. Thus the controller and action service, which are quite independent pieces of code, must have intimate knowledge of each other's internal operation. I'm not so keen on that ;-)</div></div><div><br></div><div>The event is currently necessary because of the workflow sitting between the action service and controller. Without it, the controller submit() handler could resolve the $modal promise with the captured data, which the action service could consume. What we should be seeing is the workflow service resolving that $modal promise with the data captured from the workflow, and that promise resolution going back to the action service.</div><div><br></div><div>But the workflow implementation has no concept of an over-arching model for the workflow. If that was changed, I believe all the current $scope shenanigans (which are basically about short-circuiting the workflow not doing its job ;-) would go away.</div><div><br></div><div>So, here's my rough thought: workflow.model is an object with properties named for each of the workflow steps - using the step formName as the name (hell, schema form could probably make this a doddle). The workflow model is passed to the controller for each step, which uses its own named model to store the data captured by the step - and as a side effect it can poke at (and watch) the data captured by other steps, which is often useful. Workflow $modal resolution supplies the workflow model for the consumer of the workflow to then to something with all that data.</div><div><br></div><div><br></div><div>     Richard</div><div><br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">From: Richard Jones <<a href="mailto:r1chardj0n3s@gmail.com">r1chardj0n3s@gmail.com</a>><br>
Reply-To: OpenStack List <<a href="mailto:openstack-dev@lists.openstack.org">openstack-dev@lists.openstack.org</a>><br>
Date: Friday, July 15, 2016 at 2:28 PM<br>
To: OpenStack List <<a href="mailto:openstack-dev@lists.openstack.org">openstack-dev@lists.openstack.org</a>><br>
Subject: [openstack-dev] [Horizon] Angular action services and initScope<br>
<div><div class="h5"><br>
Hi folks,<br>
<br>
Something that's been bothering me for a while is that the action services break the encapsulation model of Angular Services - that they are singletons, and consumable by multiple simultaneous consumers without those consumers affecting each other through their use of the Service.<br>
<br>
At the moment the initScope() functionality we've included in the action services breaks that model - at a minimum it is possible for multiple consumers to initScope() with different scopes simultaneously. This is the reason why I've been arguing (ok, "debating" :-) for the cessation of using scopes in this way.<br>
<br>
I think we need to do two things reasonably soon (before patterns become more ingrained):<br>
<br>
1. Stop passing in $scope to initScope in all cases - the new ActionResult-enabled pattern should hopefully replace all those<br>
2. Remove all initScope methods altogether. The only other use of initScope that I see is the pre-loading of data used during the execution of action allowed() methods. We should move that preloading/caching either into the creation of the Service object itself, or into the allowed method.<br>
<br>
If there is a use-case of initScope that I've missed (something that needs to be execute *after* the Service is created, not something that needs to tie the Service to a particular consumer of the service) then please let me know :-)<br>
<br>
<br>
     Richard<br>
<br>
</div></div>__________________________________________________________________________<br>
OpenStack Development Mailing List (not for usage questions)<br>
Unsubscribe: <a href="http://OpenStack-dev-request@lists.openstack.org?subject:unsubscribe" rel="noreferrer" target="_blank">OpenStack-dev-request@lists.openstack.org?subject:unsubscribe</a><br>
<a href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev" rel="noreferrer" target="_blank">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev</a><br>
</blockquote></div><br></div></div>