Is there a way to check if some record exists before I create a new one?

Before I give any snippets of code, I want to explain the task that I have to implement.

First of all, every class has many subjects and every class has many students. Every subject and student belongs to one class. Also, every subject and student has many grades. But one grade belongs to one subject and one student.

I wanted first in controller to check if there is one grade with those parameters (student and subject), and then if there is that grade, just update marks property (type of ‘array’ which I have in app/transforms). So here is controller app/controllers/overviewofstudents.js:

import Ember from 'ember';

export default Ember.Controller.extend({
  showS: false,
  mark: "",

  actions:{
    showSubjects(){
      this.toggleProperty('showS');
    },

    insertMark(val, val2){
      var subject2 = this.get('store').peekRecord('subject', val2);
      var student = this.get('store').peekRecord('student', val);
      var mark2 = this.get('mark');

      this.get('store').query('grade', {
        filter: {
          student: student,
          subject: subject2
        }
      }).then(function(mark){
        if(mark == null){
          this.get('store').createRecord('grade', {
            marks: [mark2],
            student: student,
            subject: subject2
          }).save().then(function(grade){
            student.get('marks').pushObject(mark2);
            student.save();
            subject2.get('grades').pushObject(mark2);
            subject2.save();
          });
        }else{
          mark.get('marks').pushObject(mark2);
          mark.save();
        }
      });
    }
  }
});

And here is app/templates/overviewofstudents.hbs:

<br> <br> <br> <br> <br>
<center>

<table>

<h2> Chosen class: {{model.name}} </h2>

<br>

<i> Current students in class: </i> <br>

<ol> 
{{#each model.students as |student|}}
  <li> {{student.firstName}}  {{student.lastName}} </li>
  <button class="choose" {{action 'showSubjects'}}> Show subjects </button>
  {{#if showS}}
    <br> Current subjects in class: 
    {{#each model.subjects as |subject|}}
      <br> <b> {{subject.title}} </b>
       Choose grade: 
      <select onchange={{action (mut mark) value="target.value"}}>
        <option value="1"> 1 </option>
        <option value="2"> 2 </option>
        <option value="3"> 3 </option>
        <option value="4"> 4 </option>
        <option value="5"> 5 </option>
      </select>
      <br>
      <button {{action 'insertMark' student.id subject.id}}> Insert mark for student </button>
    {{/each}}    
  {{/if}}
{{else}}
  <b> Currently there are no students for the class! </b>
{{/each}}
</ol>

</table>

</center>

-And all I get is error: TypeError: Cannot read property ‘pushObject’ of undefined

-I will also leave models for class, student, subject and grade.

app/models/class.js:

import DS from 'ember-data';

export default DS.Model.extend({
	name: DS.attr('string'),
	subjects: DS.hasMany('subject'),
	students: DS.hasMany('student')
});

app/models/student.js:

import DS from 'ember-data';

export default DS.Model.extend({
	firstName: DS.attr('string'),
	lastName: DS.attr('string'),
	grades: DS.hasMany('grade'),
	class: DS.belongsTo('class')
});

app/models/subject.js:

import DS from 'ember-data';

export default DS.Model.extend({
	title: DS.attr('string'),
	grades: DS.hasMany('grade'),
	class: DS.belongsTo('class')
});

app/models/grade.js:

import DS from 'ember-data';

export default DS.Model.extend({
	ocene: DS.attr('array'),
	subject: DS.belongsTo('subject'),
	student: DS.belongsTo('student')
});

Why are you using pushObject in the first place?

There definitely is a way to check if the record exists in store, if you know the record’s id, using peekRecord. In the event, where you don’t know the id but only know record’s attributs, you can peekAll record of that typr in use any of arrayProxyMixin class’ methods to filter out the record. Once you have the record and you need to update it, use set method on the record and it will update automatically for you.

Suppose you want to update record for student John, you just need to get the record and update like this: record.set('class', 'x'). And that will be it.

Also you have ocene property in your Greade model. But you are using marks property in the snippet above. I’m not sure if that is a typo or bug.

1 Like

Thank you my friend! I tried to translate it all into English from Serbian, that is why it said “ocena” there instead of “mark” my mistake. Can you please give me a short example where you use peekAll and after that filter out the record by something. It doesn’t need to be mine example whatever, I just need one example.

So, one part of the code is working and that is if block, do you have any suggestions how to do else block how can I get id of the firstObject and then update it? Can I do .save() outside the store or?

  insertMark(val, val2){
		var student = this.get('store').peekRecord('student', val);
		var subject = this.get('store').peekRecord('subject', val2);
		var mark = this.get('mark');

		var grades = this.get('store').peekAll('grade');

		grades.filterBy('student', student).filterBy('subject', subject);

		if(grades.get('firstObject') == null){
			this.get('store').createRecord('grade', {
				marks: [mark],
				student: student,
				subject: subject 
			}).save().then(function(grade){
				student.get('grades').pushObject(grade);
				student.save();
				subject.get('grades').pushObject(grade);
				subject.save();
			})
		}else{
			
			//Any suggestions for else block? How to just update marks property of the grade 
                           //model?
		}

	}
  1. yep you can do save() anywhere, you jyst need the record instance.
    e.g.
        studentS1 = store.peekRecord('student', 'S1');
        grades = studentS1.get('grades');
        grades.push('G3');
        studentS1.set('grades', grades);
        studentS1.set('lastName', 'Doe');
        studentS1.save();

What happened?
We fetched record for S1
Then we took grades attribute from the record.
We updated grades and lastName for S1, using set.
Finally we asked store to update changes to our backend. Until save() is called, the changes are updated only on the store, and the record is in updated state. Read on DS.Model clss to know more on states of record.

I think you can use the ame approach in your else block above.

  1. Your code is not written in obvious way, there are complex operations with nested then functions, which is quite unnecessary. I’m quite certain it can be optimized.

  2. You model has relationships hasMany and belongsTo. Take advantage of these to fetch model more precisely then your current approach. I strongly urge you to spend some time reading DS.Model, DS.Store and Ember.ArrayProxy classes thoroughly to have better ubderstanding of Ember.

  3. For you peekAll snippet

students = store.peekAll('student');
students.find(function(item, index) {
   const id  = item.get('id');
   const class = item.get('class');
   if(id === 'S1' && class === 'C2') {
       console.log('Found student S1 from class C2');
       return true; 
   }
  return false;
}

The above code will return an array for which true is returned. Look up find method from ArrayProxy.

1 Like

Thank you very much for this, I really do appreciate it. I have edited my first question in a more complete question (before you responded me) in Not filterBy properly; and I have given complete code. If you can found an error, or correct me somewhere please do. Because I feel I am close to solution. Thank you in advance!

I tried your way, but I always get that object that I am returning is not null. My goal is that when grades.get(‘firstObject’) === null to create new object. Please tell me what is wrong. Here is the snippet of the code?

insertMark(val1, val2){

			var student = this.store.peekRecord("student", val1);
			var subject = this.store.peekRecord("subject", val2);
			var mark = this.get('mark');
			
			alert(student.get('firstName') +  " " +  student.get('lastName'));
			alert(subject.get('title'));
			
			var grades = this.store.peekAll('grade');

			grades.find(function(item, index){
				const student2 = item.get('student');
				const subject2 = item.get('subject');
				if(student2 === student && subject2 === subject){
					return true;
				}
				return false;
			});

			if(grades.get('firstObject') !== null){
				alert("Object is null even though there is no object with those parameters. ");
			}
}

See my response in the other thread

1 Like

Great thanks man I will check it out and tell you if it works! :slight_smile: Thanks a lot! :slight_smile: