最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ExtJS 4 - Reuse components in MVC architecture - Stack Overflow

programmeradmin0浏览0评论

I have a list of users and if I click on an item in this list, a window opens. This is the same window for each user, and it's possible to have several window open at the same time. The window shows user informations so for this ponents I have the same store and the same model.

But if I load data in a specific window, I load the same data in all other open windows.

Ext.define('Cc.view.absence.Grid', {
  extend: 'Ext.grid.Panel',
  alias: 'widget.absencegrid',

  border:false,

  initComponent: function() {
    Ext.apply(this, {
      store: Ext.create('Ext.data.Store', {
        model: 'Cc.model.Absence',
        autoLoad: false,
        proxy: {
          type: 'ajax',
          reader: {
            type: 'json'
          }
        }
      }),
      columns: [
        {header: 'Du', dataIndex: 'startdate', flex: 3, renderer: this.formatDate},
        {header: 'Au', dataIndex: 'enddate', flex: 3, renderer: this.formatDate},
        {header: 'Exercice', dataIndex: 'year', align: 'center', flex: 1},
        {header: 'Statut', xtype:'templatecolumn', tpl:'<img src="../images/status-{statusid}.png" alt="{status}" title="{status}" />', align: 'center', flex: 1},
        {header: 'Type d\'absence', dataIndex: 'absencetype', align: 'center', flex: 2},
        {header: 'Commentaires', dataIndex: 'ment', flex: 6}
      ],
      dockedItems: [{
        xtype: 'toolbar',
        dock: 'top',
        items: [
          { xtype: 'tbfill'},
          { xtype: 'button', text: 'Rafraichir', action: 'refresh', iconCls: 'item-rafraichir' }
        ]
      }]
    });

    this.callParent(arguments);
  },

  formatDate: function(date) {
    if (!date) {
      return '';
    }
    return Ext.Date.format(date, 'l d F Y - H:i');
  }

});

my controller :

Ext.define('Cc.controller.Absences', {
  extend: 'Ext.app.Controller',

  models: ['Absence', 'AbsenceHistory'],

  views: [],

  refs: [
    { ref: 'navigation', selector: 'navigation' },
    { ref: 'tabPanel', selector: 'tabpanel' },
    { ref: 'absencePanel', selector: 'absencepanel' },
    { ref: 'refreshButton', selector: 'absencepanel button[action=refresh]'},
    { ref: 'absenceGrid', selector: 'absencegrid' },
    { ref: 'absenceHistory', selector: 'absencehistory' },
  ],

  init: function() {
    this.control({
      'absencepanel button[action=refresh]': {
        click: this.onClickRefreshButton
      },
      'absencegrid': {
        selectionchange: this.viewHistory
      },
      'absencegrid > tableview': {
        refresh: this.selectAbsence
      },
    });
  },

  selectAbsence: function(view) {
    var first = this.getAbsenceGrid().getStore().getAt(0);
    if (first) {
      view.getSelectionModel().select(first);
    }
  },

  viewHistory: function(grid, absences) {
    var absence = absences[0],
      store = this.getAbsenceHistory().getGrid().getStore();
    if(absence.get('id')){
      store.getProxy().url = '/absence/' + absence.get('id') +'/history';
      store.load();
    }
  },

  onClickRefreshButton: function(view, record, item, index, e){
    var store = this.getAbsenceGrid().getStore();
    store.load();
  },
});

other controller which create only one instance of absence.Panel :

Ext.define('Cc.controller.Tools', {
  extend: 'Ext.app.Controller',

  stores: ['Tools', 'User' ],

  models: ['Tool'],

  views: [],

  refs: [
    { ref: 'navigation', selector: 'navigation' },
    { ref: 'tabPanel', selector: 'tabpanel' },
    { ref: 'toolList', selector: 'toollist' },
    { ref: 'toolData', selector: 'toollist dataview' }
  ],

  init: function() {
    this.control({
      'toollist dataview': {
        itemclick: this.loadTab
      },
    });
  },

  onLaunch: function() {
    var dataview = this.getToolData(),
        store = this.getToolsStore();

    dataview.bindStore(store);
  },

  loadTab: function(view, record, item, index, e){
    var tabPanel = this.getTabPanel();
    switch (record.get('tab')) {
      case 'absences':
        if(Ext.getCmp('absence-panel')){
          tabPanel.setActiveTab(Ext.getCmp('absence-panel'));
        }
        else {
          var panel = Ext.create('Cc.view.absence.Panel',{
            id: 'absence-panel'
          }),
              store = panel.getGrid().getStore();
          panel.enable();
          tabPanel.add(panel);
          tabPanel.setActiveTab(panel);
          store.getProxy().url = '/person/' + this.getUserId() +'/absences';
          store.load();
        }
      break;

      default: 
      break;
    }
  },

  getUserId: function(){
    var userStore = this.getUserStore();
    var id = userStore.first().get('id')
    return id;
  }
});

the other controller which create many instances of absence.Panel :

Ext.define('Cc.controller.Agents', {
  extend: 'Ext.app.Controller',

  stores: ['Agents'],

  models: ['Agent', 'Absence', 'AbsenceHistory'],

  views: ['agent.List', 'agent.Window', 'absence.Panel', 'absence.Grid', 'absence.History'],
  refs: [
    { ref: 'agentList', selector: 'agentlist' },
    { ref: 'agentData', selector: 'agentlist dataview' },
    { ref: 'agentWindow', selector: 'agentwindow' },
  ],

  init: function() {
    this.control({
      'agentlist dataview':{
        itemclick: this.loadWindow
      },
    });
  },

  onLaunch: function() {
    var dataview = this.getAgentData(),
        store = this.getAgentsStore();
    dataview.bindStore(store);
  },

  loadWindow: function(view, record, item, index, e){
    if(!Ext.getCmp('agent-window'+record.get('id'))){
      var window = Ext.create('Cc.view.agent.Window', {
        title: 'À propos de '+record.get('firstname')+' '+record.get('lastname'),
        id: 'agent-window'+record.get('id')
      }),
          tabPanel = window.getTabPanel();
          absencePanel = window.getAbsencePanel();
          store = absencePanel.getGrid().getStore();

      absencePanel.enable();
      tabPanel.add(absencePanel);
      tabPanel.setActiveTab(absencePanel);
      store.getProxy().url = '/person/' + record.get('id') +'/absences';
      store.load();
    }
    Ext.getCmp('agent-window'+record.get('id')).show();
  }
});

and the absence.Panel view, container of absence.Grid :

Ext.define('Cc.view.absence.Panel', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.absencepanel',

  title: 'Mes absences',
  iconCls: 'item-outils',
  closable: true,
  border: false,
  disabled: true,
  layout: 'border',

  initComponent: function() {
    this.grid = Ext.create('Cc.view.absence.Grid', {
      region: 'center'
    });
    this.history = Ext.create('Cc.view.absence.History', {
      region: 'south',
      height: '25%'
    });
        Ext.apply(this, {
      items: [
        this.grid,
        this.history
      ]
    });

        this.callParent(arguments);
    },

  getGrid: function(){
    return this.grid;
  },

  getHistory: function(){
    return this.history;
  }
});

I have a list of users and if I click on an item in this list, a window opens. This is the same window for each user, and it's possible to have several window open at the same time. The window shows user informations so for this ponents I have the same store and the same model.

But if I load data in a specific window, I load the same data in all other open windows.

Ext.define('Cc.view.absence.Grid', {
  extend: 'Ext.grid.Panel',
  alias: 'widget.absencegrid',

  border:false,

  initComponent: function() {
    Ext.apply(this, {
      store: Ext.create('Ext.data.Store', {
        model: 'Cc.model.Absence',
        autoLoad: false,
        proxy: {
          type: 'ajax',
          reader: {
            type: 'json'
          }
        }
      }),
      columns: [
        {header: 'Du', dataIndex: 'startdate', flex: 3, renderer: this.formatDate},
        {header: 'Au', dataIndex: 'enddate', flex: 3, renderer: this.formatDate},
        {header: 'Exercice', dataIndex: 'year', align: 'center', flex: 1},
        {header: 'Statut', xtype:'templatecolumn', tpl:'<img src="../images/status-{statusid}.png" alt="{status}" title="{status}" />', align: 'center', flex: 1},
        {header: 'Type d\'absence', dataIndex: 'absencetype', align: 'center', flex: 2},
        {header: 'Commentaires', dataIndex: 'ment', flex: 6}
      ],
      dockedItems: [{
        xtype: 'toolbar',
        dock: 'top',
        items: [
          { xtype: 'tbfill'},
          { xtype: 'button', text: 'Rafraichir', action: 'refresh', iconCls: 'item-rafraichir' }
        ]
      }]
    });

    this.callParent(arguments);
  },

  formatDate: function(date) {
    if (!date) {
      return '';
    }
    return Ext.Date.format(date, 'l d F Y - H:i');
  }

});

my controller :

Ext.define('Cc.controller.Absences', {
  extend: 'Ext.app.Controller',

  models: ['Absence', 'AbsenceHistory'],

  views: [],

  refs: [
    { ref: 'navigation', selector: 'navigation' },
    { ref: 'tabPanel', selector: 'tabpanel' },
    { ref: 'absencePanel', selector: 'absencepanel' },
    { ref: 'refreshButton', selector: 'absencepanel button[action=refresh]'},
    { ref: 'absenceGrid', selector: 'absencegrid' },
    { ref: 'absenceHistory', selector: 'absencehistory' },
  ],

  init: function() {
    this.control({
      'absencepanel button[action=refresh]': {
        click: this.onClickRefreshButton
      },
      'absencegrid': {
        selectionchange: this.viewHistory
      },
      'absencegrid > tableview': {
        refresh: this.selectAbsence
      },
    });
  },

  selectAbsence: function(view) {
    var first = this.getAbsenceGrid().getStore().getAt(0);
    if (first) {
      view.getSelectionModel().select(first);
    }
  },

  viewHistory: function(grid, absences) {
    var absence = absences[0],
      store = this.getAbsenceHistory().getGrid().getStore();
    if(absence.get('id')){
      store.getProxy().url = '/absence/' + absence.get('id') +'/history';
      store.load();
    }
  },

  onClickRefreshButton: function(view, record, item, index, e){
    var store = this.getAbsenceGrid().getStore();
    store.load();
  },
});

other controller which create only one instance of absence.Panel :

Ext.define('Cc.controller.Tools', {
  extend: 'Ext.app.Controller',

  stores: ['Tools', 'User' ],

  models: ['Tool'],

  views: [],

  refs: [
    { ref: 'navigation', selector: 'navigation' },
    { ref: 'tabPanel', selector: 'tabpanel' },
    { ref: 'toolList', selector: 'toollist' },
    { ref: 'toolData', selector: 'toollist dataview' }
  ],

  init: function() {
    this.control({
      'toollist dataview': {
        itemclick: this.loadTab
      },
    });
  },

  onLaunch: function() {
    var dataview = this.getToolData(),
        store = this.getToolsStore();

    dataview.bindStore(store);
  },

  loadTab: function(view, record, item, index, e){
    var tabPanel = this.getTabPanel();
    switch (record.get('tab')) {
      case 'absences':
        if(Ext.getCmp('absence-panel')){
          tabPanel.setActiveTab(Ext.getCmp('absence-panel'));
        }
        else {
          var panel = Ext.create('Cc.view.absence.Panel',{
            id: 'absence-panel'
          }),
              store = panel.getGrid().getStore();
          panel.enable();
          tabPanel.add(panel);
          tabPanel.setActiveTab(panel);
          store.getProxy().url = '/person/' + this.getUserId() +'/absences';
          store.load();
        }
      break;

      default: 
      break;
    }
  },

  getUserId: function(){
    var userStore = this.getUserStore();
    var id = userStore.first().get('id')
    return id;
  }
});

the other controller which create many instances of absence.Panel :

Ext.define('Cc.controller.Agents', {
  extend: 'Ext.app.Controller',

  stores: ['Agents'],

  models: ['Agent', 'Absence', 'AbsenceHistory'],

  views: ['agent.List', 'agent.Window', 'absence.Panel', 'absence.Grid', 'absence.History'],
  refs: [
    { ref: 'agentList', selector: 'agentlist' },
    { ref: 'agentData', selector: 'agentlist dataview' },
    { ref: 'agentWindow', selector: 'agentwindow' },
  ],

  init: function() {
    this.control({
      'agentlist dataview':{
        itemclick: this.loadWindow
      },
    });
  },

  onLaunch: function() {
    var dataview = this.getAgentData(),
        store = this.getAgentsStore();
    dataview.bindStore(store);
  },

  loadWindow: function(view, record, item, index, e){
    if(!Ext.getCmp('agent-window'+record.get('id'))){
      var window = Ext.create('Cc.view.agent.Window', {
        title: 'À propos de '+record.get('firstname')+' '+record.get('lastname'),
        id: 'agent-window'+record.get('id')
      }),
          tabPanel = window.getTabPanel();
          absencePanel = window.getAbsencePanel();
          store = absencePanel.getGrid().getStore();

      absencePanel.enable();
      tabPanel.add(absencePanel);
      tabPanel.setActiveTab(absencePanel);
      store.getProxy().url = '/person/' + record.get('id') +'/absences';
      store.load();
    }
    Ext.getCmp('agent-window'+record.get('id')).show();
  }
});

and the absence.Panel view, container of absence.Grid :

Ext.define('Cc.view.absence.Panel', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.absencepanel',

  title: 'Mes absences',
  iconCls: 'item-outils',
  closable: true,
  border: false,
  disabled: true,
  layout: 'border',

  initComponent: function() {
    this.grid = Ext.create('Cc.view.absence.Grid', {
      region: 'center'
    });
    this.history = Ext.create('Cc.view.absence.History', {
      region: 'south',
      height: '25%'
    });
        Ext.apply(this, {
      items: [
        this.grid,
        this.history
      ]
    });

        this.callParent(arguments);
    },

  getGrid: function(){
    return this.grid;
  },

  getHistory: function(){
    return this.history;
  }
});
Share Improve this question edited May 25, 2011 at 14:57 Cyril F asked May 24, 2011 at 13:51 Cyril FCyril F 1,3393 gold badges18 silver badges31 bronze badges 2
  • Sounds like you need to create a unique instance of each window when the user clicks on the item? Right now I think you are just opening duplicates? – dmackerman Commented May 24, 2011 at 14:02
  • Same store is link to all window. So if I load data into a new window, all other open windows are reloaded too. But I want to reload data only If I click on the refresh button. – Cyril F Commented May 24, 2011 at 14:06
Add a ment  | 

1 Answer 1

Reset to default 5

Yes. Here is more details explanation of what I am doing. I hope you have read the forum pletely. There is still one information that we are not clear about. That is the exact use of using "stores" property in controller. IMHO Sencha team should explain the MVC in much more detail with plex examples.

Yes, what you have newly posted is correct. when you have create new view, create a new instance of the store! Now, from the forum discussions, people argue about MVC. I would definitly go with steffenk . What we are doing here is injecting a new instance of store to my view. And I am ignoring the stores property of the controller.

Here is an example:

This is my view. Its a panel (with user profile information) that I display on my tabpanel :

Ext.define('Dir.view.profile.View' ,{
    extend: 'Ext.panel.Panel',
    alias : 'widget.profileview',
    title : 'Profile',
    profileId: 1,   // default and dummy value
    initComponent: function() {

        // configure necessary stuff, I access my store etc here..       
        // console.log(this.profileStore);
        this.callParent(arguments);         
    },
    // Other view methods goes here
});

Now, look at my controller:

Ext.define('Dir.controller.Profile', {
    extend: 'Ext.app.Controller',
    //stores: ['Profile'],   --> Note that I am NOT using this!
    refs: [     
        {ref:'cp',selector: 'centerpane'}
    ],
    views: ['profile.View'],
    init: function() {
        // Do your init tasks if required
    },  
    displayProfile: function(selectedId) {

            // create a new store.. pass your config, proxy url etc..
        var store = Ext.create('Dir.store.Profile',{profileId: selectedId});

        console.log('Display Profile for ID ' + selectedId);    

        // Create instance of my view and pass the new store
        var view = Ext.widget('profileview',{profileId: selectedId,profileStore: store});

        // Add my new view to centeral panel and display it...
        this.getCp().add(view);
        this.getCp().setActiveTab(view);
    }
});

My displayProfile() is called from some event listeners (Menu, Tree etc) and it passes a id. My controller using this id to setup a new store and view. I hope the above code gives you a clear picture of what I said yesterday.

In your controller, you will have to add Ext.require('Dir.store.Profile'); so that ExtJS know you have such a store. This is because we are not making use of stores property.

Now, you can want to reuse these created stores elsewhere, you can add them to the StoreManager. With this, you can access your created stores at any place, add and remove stores. But this es with a overhead of you managing the instances of the store.


Why do you share the same instance of the store with different views? when you have a new view, create a new instance of the store. This will prevent updated data appearing on other windows when one is refreshed.

发布评论

评论列表(0)

  1. 暂无评论