UI5 data binding via this.getOwnerComponent().getModel() or via this.getView().getModel()? - performance

The common way to make a data binding in UI5 is via this.getView().getModel():
const wantedModel = this.getView().getModel("wantedModel");
this.getView().setModel(wantedModel);
However, it's possible to perform a binding via this.getOwnerComponent().getModel() as well:
const wantedModel = this.getOwnerComponent().getModel("wantedModel");
this.getView().setModel(wantedModel);
In my case, the data model is defined in manifest.json, and I have two options to retrieve a model for a binding:
via getView() in onBeforeRendering(), which is called every time a view is rendered
via getOwnerComponent() in onInit(), which is called only once.
To avoid setting a model every time a view is rendered, I consider using the second approach. What are the possible advantages/drawbacks, primarily performance-related, of getting a model via getOwnerComponent() over getView()?

In the base controller, from which all other controllers will be inherited, create a method like this:
sap.ui.define([
"sap/ui/core/Core"
], (Core) => Controller.extend("com.myapp.controller.BaseController", {
getModel(sName) {
const dataModel = this.getView().getModel(sName)
|| this.getOwnerComponent().getModel(sName)
|| Core.getModel(sName);
return dataModel;
}
}));
Then put the base controller in your custom library and reuse it in the future.

Related

Laravel Query Multiple times on ViewServiceProvider

I put my global variable using view->with() on ViewServiceProvider.php but the problem is it query multiple times on single page. is this a problem or it's really just this? it shows multiple times select * from weather where id = 1 below
Yes, so the problem here is because you are using * for the view composer which means that you want to execute this code for every single view that is being rendered. A better approach is to include it to the partial that uses the weather instance, for example:
view()->composer('partial.nav', function($view) {}); // this will be used only on the navigation partial view.
// multiple views like this:
view()->composer(
['profile', 'dashboard'],
function($view) {}
);
// or create a view composer class and register it as a singleton
$this->app->singleton(\App\Http\Composers\WeatherComposer::class);

Apply MVC pattern in ES6 for invoking methods

I am using the Model-View-Controller framework to structure my files for readable, reusable, and refactorable purposes.
My goal is to invoke methods from two seperate classes while working with one class. In practice; I want to access the methods for example in file Model.js and View.js from Controller.js.
Previously in ES5 I've had one file called app.js that used the IIFE approach:
var View = (function(){
dump(){
console.log('Hello World');
}
});
var Model = (function(){
// Code goes here
});
var Controller = (function(viewCtrl, viewCtrl){
viewCtrl.dump(); // Invoke method from View
})(View, Model);
As shown above, I would like to do something similar in ES6 too.
import View from './View';
import Model from './Model';
class Controller {
dump(){
return viewCtrl.dump();
}
init(){
console.log('Application has started');
// Make a new object of the class { View, Model }
let view = new View();
let model = new Model();
}
}
export default Controller;
In my main.js:
import Controller from './Controller';
// Make a new object of the class Controller
let controller = new Controller();
// Instantiate App
controller.init();
console.log(
controller.dump()
);
But doing so in ES6 I get error: ReferenceError: viewCtrl is not defined in main.js.
I was thinking perhaps pass View.js and Model.js as arguments in a constructor inside the Controller.js in order to declare them. But I guess it might be a better solution that looks cleaner.
So what I am basically looking for is to apply the MVC pattern using ES6, any recommendations?
Passing the instances of View and Model to the constructor of Controller is a clean solution since this would fullfill the dependency-injection-pattern.
This way you get the ability to change the instances from outside which makes the Controller testable.
In a situation where View and Model where singletons you could export them as instances instead of classes like
View.js:
export default new View();
Controller.js:
import view from "./View";
...
view.dump()
Using this way you could spare the work of passing a model and a view to a Controller manually but that would also mean that you loose the ability to test Controller or to change parts of it, like the View. Since this is one of the best advantages of the MVC-pattern, I would not recommend to import singletons. Instead I would recommend to inject View and Model using a constructor.

OctoberCMS: How to use a component's functions without knowing the component's name

In OctoberCMS, I would like to change a page process simply by attaching a different plugin component.
I have a plugin component (makeform) which inserts a form (defined in a database table).
Clicking the form's submit button calls onSubmit() which calls process().
process() is a function defined in another plugin component.
i) Can I call process() from within makeform without knowing the name of the other plugin or its component? That is, without having to use 'Acme\Plugin\Components\ProcessForm', 'processForm';
ii) Alternatively, can I programmatically discover the name of the other attached component and it's plugin, and then somehow specify it's process() function?
iii) Or is it a case of using a static properties dropdown to choose which process, and then adding the component dynamically. Always assuming I can $this->addComponent('Acme\Plugin\Components\ProcessForm', 'processForm'); beyond init().
Edit: Experimentation
I was hoping to dynamically addComponent().
Wherever I place it, in init() or elsewhere, I get the error:
Class name is not registered for the component "Acme\Plugin\Components\ProcessForm". Check the component plugin.
Even if I'm not using one of it's classes.
Many online references to this error message, but not one that has helped me.
Edit: Further explanation
A (hopefully) simplified explanation of what I'm trying to achieve.
In essence I imagine a page process consisting of a string of components.
Each component calls a function in the next component until the process ends.
The overall process can be modified simply by replacing components.
I am guessing the only way to connect components is by standardizing the function names. So this (probably?) requires components to belong to particular stages of the process, although it would be ideal if each of them can fit in at any stage (where appropriate).
illustration
extend the base component class and add your function und let the components extends from this?
make a new helper class with the function maybe call it static or so
add a global function
https://octobercms.com/forum/post/global-php-classesfunctions
I think the best approach would be to define another property in which you set the namespace of the plugin.
public function defineProperties(){
'pluginName' => [
'label' => 'Plugin Namespace to call process method from',
'type' => 'text'
]
}
--
public function onSubmit(){
$plugin = $this->property('pluginName');
$plugin::process($formData);
}
In this way you keep the component logic clean from any hardcoded plugin names.
Edit: 30/10/17
I'm not sure there's a way to list all available components inside the application. An alternative is to set up a Settings page, with a repeater of sorts in which you declare all available components with namespaces.
You than parse this to an array inside the onSubmit method and return this to the dropdown.
public function defineProperties(){
'components' => [
'label' => 'Plugin Namespace to call process method from',
'type' => 'dropdown',
'options' => 'getComponentsOptions' // optional but easier to understand
]
}
public function getComponentsOptions(){
$components = Settings::get('components');
$options = [];
foreach ($components as $component)
{
$options[$component['namespace']] = $component['name'];
}
return $options;
}
/Models/Settings/fields.yaml
fields:
components:
type: repeater
form:
fields:
name:
placeholder: My Component Name
span: left
namespace:
placeholder: Acme\Name\Components\MyComponent;
span: right
/Models/Settings.php
class Settings extends Model
{
public $implement = ['System.Behaviors.SettingsModel'];
// A unique code
public $settingsCode = 'acme_name_settings';
// Reference to field configuration
public $settingsFields = 'fields.yaml';
}
http://octobercms.com/docs/plugin/settings#introduction

Ember JS: How to load a second model based on data from a first

I have an Ember app that, rather than using Ember Data, uses our own custom AJAX data layer to talk to an API.
We're able to load two models at once using RSVP - one is a Project object via our API wrapper, the second is an object representing the logged in user. Both are passed into the controller and templates and work just fine.
But I have a need to load a second model, based on a value in the returned Project object.
Once I've loaded a model in my route like this...
App.ProjectUpdateRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('model', model);
},
model: function(params) {
return Ember.RSVP.hash({
// Load Project from API - /myapi/v1/Project/:ProjectID
Project : App.Project.create().findById(params.ProjectID),
// Load current user from local object
User : App.AuthUser,
});
},
});
...I have a Project object (or rather model.Project) with various properties including the ID of the User that owns the project.
Now I'd like to make a second API call to /myapi/v1/User/:UserID to get the User's details.
Everything I've tried - including adding further App.User.create().findById(UserID) calls into the route's setupController function and the controller - results in the correct API call, but it's asyncronous, so Ember carries on rendering the page and I can't show the result of the API call on the page.
So - how, and where in the Ember structure, do I load a second model based on data from the first? How can I get ember to wait for the resolved promise of this second AJAX call?
UPDATE
I've also tried using afterModel:function() which is almost what I need - it makes the second API call in the right place in the app flow, but I still need to add the result into my existing model array:
afterModel: function(model, tranistion, params) {
// Returns the promise but doesn't update 'model'
return Ember.RSVP.hash({
ProjectOwner : App.User.create().findById(model.Project.UserID)
});
}
Chain the promise, and Ember will take the final resultant (from the model hook)
model: function(params) {
return Ember.RSVP.hash({
// Load Project from API - /myapi/v1/Project/:ProjectID
Project : App.Project.create().findById(params.ProjectID),
// Load current user from local object
User : App.AuthUser,
}).then(function(results){
return App.User.create().findById(results.Project.UserID).then(function(user){
results.projectUser = user;
return results;
});
});
},

Extjs 4 MVC loading a view from controller

Ok so I have a controller with a method in which I want to load a view.
How do I load a view from a controller?
How do I pass some parameters from the controller to the view when I load it?
Any help is much appreciated.
To load a view, you can use Ext.widget(). Use Ext.define() to create a view in your view file. I would recommend using the alias property to define an inline xtype for the view.
When you need to load the view, you create an view using Ext.widget() and specify the xtype (alias for your view). Here is an example:
// define a window
Ext.define('MyApp.view.user.Add',
extend: 'Ext.window.Window',
alias : 'widget.adduser',
.
. // Add other properties, custom properties, methods, event handlers etc..
});
Now, when you want to create an instance in your user controller, you do:
// create an instance
var view = Ext.widget('adduser'); // refer the below note!
Note: note that there is no 'widget.'! it automatically gets added to the widget name you pass.
Now, taking about passing parameters. Like Ext.create method, you should be able to pass any parameters as:
// create an instance with params
var view = Ext.widget('adduser', {title: 'New User title'});
Regarding ref: refs help you in getting references to Views on your page. They do not help in creating an instance or load a view. If you have your view rendered, you can make use of the ref system to get hold of that instance and manipulate the view. You need to make use of the ComponentQuery to get reference of your view.
refs can be used to create new instances as well as access existing ones. By adding the option autoCreate: true to your ref, a call to the getter will result in a new instance being created using the ref definition as its config if no existing component matches the selector.
refs: [{
ref: 'list'
,selector: 'myusersgrid#users'
,autoCreate: true
// any additional options get passed as config when an instance needs to be created
,xtype: 'myusersgrid'
,itemId: 'users'
,store: 'Users'
,title: 'Users'
},{
ref: 'otherList'
,selector: 'myusersgrid#administrators'
,autoCreate: true
// any additional options get passed as config when an instance needs to be created
,xtype: 'myusersgrid'
,itemId: 'administrators'
,store: 'SpecialUsers'
,title: 'Special Users'
}],
Notice the use of the # to additionally match the itemId so I could have refs to multiple instances of the same xtype
There's also a forceCreate: true option which will make the ref's getter always return a new instance, without it autoCreate will create one instance the first time it's retrieved and then keep returning the same one.
If I understand your question I think you want to use refs, take a look at the docs for Ext.app.Controller: http://dev.sencha.com/deploy/ext-4.0.0/docs/api/Ext.app.Controller.html
Basically you create a list of refs using css selectors:
refs: [
{
ref: 'list',
selector: 'grid'
}
],
Then later in the class you can access this ref by using get, i.e.:
refreshGrid: function() {
this.getList().store.load();
}
The getList() method is created for you when you create the ref to 'list'.
I ran into this same problem. I created a method on my abstract base controller to retrieve the view instance and create on if it does not exist.
This will work properly even after the view has been destroyed - a new one will be created.
Ext.define('My.controller.Base', {
extend: 'Ext.app.Controller',
//Retrieves an instance of the top-level view
//If it has not been created yet than one is instantiated
//Also, overrides the .close() method on the view to
//null out the instance reference on the controller (very necessary)
getViewInstance: function () {
var self = this;
if(!this.viewInstance) {
if(this.views && this.views.length) {
var view = this.getView(this.views[0]);
this.viewInstance = view.create();
this.viewInstance.close = function () {
view.prototype.close.apply(this, arguments);
self.viewInstance = null;
};
}
}
return this.viewInstance;
}
});
Now all my controllers can easily access their view from w/i controller code w/o any external variables.
Use Ext.create('Proper File Name to be opened',param1 = me);
In the newly created view, use this.param1 to access the parameters.
EG: Ext.create('view.HelloOverlay, param1 = "Hello", param2 = "World");
in the controller of HelloOverlay, using this.param1 will give "Hello" and this.param2 will give "World".
Sometimes the parameters passed will be present in the view so use this.getView().paramName

Resources