I have a page with a route #grants/{grantId} that contains tabbed content. I want each tab to be accessible by route #grants/{grantId}/{tabId}.
So I've setup router like below and i works, however when I switch tabs (e.g. when i go from #grants/123/tab1 to #gratns/123/tab2) the conent of the page gets rebuild (invalidated). How can I prevent that?
var oRouter = new sap.ui.core.routing.Router([
{
pattern: "grants",
name: "_grants",
view: "grants.Grants",
viewType: sap.ui.core.mvc.ViewType.HTML,
viewId: "grants",
targetControl: "theContent",
targetAggregation: "content",
clearTarget: true,
subroutes: [
{
pattern: "grants/{id}/:period:",
name: "_grantPortfolio",
view: "grants.GrantPortfolio",
viewType: sap.ui.core.mvc.ViewType.HTML,
viewId: "grantPortfolio",
targetControl: "theContent",
targetAggregation: "content",
clearTarget: true
}]
}]);
As far as I understand your question I would suggest to implement a special logic in your controller's route match callback. There you can check - depending on the event parameter - which data needs to be loaded and which data have been loaded already. For more specific hints a running example would be required.
Related
Let's say I have my routes defined in my manifest.json like this
rootView: "sap.ui.core.sample.TargetsStandalone.targetsApp.view.App",
routing: {
config: {
targetsClass: "sap.m.routing.Targets",
viewPath: "sap.ui.core.sample.TargetsStandalone.targetsApp.view",
controlId: "rootControl",
controlAggregation: "pages",
viewType: "XML"
},
targets: {
page1: {
viewName: "View1",
viewLevel: 0
},
page2: {
viewName: "View2",
viewLevel: 1
}
}
}
Is there a way to retrieve the view references from the component(getOwnerComponent). What I want to do is, on the first controller I want to get a reference of label on "View2". It seems like there is no reference to the second view until I navigate to it. Is there a way to get the a reference to another view and its elements before I navigate to it?
When you navigate to a view, the runtime instantiate it. Before that, there is no view. You can even see in the network trace how the XML file of certain view is downloaded once you navigate to it for the first time, but not before.
Could you please help me with MVC implementation with enyojs framework
The current goal is to implement N -panels (displays, windows) for TV application which would be changed after button was pressed (channel switch for instance). It looks just like simple redirect to new URL (or controller's action) in web. So, we want to re-render views and parts of views and update windows(panels) using strategy of keeping just 2 displays at the same time (current and next we go with animation). I learned some examples of MVC with enyo from other topics here, but that implementations of MVC but some questions are still exist.
Problem: how to implement in enyo the particle updating of the view with new data from controller ?
//--- app.js
enyo.kind({
name: "my.Application",
kind: "enyo.Application",
view: "Index", // default start view
controllers: [ // I use 2.3 version and know about this rudiment property.
{name: "homeController", kind: "HomeController"}
],
viewChanged : function() {
this.inherited(arguments);
app.render(); // this updates the view. Changes from index to edit and back.
}
});
enyo.ready(function () {
app = new my.Application({name: "app"});
});
//------ controller.js + IndexView.js + editview.JS
enyo.kind({
name : "IndexView",
kind: "enyo.View",
components: [
{content: "HelloWorld, This is Index"},
{content: "This is the Index view"},
{kind: "moon.ToggleButton", content: "Show Edit", ontap: "buttonTapped"}
],
// redirect to Edit View
buttonTapped: function(sender, event){
app.controllers.homeController.Edit("message(model) to Edit View");
}
});
enyo.kind({
name : "EditView",
kind: "enyo.View",
message: "no msg",
components: [
{name: "headWithId", content: "Hello! This is EDIT."},
{content: "This is the Edit view"},
{kind: "moon.ToggleButton", content: "Show Index", ontap: "buttonTapped"}
],
bindings: [
{from: ".message", to:".$.headWithId.content"}
],
// redirect to Index View
buttonTapped: function(sender, event){
app.controllers.homeController.Index();
}
});
enyo.kind({
name : "HomeController",
kind: "enyo.Controller",
Index : function(){
app.set("view", new IndexView()); // this code updates the view of app, but maybe there is a better way to do it?
},
Edit : function(msg){
app.set("view", new EditView({message: msg}));
}
});
The previous code works find. There some question in comments. But how to implement such situation, then I don't want to rerender all the divs of the view, but just particle content (for example, leave the header and update the content):
// ----- baselayoutview.js
enyo.kind({
name : "BaseLayout",
kind: "enyo.ViewController", // or enyo.View... what is better?
components: [
{content: "this content of the View never changes"},
// next content should be changed. It's for renderTarget
{name: "RenderContentSection"} // How to render new content form Index or Edit view here?
]
});
// ----- app.js
enyo.kind({
name: "my.Application",
kind: "enyo.Application",
view: "BaseLayout", // We set this base layout view and expect the it will fill it's content by itself
controllers: [
{name: "homeController", kind: "HomeController"}
],
viewChanged : function() {
this.inherited(arguments);
app.render("RenderContentSection"); // I think it would not be working any more.. but what it the best way to do it? How to update BaseLayout view's RenderContentSection ?
}
});
You should not be calling app.render() to re-render your application. As you say, you don't want to re-render the whole thing each time. Are you stuck on Enyo 2.3 or can you update to a more recent version? Controllers have been largely deprecated, for one.
I suggest taking a look at the Panels component. You can place a Panels component within your area that changes and navigate to a panel and push whatever content you want into that panel. You individual sections would be Panel components and you can push and pop those as needed. If you need to replace the panel.
If you really want to do it that way, you can modify your BaseLayout component so that it uses createComponent() to create whichever partial view you want.
enyo.kind({
name : "BaseLayout",
// This can be just a plain enyo.Control
components: [
{content: "this content of the View never changes"},
// next content should be changed. It's for renderTarget
{name: "renderContentSection"} // How to render new content form Index or Edit view here?
],
replacePartial: function(partial) {
this.$.renderContentSection.destroyClientControls();
this.$.renderContentSection.createComponent({kind: partial});
this.$.renderContentSection.render();
}
});
Here's a fiddle showing that off: http://jsfiddle.net/RoySutton/LhjLv6hy/
You could create a new control with a 'chrome' controls and a client area that controls get rendered into, but this is pretty simple. Still, take a look at Panels. It's what they were designed for.
I would like to know if it possible to extend in some way the mechanism Marionette Layouts are based on creating a sort of stack like navigation.
Marionette behaviour.
Before a region show()'s a view it calls close() on the currently displayed view. close() acts as the view's destructor, unbinding all events, rendering it useless and allowing the garbage collector to dispose of it.
My scenario.
Suppose I have a sort of navigation mechanism where a Layout acts as controller and first displays an ItemView called A, then a click somewhere allows to switch to ItemView B. At this point, an action on B (like for example a tap on a back button) allows to return to A without recreating it.
How is it possible to achieve the previous scenario without creating again A and maintaning its state?
For iOS people, I would like to mimic a sort of UINavigationController.
Any advice?
EDIT
My goal is to restore a prev cached view with its state without creating it again.
My scenario is the following. I have a layout with two regions: A e B.
I do a click somehere within A and A and B are closed to show C and D. Now a back click would restore A and B with their states. Events, models, etc...but since views are closed events are removed.
Use a backbone router to listen to URL change events. Setup routes for each of your views and then have the router call the layout to change the view it's displaying in response to each route. The user could click back or forward any number of times and the app responds accordingly and displays the correct view. Your router might look like:
var Router = Backbone.router.extend({
routes: {
'my/route/itemViewA': 'showItemViewA',
'my/route/itemViewB': 'showItemViewB'
},
showItemViewA: function () {
layout.showItemView('a');
},
showItemViewB: function () {
layout.showItemView('b');
}
});
Your layout might look something like this:
var Layout = Backbone.Marionette.Layout.extend({
regions: {
someRegion: 'my-region-jquery-selector'
},
initialize: function () {
this.createViews();
},
createViews: function () {
this.views = {
a: new Backbone.Marionette.ItemView,
b: new Backbone.Marionette.ItemView
};
},
showItemView: function (view) {
this.someRegion.show(this.views[view]);
// You might want to do some other stuff here
// such as call delegateEvents to keep listening
// to models or collections etc. The current view
// will be closed but it won't be garbage collected
// as it's attached to this layout.
}
});
The method of communication between the router and the layout doesn't have to be a direct call. You could trigger further application-wide events or do anything else you can think of. The router above is very basic but gets the job done. You could create a more intelligent router to use a single route with parameters to determine dynamically which itemView to show.
Every time the user does something that requires changing views, you can update the browser's history by using router.navigate('my/route/itemViewB', {trigger: true});. Also, if you set up your app to only render on history change events then you don't need to set up two mechanisms for rending each view.
I use this pattern in my own apps and it works very well.
#Simon's answer is headed in the correct direction. However, the only way to stop Marionette from closing views is to modify a bit of it's Region code.
var NoCloseRegion = Marionette.Region.extend({
open: function(view) {
// Preserve the currentView's events/elements
if (this.currentView) { this.currentView.$el.detach(); }
// Append the new view's el
this.$el.append(view.el);
}
});
The, when be sure to specify our new Region class when creating the Layout view
var Layout = Backbone.Marionette.Layout.extend({
regions: {
someRegion: {
selector: 'my-region-jquery-selector',
regionType: NoCloseRegion
},
},
initialize: function () {
this.createViews();
},
createViews: function () {
this.views = {
a: new Backbone.Marionette.ItemView,
b: new Backbone.Marionette.ItemView
};
},
showItemView: function (name) {
// Don't `show`, because that'll call `close` on the view
var view = this.views[name];
this.someRegion.open(view)
this.someRegion.attachView(view)
}
});
Now, instead of calling show which closes the old view, renders the new, and attaches it to the region (and triggers a few events), we can detach the old view, attach the new, and open it.
I want to render a view which I create when the button on another view is clicked.
Here is my controller code, and I am following the MVC architecture
Ext.define('demo.controller.LoginController' , {
extend: 'Ext.app.Controller',
config: {
refs:{
loginAction: 'button[action=login]'
},
control:{
loginAction: {
tap:'loginProcess'
}
}
},
loginProcess:function(button,e,opts){
// Render View here
}
});
I have searched and I came across getMainView().push() and Ext.ViewPoart.add() but it's not working. According to the MVC pattern how should call this view from a controller?
EDIT
code of profilecontainer
Ext.define('demo.view.ProfileContainer',{
extend:'Ext.Panel',
xtype:'profilecontainer',
requires: [
'Ext.Label'
],
config: {
items:[{
xtype:'label',
html:'hi'
}]
}
});
Both of the ways you have tried should work, if you set them up correctly.
First, getMainView().push(newView) will work if mainView is an Ext.navigation.View. See the docs for this method here: http://docs.sencha.com/touch/2.2.1/#!/api/Ext.navigation.View-method-push
You can also use Ext.Viewport.setActiveItem(newView), assuming you have no typos (your post says ViewPoart). (Ext.Viewport.add will add the panel to the Viewport, but not set it as the active Card in the layout)
If neither of these are working, then you probably are not configuring your controller correctly. If that is the case, ensure that your loginProcess method is being called. If it is not, then your selector, button[action=login], is not correct.
I know this question was already posted in StackOverflow but I either didnt understand or sencha changed somewhat.
My app loads a form panel for login, then I would like to save the user info that have just loged on. This way I can change my view anytime I want and still know the name of the loged in user.
Here is my main code:
//<debug>
Ext.Loader.setPath({
'Ext': 'sdk/src'
});
//</debug>
Ext.application({
name: 'APP',
loadedUser: 'the test',
requires: ['Ext.MessageBox'],
views: ['Main', 'Home', 'Login'],
models: ['User'],
stores: ['Users'],
controllers: ['Main'],
icon: {
57: 'resources/icons/Icon.png',
72: 'resources/icons/Icon~ipad.png',
114: 'resources/icons/Icon#2x.png',
144: 'resources/icons/Icon~ipad#2x.png'
},
phoneStartupScreen: 'resources/loading/Homescreen.jpg',
tabletStartupScreen: 'resources/loading/Homescreen~ipad.jpg',
setLoadedUser: function(arg) {
this.loadedUser = arg;
},
launch: function() {
// Destroy the #appLoadingIndicator element
Ext.fly('appLoadingIndicator').destroy();
// Initialize the main view
Ext.Viewport.add(Ext.create('APP.view.Main'));
},
onUpdated: function() {
Ext.Msg.confirm("Application Update", "This application has just successfully been updated to the latest version. Reload now?", function() {
window.location.reload();
});
}
});
The 'loadedUser' its what I wanted to be my global variable, and the method setLoadedUser(arg) its suposed to change that value.
I can access 'loadedUser' no problem, but I can't change its value.
Another question: loadedUser can it be an array/data structure?
How are you accessing the function? This works for me. Remember you should access it like this:
APP.app.setLoadedUser('test');
And yes, it can be any value. :)
You can also use localStorage to, set/get Your variables:
Set it as:
localStorage.setItem('currentUserId', userID)
Get it as:
localStorage.getItem('currentUserId')
You can use it, anywhere in Your script.
Yes, the works when the function is inside of the app.js file.
It does not work if the function is inside of the controller file.
So if you have a project application called IronMan, the call from the view code to the global function, flyAway(), in your app.js file would look like:
IronMan.app.flyAway();