cancel
Showing results for 
Search instead for 
Did you mean: 

SAPUI5: How do you update a Label in a View from a json model?

lokesh_jadhav
Explorer
0 Kudos

Hi I need help with updating my Label in a view with new values from a json model everytime a button is clicked. How do you do this in SAPUI5?

You can see there is a question text and answer text coming from questionnaire model I would like to update from data[0].question.text to data[1].question.text which is displayed in my Toolbar text. How to achieve this in SAPUI5?

I have used toolbar header for question text and different labels for options? Is this right way? Will I able to update the toolbar header text?

My Code is as below

App.view.xml

<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:l="sap.ui.layout" xmlns:f="sap.ui.layout.form" xmlns:core="sap.ui.core"
displayBlock="true" controllerName="opensap.onlinequestionnaire.controller.App" height="100%">
<Page title="Online Questionnaire" class="sapUiContentPadding" id="myPage">
    <headerContent>
        <Button icon="sap-icon://action" tooltip="Share"/>
    </headerContent>
    <subHeader/>
    <content>
        <VBox class="sapUiSmallMargin">
            <f:SimpleForm id="SimpleFormToolbar" layout="ResponsiveGridLayout">
                <f:toolbar>
                    <Toolbar id="TB1">
                        <Title text="Question 1" level="H4" titleStyle="H4"/>
                        <ToolbarSpacer/>
                        <Button icon="sap-icon://drop-down-list"/>
                    </Toolbar>
                </f:toolbar>
                <f:content>
                    <Toolbar design="Solid">
                        <Title text="{questionnaire>/data/0/question/text}" level="H5" titleStyle="H5" textAlign="Center" id="questionText"/>
                    </Toolbar>
                    <VBox id="multipleChoiceHolder">
                        <HBox id="answerRow1">
                            <HBox width="700px" backgroundDesign="Solid" alignItems="Center" id="mCHorHolder1"><CheckBox id="checkBox1"/><Label text="Dogs" id="multipleChoice1"/></HBox>
                            <HBox width="700px" backgroundDesign="Solid" alignItems="Center" id="mCHorHolder2"><CheckBox id="checkBox2"/><Label text="Cats" id="multipleChoice2"/></HBox>
                        </HBox>
                        <HBox id="answerRow2">
                            <HBox width="700px" backgroundDesign="Solid" alignItems="Center" id="mCHorHolder3"><CheckBox id="checkBox3"/><Label text="Rabbits" id="multipleChoice3"/></HBox>
                            <HBox width="700px" backgroundDesign="Solid" alignItems="Center" id="mCHorHolder4"><CheckBox id="checkBox4"/><Label text="Hamsters" id="multipleChoice4"/></HBox>
                        </HBox>
                    </VBox> </f:content>
            </f:SimpleForm>
        </VBox>
    </content>
    <footer>
        <Toolbar id="toolBar">
            <Button text="Previous" type="Emphasized" width="300px" press="goToPrevious" icon="sap-icon://navigation-left-arrow" id="previousButton"/>
            <ToolbarSpacer/>
            <Button text="Next" type="Emphasized" width="300px" press="goToNext" icon="sap-icon://navigation-right-arrow" iconFirst="false"
                id="nextButton"/>
        </Toolbar>
    </footer>
</Page>

questionnaire.json

{ data: [{
    "question": "Which pet do you like from the following?",
    "answers": ["Cats", "Rabbits", "Dogs", "Hamsters"],
    "correct": 1
}, {
    "question": "Which pet do you prefer from the following?",
    "answers": ["Cats", "Dogs"],
    "correct": 1
}, {
    "question": "What food brand does your pet eat?",
    "answers": ["Pedigree", "Catfood", "Rabbitfood", "HamstersFood"],

    "correct": 1
}]
}  


App.controller.js

sap.ui.define([
"sap/ui/core/mvc/Controller"
], function(Controller){


Controller.extend("opensap.onlinequestionnaire.controller.App", {
    goToPrevious:function(){
        alert("Previous Question");
    },
    goToNext:function(){
        alert("Next Question");
    }
});

});

My Front end looks like this:


Accepted Solutions (1)

Accepted Solutions (1)

I have a few thoughts on this.

  • Give each question a serial number / key, incremental numbers like 1,2,3 for example.
  • Store important information globally like current question, previous question, current questions's answers etc.
  • Split the question string and answers array.
  • Create a 2nd JSON model to store current question from questionnaire's model, let's use the name tempModel for this example.
  • Create check boxex through JS based on answers, number of answers etc, through java script.

Here's how the code would look. I'm assuming a few things from your post and the structure of your data. The goToPrevious function would work similarly.

Note - this is just 1 way to do it, off the top of my head, there may be a more elegant/efficient solution, but first time around this is how I'd do it.

Note 2- this solution is assuming you already have the 1st question up and are navigating to the next question. You'd want to run slightly different version of this on initialize or when you want to load the 1st question.

View:

<p><Title text="{tempModel >/question}" level="H5" titleStyle="H5" textAlign="Center" id="questionText"/><br>	</p><p><f:SimpleForm id="simpleFormID" editable="true" title="" layout="ResponsiveGridLayout" 
		minWidth="1024" labelSpanL="3" labelSpanM="3" labelSpanS="2"
		emptySpanL="2" emptySpanM="2" emptySpanS="1">
		<f:content>
	
		</f:content>
	</f:SimpleForm>
</p>

Controller:

currentQ: null,  // Store the current question's object
previousQ: null, // Store the previous question's object
currentAnswers: null, // Store all possible answers for current Q
correctAnswer: null, // Store the correct answer for current Q

goToNext: function() {
        var curQ = this.currentQ;
        var q = questionnaireModel.getData() // the JSON object with question, answers, correct and key properties
        
        if (curQ = q.length-1) {    // check if last question assuming key starts from 1
            MessageBox.show("End of questions")
            return;
        }
        var checkArr = [] // Store check boxs data
        q.forEach(function(i) { 
	    if (i.key === curQ + 1) {
                var obj = {}
                obj.["question"] = i.question
                obj.["key"] = i.key
                checkArr = i.answers
                this.currentAnswers = i.answers // store all current answers options globally.
                this.correctAnswer = i.correct // store position of current correct answer.
                this.currentQ = i.key // update current question key with next question's key
                this.previousQ = curQ // udpate previous question key with current question's key
                
                sap.ui.getCore().getModel("tempModel").setData(obj) // set question to tempModel bound to label
                sap.ui.getCore().getModel("tempModel").refresh() // refresh model to update new question in view
            }
        });
      
      	var oSimpleForm = this.getView().byId("simpleFormID")  // Simple form to store controls. I like SFs because of its automatic layout features
        
      	checkArr.forEach(function(chk, index){
          
          var i = index;
          if ((++i).toString() === this.correctAnswer) { // increment index to match answer's position in array which starts at 1 instead of 0
          	this.correctAnswer = chk // overwrite current correct answer's position with actual correct answer.
          }
          
          var oLabel = new sap.m.Label({
            	text: chk
          });
          
          var oCheckBox = new sap.m.CheckBox(chk{  // create check box with value as id
           		editable: false
                select: jQuery.proxy(this.onCheckBoxSelect, this)                             
           });
           oSimpleForm.destroyContent(); // Destroy any previous content before adding new controls
      	   oSimpleForm.addContent(oLabel);
           oSimpleForm.addContent(oCheckBox);
        });
	},

    onCheckBoxSelect: function(oEvent) {
      var answer = oEvent.getSource().sId // ID of selected checkbox which will also be the correct answer.
    	
      if ( answer === this.correctAnswer ) {
        	*Do something*
      }
    
    },
lokesh_jadhav
Explorer

@Abdul Moh'd

Thank you for your answer it has been helpful for me while developing my app.
I commented again because I wanted to tell you that because of you I learnt about the usage of MessageBox control. Thank you very much.

0 Kudos

You're welcome.

Answers (2)

Answers (2)

lokesh_jadhav
Explorer
0 Kudos

@Abdul Moh'd

Hi I like the idea what you are stating but the code which you showed is a bit hard for me to understand as my coding skills in javascript and SAPUI5 are of beginner level. I will show you what I did.

As you suggested I created a separate json Model named currentQuestion.json which holds the current question with properties index, questions.
Now in my setNext() I am able to set the question present on 0th index that is "Which pet do you like from the following?" question to my currentQuestion.question property.
So now my currentQuestion has index = 0, question = "Which pet do you like from the following?".
But now how do I increment this index to next value on the click of goToNext() and update the question to next question and display it in the <Title text="{questionnaire>/data/0/question}" level="H5" titleStyle="H5" textAlign="Center" id="questionText"/> .
This I am unable to achieve.


App.view.xml:

<f:SimpleForm id="SimpleFormToolbar" layout="ResponsiveGridLayout">
   <f:toolbar>
	<Toolbar id="questionNumberHolder">
		<Title text="Question 1" level="H4" titleStyle="H4"/>
	<ToolbarSpacer/>
		<Button icon="sap-icon://drop-down-list"/>
        </Toolbar>
   </f:toolbar>
   <f:content>
	<Toolbar design="Solid" id="questionTextHolder">
		<Title text="{questionnaire>/data/0/question}" level="H5" titleStyle="H5" textAlign="Center" id="questionText"/> 
	</Toolbar>
		<VBox id="answersHolder">
			<items id="answerChoices">
				<VBox id="multipleChoices">
				    <items>
					<HBox backgroundDesign="Solid" alignItems="Center">
				           <items>
						<CheckBox id="mCheckBox0"></CheckBox><Label text="{questionnaire>/data/0/answers/0}" id="multipleChoice0" width="500px"></Label>
						<CheckBox id="mCheckBox1"></CheckBox><Label text="{questionnaire>/data/0/answers/1}" id="multipleChoice1" width="500px"></Label>
				           </items>
					</HBox>
					<HBox backgroundDesign="Solid" alignItems="Center">
					    <items>
						<CheckBox id="mCheckBox2"></CheckBox><Label text="{questionnaire>/data/0/answers/2}" id="multipleChoice2" width="500px"></Label>
						<CheckBox id="mCheckBox3"></CheckBox><Label text="{questionnaire>/data/0/answers/3}" id="multipleChoice3" width="500px"></Label>
					    </items>
					</HBox>
				   </items>
			         </VBox>
.... </Page>
</mvc:View>

questionnaire.json

{
	"data": [{
		"question": "Which pet do you like from the following?",
		"answers": ["Cats", "Rabbits", "Dogs", "Hamsters"],
		"correct": 1
	}, {
		"question": "Which pet do you prefer from the following?",
		"answers": ["Cats", "Dogs"],
		"correct": 1
	}, {
		"question": "What food brand does your pet eat?",
		"answers": ["Pedigree", "Catfood", "Rabbitfood", "HamstersFood"],
		"correct": 1
	}]
}

App.controller.js

sap.ui.define([
	"sap/ui/core/mvc/Controller",
	"sap/ui/model/json/JSONModel"
], function(Controller, JSONModel) {


	var oCurrentQuestionModel;
	var oQuestionModel;
	var iCurrentIndex;


	Controller.extend("opensap.onlinequestionnaire.controller.App", {
		onInit: function() {
			this.questionSetUp();
			this.getView().setModel(new JSONModel({
				index: false,
				question: {}
			}), "currentQuestion");
		},
		goToPrevious: function() {
			//alert("Previous Question");
			this.byId("multipleChoices").setVisible(true);
			this.byId("singleChoices").setVisible(false);
		},
		goToNext: function() {
			//alert("Next Question");
			//alert(this.byId("mC"));
			this.setNext();
			this.byId("multipleChoices").setVisible(false);
			this.byId("singleChoices").setVisible(true);
		},
		questionSetUp: function() {
			this.byId("multipleChoices").setVisible(true);
			this.byId("singleChoices").setVisible(false);
			this.byId("textInput").setVisible(false);
		},
		setNext: function() {
			oCurrentQuestionModel = this.getView().getModel("currentQuestion");
			oQuestionModel = this.getOwnerComponent().getModel("questionnaire");
			iCurrentIndex = oCurrentQuestionModel.getProperty("/index");
			//console.log(iCurrentIndex);
			//console.log(oQuestionModel.getProperty("/data/0/question"));
			if (iCurrentIndex) {
				++iCurrentIndex;
			} else {
				iCurrentIndex = 0;
			}
			//iCurrentIndex = iCurrentIndex ? ++iCurrentIndex : 0;
			this.setCurrentQuestionModel();
			this.byId("questionText").setText(oCurrentQuestionModel.getProperty("/question"));


			console.log(oCurrentQuestionModel.getProperty("/question"));
			console.log(oCurrentQuestionModel.getProperty("/"));
		},
		setCurrentQuestionModel: function() {
			var oCurrent = {
				index: iCurrentIndex,
				//index: i,
				question: oQuestionModel.getProperty("/data/" + iCurrentIndex + "/question")
					//question: oQuestionModel.getProperty("/data/" + i + "/question")
			};
			oCurrentQuestionModel.setProperty("/", oCurrent);
			this.byId("questionText").setText(oCurrentQuestionModel.getProperty("/question"));
		}
	});


});

 

Do not bind the questionnaire model (array of objects) directly the title, extract one single object from the array (current question), set this object to the temporary/empty model you will create separately and bind this tempModel model to your Title control, like so:

<Title text="{tempModel >/question} />

When you select the next question, the next object in the questionnaire array should overwrite the current question in the temporary model, which is bound to Title, this way due to 2 way binding, the Tile will always show the contents of tempModel.

If you bind this - questionnaire>/data/0/question, you are binding the first object of the array in the questionnaire model, and it will only show the first object of the array.

lokesh_jadhav
Explorer
0 Kudos

@Abdul Moh'd
Okay so I will bind <Title text = "{currentQuestion>/question}" /> to my temporary model which holds the current question but I am unable to figure out how to update the question property of currentQuestion to next question everytime user clicks on next Button.

At the moment my currentQuestion looks like this:

currentQuestion.json

{
index: 0
question: "Which pet do you like from the following?"
}

Now I want it to be set to next question and then update my title to display it. But I am unable to figure out how to do this.

I created the currentQuestion model as follows in the init() of controller.

this.getView().setModel(new JSONModel({
				index: false,
				question: {}
			}), "currentQuestion");

Maintain an index in questionnaire.json, either have back end devs send you an index or attach your own (loop at array and create a new property called index in each object and increment it's value by 1)

Use this index to compare current question's and find the next question in queue. Extract the next question's object from the array, set this object to the currentQuestion model.

sap.ui.getCore().getModel("currentQuestion").setProperty("/question",arr[i].question)<br><br>

where arr is questionnaire.json and i is next question's object. I was doing the same in my original solution.

lokesh_jadhav
Explorer
0 Kudos

Abdul Moh'd

Thank you Abdul. You have been a great help. I will try the way you suggested.

Sharathmg
Active Contributor
0 Kudos

Use the CheckBox with text option to you can easily pull up the selected option by the user and get the relevant text.

You can update any data bound to the controls. So, first build the local model and bind to the screens.

Then, in the nextQuestion method, modify the model and that will change the display.

After clicking every question, maintain the user entries in a separate model which can be used to update the backend.

Regards,

Sharath