0 Votes

Changes for page Todolist Macro

Last modified by Ryan C on 2025/04/30 07:54

From version 8.1
edited by Ryan C
on 2025/04/24 10:23
Change comment: There is no comment for this version
To version 26.1
edited by Ryan C
on 2025/04/24 11:19
Change comment: There is no comment for this version

Summary

Details

Page properties
Content
... ... @@ -1,7 +1,9 @@
1 1  Macro to display todo-lists using emberjs. This code is based on the emberjs demo. You can see an example below
2 2  
3 3  {{todolist width="50%" center="1"}}
4 -a thing|1
5 -a second thing|0
4 +a 3nd thing|0
5 +adding this thing|0
6 +adding this thing|0
7 +refreshed they are still there not even having to save page|0
6 6  
7 7  {{/todolist}}
XWiki.JavaScriptExtension[0]
Code
... ... @@ -1,257 +1,302 @@
1 1  requirejs.config({
2 - //By default load any module IDs from js/lib
3 - baseUrl: '${xwiki.getDocument("TodoLists.TodoListMacro").getURL("download")}/',
4 - //except, if the module ID starts with "app",
5 - //load it from the js/app directory. paths
6 - //config is relative to the baseUrl, and
7 - //never includes a ".js" extension since
8 - //the paths config could be for a directory.
9 - paths: {
2 + baseUrl: '${xwiki.getDocument("TodoLists.TodoListMacro").getURL("download")}/',
3 + paths: {
4 + 'jquery': 'https://code.jquery.com/jquery-3.6.0.min' // Updated jQuery version
5 + },
6 + shim: {
7 + 'ember-data': {
8 + deps: ['ember', 'jquery']
10 10   },
11 - shim: {
12 - 'ember-data': {
13 - deps: ['ember', 'jquery']
14 - },
15 - 'ember': {
16 - deps: ['jquery']
17 - }
10 + 'ember': {
11 + deps: ['jquery']
18 18   }
13 + }
19 19  });
20 -require(['jquery', 'handlebars-v1.2.1', 'ember-data', 'ember' ], function ($) {
21 21  
22 -// create app
23 -window.Todos = Ember.Application.create({
24 - rootElement: '#todoappdiv'
25 -});
16 +require(['jquery', 'handlebars-v1.2.1', 'ember-data', 'ember'], function($) {
17 + // Create app
18 + window.Todos = Ember.Application.create({
19 + rootElement: '#todoappdiv'
20 + });
26 26  
27 -// define the todolist store that will write to the TodoListsService
28 -Todos.ApplicationAdapter = DS.Adapter.extend({
29 - createRecord: function(store, type, record) {
30 - console.log("createRecord");
31 - var query = { create: "1", content : JSON.stringify(record) };
32 - return new Ember.RSVP.Promise(function(resolve, reject) {
33 - jQuery.getJSON("${xwiki.getURL("TodoLists.TodoListsService")}?page=" + XWiki.currentSpace + "." + XWiki.currentPage + "&xpage=plain&outputSyntax=plain", query).then(function(data) {
34 - Ember.run(null, resolve, data);
35 - }, function(jqXHR) {
36 - jqXHR.then = null; // tame jQuery's ill mannered promises
37 - Ember.run(null, reject, jqXHR);
22 + // Define the todolist store that will write to the TodoListsService
23 + Todos.ApplicationAdapter = DS.Adapter.extend({
24 + namespace: 'api', // Moved inside the adapter definition
25 +
26 + createRecord: function(store, type, record) {
27 + console.log("createRecord");
28 + var data = this.serialize(record, { includeId: true });
29 + var query = { create: "1", content: JSON.stringify(data) };
30 +
31 + return new Ember.RSVP.Promise(function(resolve, reject) {
32 + var url = "${xwiki.getURL('TodoLists.TodoListsService')}?page=" +
33 + XWiki.currentSpace + "." + XWiki.currentPage +
34 + "&xpage=plain&outputSyntax=plain";
35 + console.log("Creating record, URL:", url);
36 +
37 + jQuery.getJSON(url, query).then(function(data) {
38 + console.log("Create success:", data);
39 + Ember.run(function() {
40 + resolve(data);
41 + });
42 + }, function(jqXHR) {
43 + console.error("Create error:", jqXHR);
44 + Ember.run(function() {
45 + reject(jqXHR);
46 + });
47 + });
38 38   });
39 - });
40 - },
41 - deleteRecord: function(store, type, record) {
42 - console.log("deleteRecord");
43 - return this.saveAll(store.recordArrayManager.recordArraysForRecord(record).list[0].content);
44 - },
45 - find: function(store, type, id) {
46 - console.log("find");
47 - return;
48 - },
49 - findAll: function(store, type, sinceToken) {
50 - console.log("findAll");
51 - var query = { since: sinceToken };
52 - return new Ember.RSVP.Promise(function(resolve, reject) {
53 - jQuery.getJSON("${xwiki.getURL("TodoLists.TodoListsService")}?page=" + XWiki.currentSpace + "." + XWiki.currentPage + "&xpage=plain&outputSyntax=plain", query).then(function(data) {
54 - Ember.run(null, resolve, data);
55 - }, function(jqXHR) {
56 - jqXHR.then = null; // tame jQuery's ill mannered promises
57 - Ember.run(null, reject, jqXHR);
49 + },
50 +
51 + deleteRecord: function(store, type, record) {
52 + console.log("deleteRecord");
53 + var recordArrays = store.recordArrayManager.recordArraysForRecord(record);
54 + if (recordArrays && recordArrays.list && recordArrays.list[0]) {
55 + return this.saveAll(recordArrays.list[0].content);
56 + }
57 + return Ember.RSVP.resolve();
58 + },
59 +
60 + find: function(store, type, id) {
61 + console.log("find");
62 + return Ember.RSVP.resolve();
63 + },
64 +
65 + findAll: function(store, type, sinceToken) {
66 + console.log("findAll");
67 + var query = { since: sinceToken };
68 +
69 + return new Ember.RSVP.Promise(function(resolve, reject) {
70 + var url = "${xwiki.getURL('TodoLists.TodoListsService')}?page=" +
71 + XWiki.currentSpace + "." + XWiki.currentPage +
72 + "&xpage=plain&outputSyntax=plain";
73 + console.log("Finding all records, URL:", url);
74 +
75 + jQuery.getJSON(url, query).then(function(data) {
76 + console.log("FindAll success:", data);
77 + Ember.run(function() {
78 + resolve(data);
79 + });
80 + }, function(jqXHR) {
81 + console.error("FindAll error:", jqXHR);
82 + Ember.run(function() {
83 + reject(jqXHR);
84 + });
85 + });
58 58   });
59 - });
60 - },
61 - updateRecord: function(store, type, record) {
62 - return this.saveAll(store.recordArrayManager.recordArraysForRecord(record).list[0].content);
63 - },
64 - saveAll: function(alldata) {
65 - console.log("updateRecord");
66 - var query = { save: "1", content : JSON.stringify(alldata) };
67 - return new Ember.RSVP.Promise(function(resolve, reject) {
68 - jQuery.getJSON("${xwiki.getURL("TodoLists.TodoListsService")}?page=" + XWiki.currentSpace + "." + XWiki.currentPage + "&xpage=plain&outputSyntax=plain", query).then(function(data) {
69 - Ember.run(null, resolve, data);
70 - }, function(jqXHR) {
71 - jqXHR.then = null; // tame jQuery's ill mannered promises
72 - Ember.run(null, reject, jqXHR);
87 + },
88 +
89 + updateRecord: function(store, type, record) {
90 + console.log("updateRecord");
91 + var recordArrays = store.recordArrayManager.recordArraysForRecord(record);
92 + if (recordArrays && recordArrays.list && recordArrays.list[0]) {
93 + return this.saveAll(recordArrays.list[0].content);
94 + }
95 + return Ember.RSVP.resolve();
96 + },
97 +
98 + saveAll: function(alldata) {
99 + console.log("saveAll");
100 + var query = { save: "1", content: JSON.stringify(alldata) };
101 +
102 + return new Ember.RSVP.Promise(function(resolve, reject) {
103 + var currentPage = XWiki.currentSpace + "." + XWiki.currentPage;
104 + var url = "${xwiki.getURL('TodoLists.TodoListsService')}?page=" +
105 + currentPage + "&xpage=plain&outputSyntax=plain";
106 + console.log("Saving all records, URL:", url, "Page:", currentPage);
107 +
108 + jQuery.getJSON(url, query).then(function(data) {
109 + console.log("SaveAll success:", data);
110 + Ember.run(function() {
111 + resolve(data);
112 + });
113 + }, function(jqXHR) {
114 + console.error("SaveAll error:", jqXHR);
115 + Ember.run(function() {
116 + reject(jqXHR);
117 + });
118 + });
73 73   });
120 + }
121 + });
122 +
123 + // Routing
124 + Todos.Router.map(function() {
125 + this.resource('todos', { path: '' }, function() {
126 + this.route('active');
127 + this.route('completed');
74 74   });
75 - }
76 -});
129 + });
77 77  
78 -// use my own store
79 -Todos.store = DS.Store.create({
80 - adapter: 'ApplicationAdapter'
81 -});
131 + Todos.TodosRoute = Ember.Route.extend({
132 + model: function() {
133 + return this.store.find('todo');
134 + }
135 + });
82 82  
83 -// Todos.ApplicationAdapter = DS.FixtureAdapter.extend();
137 + Todos.TodosIndexRoute = Ember.Route.extend({
138 + model: function() {
139 + return this.modelFor('todos');
140 + }
141 + });
84 84  
85 -// routing
86 -Todos.Router.map(function() {
87 - this.resource('todos', { path: '' }, function() {
88 - this.route('active');
89 - this.route('completed');
143 + Todos.TodosActiveRoute = Ember.Route.extend({
144 + model: function() {
145 + return this.store.filter('todo', function(todo) {
146 + return !todo.get('isCompleted');
147 + });
148 + },
149 + renderTemplate: function(controller) {
150 + this.render('todos/index', { controller: controller });
151 + }
90 90   });
91 -});
92 92  
93 -Todos.TodosRoute = Ember.Route.extend({
94 - model: function() {
95 - return this.store.find('todo');
96 - }
97 -});
154 + Todos.TodosCompletedRoute = Ember.Route.extend({
155 + model: function() {
156 + return this.store.filter('todo', function(todo) {
157 + return todo.get('isCompleted');
158 + });
159 + },
160 + renderTemplate: function(controller) {
161 + this.render('todos/index', { controller: controller });
162 + }
163 + });
98 98  
99 -Todos.TodosIndexRoute = Ember.Route.extend({
100 - model: function() {
101 - return this.modelFor('todos');
102 - }
103 -});
165 + // Data model
166 + Todos.Todo = DS.Model.extend({
167 + title: DS.attr('string'),
168 + priority: DS.attr('string'),
169 + assignee: DS.attr('string'),
170 + isCompleted: DS.attr('boolean')
171 + });
104 104  
105 -// ... additional lines truncated for brevity ...
106 -Todos.TodosActiveRoute = Ember.Route.extend({
107 - model: function(){
108 - return this.store.filter('todo', function(todo) {
109 - return !todo.get('isCompleted');
110 - });
111 - },
112 - renderTemplate: function(controller) {
113 - this.render('todos/index', {controller: controller});
114 - }
115 -});
173 + // Controller
174 + Todos.TodoController = Ember.ObjectController.extend({
175 + actions: {
176 + editTodo: function() {
177 + this.set('isEditing', true);
178 + },
179 + acceptChanges: function() {
180 + this.set('isEditing', false);
116 116  
117 -Todos.TodosCompletedRoute = Ember.Route.extend({
118 - model: function(){
119 - return this.store.filter('todo', function (todo) {
120 - return todo.get('isCompleted');
121 - });
122 - },
123 - renderTemplate: function(controller){
124 - this.render('todos/index', {controller: controller});
125 - }
126 -});
127 -
128 -// data model
129 -Todos.Todo = DS.Model.extend({
130 - title: DS.attr('string'),
131 - priority: DS.attr('string'),
132 - assignee: DS.attr('string'),
133 - isCompleted: DS.attr('boolean')
134 -});
135 -
136 -// ... additional lines truncated for brevity ...
137 -Todos.Todo.FIXTURES = [
138 - {
139 - id: 1,
140 - title: 'Learn Ember.js X',
141 - isCompleted: true
142 - },
143 - {
144 - id: 2,
145 - title: '...',
146 - isCompleted: false
147 - },
148 - {
149 - id: 3,
150 - title: 'Profit!',
151 - isCompleted: false
152 - }
153 -];
154 -
155 -// controler
156 -Todos.TodoController = Ember.ObjectController.extend({
157 - actions: {
158 - editTodo: function () {
159 - this.set('isEditing', true);
182 + if (Ember.isEmpty(this.get('model.title'))) {
183 + this.send('removeTodo');
184 + } else {
185 + this.get('model').save();
186 + }
187 + },
188 + removeTodo: function() {
189 + var todo = this.get('model');
190 + todo.deleteRecord();
191 + todo.save();
192 + }
160 160   },
161 - acceptChanges: function() {
162 - this.set('isEditing', false);
163 163  
164 - if (Ember.isEmpty(this.get('model.title'))) {
165 - this.send('removeTodo');
166 - } else {
167 - this.get('model').save();
168 - }
169 - },
170 - removeTodo: function() {
171 - var todo = this.get('model');
172 - todo.deleteRecord();
173 - todo.save();
174 - }
175 - },
195 + isEditing: false,
176 176  
177 - isEditing: false,
197 + isCompleted: function(key, value) {
198 + var model = this.get('model');
178 178  
179 - isCompleted: function(key, value){
180 - var model = this.get('model');
200 + if (value === undefined) {
201 + // Property being used as a getter
202 + return model.get('isCompleted');
203 + } else {
204 + // Property being used as setter
205 + model.set('isCompleted', value);
206 + model.save();
207 + return value;
208 + }
209 + }.property('model.isCompleted')
210 + });
181 181  
182 - if (value === undefined) {
183 - // property being used as a getter
184 - return model.get('isCompleted');
185 - } else {
186 - // property being used as setter
187 - model.set('isCompleted', value);
188 - model.save();
189 - return value;
190 - }
191 - }.property('model.isCompleted')
192 -});
212 + Todos.TodosController = Ember.ArrayController.extend({
213 + actions: {
214 + createTodo: function() {
215 + // Get the todo title set by the "New Todo" text field
216 + var title = this.get('newTitle');
217 + if (!title || !title.trim()) { return; }
193 193  
194 -Todos.TodosController = Ember.ArrayController.extend({
195 - actions: {
196 - createTodo: function () {
197 - // Get the todo title set by the "New Todo" text field
198 - var title = this.get('newTitle');
199 - if (!title.trim()) { return; }
219 + console.log("Creating new todo with title:", title);
200 200  
201 - // Create the new Todo model
202 - var todo = this.store.createRecord('todo', {
203 - title: title,
204 - priority: "P3",
205 - assignee: "all",
206 - isCompleted: false
207 - });
221 + // Create the new Todo model
222 + var todo = this.store.createRecord('todo', {
223 + title: title,
224 + priority: "P3",
225 + assignee: "all",
226 + isCompleted: false
227 + });
208 208  
209 - // Clear the "New Todo" text field
210 - this.set('newTitle', '');
229 + // Clear the "New Todo" text field
230 + this.set('newTitle', '');
211 211  
212 - // Save the new model
213 - todo.save();
232 + // Save the new model
233 + todo.save().then(function(savedTodo) {
234 + console.log("Todo saved successfully:", savedTodo);
235 + // Force a refresh of the list after saving
236 + Ember.run.later(function() {
237 + console.log("Refreshing todo list");
238 + Todos.store.find('todo');
239 + }, 100);
240 + }, function(error) {
241 + console.error("Failed to save todo:", error);
242 + });
243 + },
244 +
245 + clearCompleted: function() {
246 + var completed = this.filter(function(todo) {
247 + return todo.get('isCompleted');
248 + });
249 +
250 + completed.forEach(function(todo) {
251 + todo.deleteRecord();
252 + todo.save();
253 + });
254 + }
214 214   },
215 - clearCompleted: function () {
216 - var completed = this.filterProperty('isCompleted', true);
217 - completed.invoke('deleteRecord');
218 - completed.invoke('save');
219 - }
220 - },
221 221  
222 - remaining: function () {
223 - return this.filterProperty('isCompleted', false).get('length');
224 - }.property('@each.isCompleted'),
257 + remaining: function() {
258 + return this.filter(function(todo) {
259 + return !todo.get('isCompleted');
260 + }).get('length');
261 + }.property('@each.isCompleted'),
225 225  
226 - inflection: function () {
227 - var remaining = this.get('remaining');
228 - return remaining === 1 ? 'item' : 'items';
229 - }.property('remaining'),
230 - hasCompleted: function () {
231 - return this.get('completed') > 0;
232 - }.property('completed'),
263 + inflection: function() {
264 + var remaining = this.get('remaining');
265 + return remaining === 1 ? 'item' : 'items';
266 + }.property('remaining'),
267 +
268 + hasCompleted: function() {
269 + return this.get('completed') > 0;
270 + }.property('completed'),
233 233  
234 - completed: function () {
235 - return this.filterProperty('isCompleted', true).get('length');
236 - }.property('@each.isCompleted'),
272 + completed: function() {
273 + return this.filter(function(todo) {
274 + return todo.get('isCompleted');
275 + }).get('length');
276 + }.property('@each.isCompleted'),
237 237  
238 - allAreDone: function(key, value) {
239 - if (value === undefined) {
240 - return !!this.get('length') && this.everyProperty('isCompleted', true);
241 - } else {
242 - this.setEach('isCompleted', value);
243 - this.invoke('save');
244 - return value;
245 - }
246 -}.property('@each.isCompleted')
247 -});
278 + allAreDone: function(key, value) {
279 + if (value === undefined) {
280 + return this.get('length') > 0 && this.every(function(todo) {
281 + return todo.get('isCompleted');
282 + });
283 + } else {
284 + this.forEach(function(todo) {
285 + todo.set('isCompleted', value);
286 + todo.save();
287 + });
288 + return value;
289 + }
290 + }.property('@each.isCompleted')
291 + });
248 248  
249 -// editing
250 -Todos.EditTodoView = Ember.TextField.extend({
251 - didInsertElement: function() {
252 - this.$().focus();
253 - }
254 -});
293 + // Editing
294 + Todos.EditTodoView = Ember.TextField.extend({
295 + didInsertElement: function() {
296 + this.$().focus();
297 + }
298 + });
255 255  
256 -Ember.Handlebars.helper('edit-todo', Todos.EditTodoView);
300 + Ember.Handlebars.helper('edit-todo', Todos.EditTodoView);
257 257  });
302 +
XWiki.WikiMacroClass[0]
Cached
... ... @@ -1,0 +1,1 @@
1 +No
Context elements
... ... @@ -1,0 +1,1 @@
1 +User
Asynchronous rendering
... ... @@ -1,0 +1,1 @@
1 +No