Extjs 4 MVC loading a view from controller - model-view-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

Related

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

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.

Cakephp 3 View variables set in App controller not available when rendering specific template / AJAX requests

I am setting few view variables within my App controller such as company name, address, contact information which changes based on sub domains so that they are available throughout all view templates. However I am struggling to identify why the are not available when making ajax request.
//App Controller beforeFilter
$this->set('company', 'Test Company');
$this->set('address', '14 Test Street, Test, TE5 3ST');
$this->set('email', 'test#test.com');
Above variable are available for all none ajax i.e when I am not rendering specific template request however for below example request I am not able to access those variables in test_data template.
function _ajaxGetTestData()
{
$view = new View();
$content = $view->render('Home/Ajax/test_data');
$response['content'] = $content;
$response['success'] = TRUE;
$this->set(compact('response'));
$this->set('_serialize', ['response']);
}
That is because you did not serialize the variables from the app controller.
You can try in your method:
$response['company'] = $company;
$response['address'] = $address;
$response['email'] = $email;
Or https://book.cakephp.org/3/en/views/json-and-xml-views.html#using-a-data-view-with-template-files
You have to set view variables before render is called.
$this->set('data');
$this->render('custom_view');
When you call $this->set it sets the view variables on the Controller class. These variables are eventually passed to the View Builder, which creates a new View class and returns a Result containing the HTML for this new View.
When you want to render your own View manually you need to pass it the view variables manually too - $this->set isn't setting view variables in this new View class you created here:
$view = new View();
$content = $view->render('Home/Ajax/test_data'); // Has nothing to do with $this->set, you'd have to pass the variables in manually
This isn't generally the simplest approach to take to render an AJAX view.
While you can generally continue to use $this->set in beforeFilter as you already are:
public function beforeFilter(Event $event)
{
$this->set('company', 'Test Company');
$this->set('address', '14 Test Street, Test, TE5 3ST');
$this->set('email', 'test#test.com');
}
.. the easiest method to make an AJAX-compatible is to enable the JSON/XML handler let the built-in JSON/XML renderers do their magic.
In the action function (index/view/edit/whatever) just include the company/address/email in the _serialize variable.
For example, a "view" function might look like:
public function view($id = null)
{
// Do regular view stuff:
$entity = $this->MyTable->get($id);
$this->set('entity', $entity);
// Include ALL the variables you want in the response in _serialize:
$this->set('_serialize', ['entity', 'company','address', 'email']);
}
If you are sure you need a custom template (which isn't required), don't render it manually, just set the template when AJAX is detected:
if($this->request->is('ajax')){
$this->viewBuilder()->setTemplate('Home/Ajax/test_data');
}
This will automatically be rendered for you, using the variables you set with $this->set.
If you want to make a global custom template (for example, wrapping all your data with a "response" node), for all AJAX requests, use a new Layout instead of a custom template:
if($this->request->is('ajax')){
$this->viewBuilder()->setLayout('custom_json');
}
Create this layout in src/Template/Layout/custom_json.ctp and format it as you wish, for example:
<?php
/**
* #var \App\View\AppView $this
*/
?>
{"response": <?= $this->fetch('content') ?> }
See from docs:
Enabling Data Views https://book.cakephp.org/3/en/views/json-and-xml-views.html#enabling-data-views-in-your-application
Using the _serialize Key https://book.cakephp.org/3/en/views/json-and-xml-views.html#using-data-views-with-the-serialize-key
Custom Layouts https://book.cakephp.org/3/en/views.html#layouts

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.

namespacing with js prototype framework

When you create a class in the name space of example.
em.components.grid
em.components.grid.Popup = Class.create(
{
initialize: function(params){
...
},
show:function(){
// create or show
}
});
Does this mean in other classes I have access to the show method if I use the namespace path above.
// Another class in prototype
em.components.grid.Popup.show();
Or does your new class your trying to access show from have to be in the same namespace.
Is namespacing kind of like packages in other languages. So by giving a namespace you can keep all your classes related to for example grid in one name space and possible other classes unrelated to grid in another namespace.
Update
This raises 2 other questions, lets say i create my class like above with the same namespace. Then in another js document I instantiate the class
var popup = new em.components.grid.Popup()
Then popup would be a global variable not? which I don't want to have in my files if possible. Seen as I have went to all the trouble of giving it a unique name space. To then create an instance of the class on a global variable somewhere else in a js file.
So in the case of a popup is it best to have it global or would it be best to create it on a rollover event and remove it on a rollout event.
//pseudo code
$$('domelementClass').observe('mouseover', function(event) {
var popup= new em.components.grid.Popup(event.target);
})
the issue I see with above is I have no reference to remove it on the rollout.
$$('domelementClass').observe('mouseout', function(event) {
popup.remove();
})
Namespacing has the same purpose of packaging, avoiding collision. As your example above shows, in JavaScript, you namespace functions and variables by making them properties of an object.
Does this mean in other classes I have access to the show method if I
use the namespace path above.
// Another class in prototype em.components.grid.Popup.show();
In this case no because 'show()' is an instance method, it can only be called once you have a new Popup. You can use your namespaced Popup as an instance in another class or if you want to call show like a static method in Java then you would call Popup.prototype.show();
var Popup = Class.create({
initialize: function(params){
alert("I exist");
},
show:function(){
alert("show!");
}
});
// Popup.show(); // would error:
// Uncaught TypeError: Object function klass() {
// this.initialize.apply(this, arguments);
// } has no method 'show'
Popup.prototype.show();
foo = new Popup();
foo.show();
Some useful links:
http://michaux.ca/articles/javascript-namespacing
http://blog.anselmbradford.com/2009/04/09/object-oriented-javascript-tip-creating-static-methods-instance-methods/

Passing Instance to Shared Method in VB.NET

I have been thrown in at the deep end with an existing VB.NET project at work. I have never used VB.NET before so I am struggling a little. Does anyone know how to solve the following.
I need to pass an instance to client side and then pass it to a shared method in order to access instance methods from when the shared method.
The starting point is a fileupload control within the HTML of my Contacts.aspx file:
<asp:FileUpload ID="DocUpload1" runat="server" onchange="CallMe();" />
The onchange event calls a javascript method, see below, this uses AJAX PageMethods to called a Shared method in my code behind
This is the script code which is in my Contact.aspx file
<script language="javascript">
function CallMe() {
// call server side method
PageMethods.GetContact(0, CallSuccess, CallFailed, null);
}
// set the destination textbox value with the ContactName
function CallSuccess(res, destCtrl) {
}
// alert message on some failure
function CallFailed(res, destCtrl) {
alert(res.get_message());
}
</script>
This is an example class of the type of thing I want to do, I think I need to use the "instance As Contacts" as an input parameter to the WebMethod function but I don't know how to pass the instance into it:
This is the class within my Contacts.aspx.vb file.
Partial Class Contacts
<System.Web.Services.WebMethod()> _
Public Shared Function GetContact(ByVal instance As Contacts) As String
Return instance.GetContactName() 'This is an instance class which I need to call.
End Function
'This is my instance class which I want to call from the Shared Class.
Public Shared Function GetContactName() As String
Return "Fred Bloggs"
End Function
End Class
If anyone knows the solution please could they update the code as I probably won't be able to understand if you just give a description. I just hope I am along the right tracks.
If I understand you correctly, you want to access a class (your instance) created in the ASP.Net page life-cycle from your PageMethod - eg created during initial page load or file upload etc.
This is not directly possible as PageMethods do not go through the full page life-cycle (they are essentially webservices). So you need to pass some sort of identifier to the client that, when passed back to the server in the PageMethod, can be used to re-create or retrieve your instance.
eg During the initial page load:
session("ContactID") = instance
Your PageMethod might look something like:
Public Shared Function GetContact(ByVal key As String) As String
Return HttpContext.Current.Session(key).GetContactName()
End Function
where the parameter key is the same key you used to store your instance in the session state.
In your javascript:
function CallMe() {
// call server side method
PageMethods.GetContact('ContactID', CallSuccess, CallFailed, null);
}

Resources