The Films ViewController

In past chapters, we have already covered some examples of how to save data. We used the form submit, an Ajax request, and also the writing resource from the Store. In this chapter, let's focus on functionalities we have not implemented yet. Do not worry. The complete implementation is available within the source code distributed with this book.

The createDialog method

We created all the views for our application. In the base ViewController, we created the handlers for the Add and Edit buttons, and both call the createDialog method, which we are going to develop now.

The idea is to display a blank Edit window in case the user clicks on the Add button, and to display the selected film in case the user clicks on the Edit button. The source code for this method is as follows:

createDialog: function(record){

    var me = this,
        view = me.getView(),        //#1
        glyphs = Packt.util.Glyphs;

    me.isEdit = !!record;           //#2
    me.dialog = view.add({          //#3
        xtype: 'film-window',
        viewModel: {                //#4
            data: {                 //#5
                title: record ? 'Edit: ' + record.get('title') : 'Add Film',
                glyph: record ? glyphs.getGlyph('edit') : glyphs.getGlyph('add')
            },
            links: {                     //#6
                currentFilm: record || { //#7
                    type: 'Film',
                    create: true
                }
            }
        },
        session: true //#8
    });

    me.dialog.show(); //#9
}

The first thing we are going to do is get the reference of the View, which is the Films class (#1). Next, we are also going to create an isEdit flag (#2) and assign it to the ViewController so that we can access other methods later (such as the save method).

Then, we are going to instantiate the Edit window, adding it to the View (#3). When we add a child to the main View, it inherits the ViewModel and the ViewController as well. However, in this case, we are setting specific configurations to the Edit window ViewModel as well (#4), meaning it will have access to whatever configuration is already there plus the ones we are setting, such as title and glyph, which is predefined data (#5) to this ViewModel.

Next, we will create a link (#6) to a record called currentFilm ((#7), which we used in the bind configurations in the Edit window). If it is an edit, it will link to the selected row in the grid; otherwise, we create a new Film Model instance.

We are also going to create a child session (#8) for this View. We are going to discuss session when we discuss the save method.

Finally, we display the Edit window pop up (#9).

Getting the selected actor from Live Search

When a user searches for an actor and clicks on Add Selected, we will have to handle the event in the ViewController in the onSaveActors methods. The logic for this method is that we need to get the actor ID selected in the combobox and search for its value in the actors Store. Once we have the Actor Model instance, we can add it to the actorsGrid store. The code is as follows:

onSaveActors: function(button, e, options){
    var me = this,
        value = me.lookupReference('comboActors').getValue(), //#1
        store = me.getStore('actors'),                        //#2
        model = store.findRecord('actor_id', value),          //#3
        actorsGrid = me.lookupReference('actorsGrid'),        //#4
        actorsStore = actorsGrid.getStore();                  //#5

    if (model){
        actorsStore.add(model); //#6
    }

    me.onCancelActors(); //#7
}  

First, we get the reference of the combobox (#1) and get its value, which will return the ID of the selected actor. Next, we will get the reference of the actors Store declared in the FilmsModel (#2). We will search for the actor selected in the Store (#3); it will return the Actor Model reference or null if the actor does not exist. Then we get the actorsGrid reference ((#4)—the one inside the films form), and we also get its Store (#5).

If an actor is found, we add to the actorsGrid Store (#6). This grid is bound to the selected film actors association, so if an actor is added or deleted from the grid Store, it is also added or deleted from the association. This is another example of two-way data binding in Ext JS 5.

And at last, we close the Live Search pop up (#7) with the following method:

onCancelActors: function(button, e, options){
    var me = this;
    me.searchActors = Ext.destroy(me.searchActors);
}

Saving the form and working with sessions

Now it is time to save any data updated, deleted, or created. Any changes we make in the form will be saved to the session (Ext.data.Session). Sessions were introduced in Ext JS 5 and are great for when we are working with associations. We added a session to our films View by adding the configuration session: true in it. Then, all the stores from the ViewModel are also bound to the session, meaning any change made in the stores or in the session will be synchronized.

Let's take a look at the onSave method:

onSave: function(button, e, options){
    var me = this,
        dialog = me.dialog,
        form = me.lookupReference('filmForm'),
        isEdit = me.isEdit,
        session = me.getSession(), //#1
        id;

    if (form.isValid()) {
        if (!isEdit) {
            id = dialog.getViewModel().get('currentFilm').id; //#2
        }
        dialog.getSession().save(); //#3
        if (!isEdit) {
            me.getStore('films').add(session.getRecord('Film', id)); //#4
        }
        me.onCancel();
    }

    var batch = session.getSaveBatch(); //#5
    if (batch){
        batch.start();                  //#6
    }
}

We are going to get the pending information to be saved from the session and save it in the server. First, we need to get the session (#1). Then, we are going to get the film's id if it is a new film ((#2)—a random temporary ID is created for every Model in Ext JS, usually with the name of the entity and a sequential number). This id will be overwritten after we save it in the database and use the database table sequential ID.

Remember that when we created the dialog, we assigned a child session to the Edit window? This allows us to work with the data without committing to it, meaning we can roll back the changes easily by destroying the Edit window. When we want to save the data from the child session into the Films session officially, we can call the getSession method from the Edit window and save it (#3). This will save the child session data in the Film session.

Next, if it is a new film, we also want to add the record to the films Store (so it can be displayed in the FilmsGrid as well—(#4)).

There are two different ways to save information from a session. The first way is using batch (Ext.data.Batch) that can be retrieved from the session (#5), and the second way is executing its method start (#6). This will trigger the CRUD operations and will use the proxy details to connect to the server of the pending models of the session to be saved.

Custom Writer – saving associated data

However, the session does not save associated data. If we create a Film, Category, and Actor, the create operation from each respective Model will be triggered, but the data to be saved in the many-to-many matrix tables will not be sent to the server.

We can create a custom writer class that will send any associated data to the server as well in a single batch. Then, we need to handle the proper CRUD operations of the associated data on the server as well. The code is as follows:

Ext.define('Packt.ux.data.writer.AssociatedWriter', {
    extend: 'Ext.data.writer.Json',
    alias: 'writer.associatedjson',

    constructor: function(config) {
        this.callParent(arguments);
    },

    getRecordData: function (record, operation) {
        record.data = this.callParent(arguments);
        Ext.apply(record.data, record.getAssociatedData());
        return record.data;
    }
});

Then, we need to go back to the Packt.model.Base class, add this writer class to the requires declaration, and change the writer type as follows:

writer: {
    type: 'associatedjson',
    //...
},

All associated data will be sent to the server; in the same way, we are receiving a nested JSON from the server when we read information from it.

Saving session data manually

A second option to save the session data to the server is doing it manually. If we use the getChanges method of the session, it will return an object with all the pending information to be saved in the server, including associated data.

For example, if we try to edit a Film, add some Actor information and Category information, and call JSON.stringify(session. getChanges(), null, 4), we will have an output similar to the following:

{
    "Film": {
        "U": [
            {
                "title": "ACADEMY DINOSAUR - edit",
                "language_id": "2",
                "original_language_id": "3",
                "film_id": "1",
                "id": null
            }
        ],
        "categories": {
            "D": {
                "1": [
                    "6"
                ]
            },
            "C": {
                "1": [
                    "7",
                    "8",
                    "9"
                ]
            }
        },
        "actors": {
            "D": {
                "1": [
                    "1"
                ]
            },
            "C": {
                "1": [
                    "71"
                ]
            }
        }
    }
}

This means we are updating (U) some fields of the Film with ID of 1, deleting (D) the category with ID of 6 from the Film 1 and adding (C) the categories 7, 8, and 9 to the Film 1 as well. We are also deleting (D) the Actor 1 from Film 1 and adding (C) the Actor 71 to the Film 1 as well. Note that the categories and actors are the names of the many-to-many associations we created for Film Model.

We can also use this object to save the data manually in the server.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset